## Effects Summary
The *new desugaring* focuses entirely on traits and how they are treated with the effects system.
Before we get to the new desugaring, let's first take a look at the old desugaring:
```rust
#[const_trait]
trait Foo {
type Assoc;
fn method();
}
impl const Foo for () {
type Assoc = ();
fn method() {}
}
```
Before the new proposed desugaring, this currently desugars into:
```rust
trait Foo<const RUNTIME: bool> {
type Assoc;
fn method();
}
impl<const RUNTIME: bool> Foo<RUNTIME> for () {
type Assoc = ();
fn method() {}
}
```
This lead to problems with projections, as it is now possible for `<T as Foo<true>>::Assoc != <T as Foo<false>>::Assoc`, and forces us to use syntax like `<Type as ~const Foo>::Assoc`.
A discussion of this approach can be found in this [Zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/419616-t-compiler.2Fproject-const-traits/topic/projections.20on.20.28~.29const.20Trait.20.26.20.28~.29const.20assoc.20ty.20bounds). My biggest concern for this would be "how do we teach this", as the requirements for using `<T as const Tr>::Assoc` vs `<T as ~const Tr>::Assoc` vs `<T as Tr>::Assoc` can be unclear/confusing to users.
But note that we would be in this situation only by suggesting that having a `#[const_trait]` creates two separate traits for in a const environment vs in a non-const environment. This paradigm would then make associated types ambiguous. So what if we made it a single trait only?
```rust
#[const_trait]
trait Foo {
type Assoc;
fn method();
}
impl const Foo for () {
type Assoc = ();
fn method() {}
}
```
Let's revisit this example. Under the new desugaring, this would result in:
```rust
trait Foo {
type Effects;
type Assoc;
fn method<const RUNTIME: bool>() where Self::Effects: Compat<RUNTIME>;
}
impl Foo for () {
type Effects = Maybe;
type Assoc = ();
fn method<const RUNTIME: bool>() where Self::Effects: Compat<RUNTIME> {}
}
```
We're implementing one trait instead of two traits, which resolves the issue with projecting associated types.
The effects type could be one out of three types: `Maybe`, which means both const and runtime environment allowed, `NoRuntime`, which only allows running in const, and `Runtime`, which only allows running in runtime.
Each method gets their own `RUNTIME` parameter, and we use the where clause to guard against const contexts calling methods on traits only implemented for a runtime context. The `Compat` trait is defined as follows: (this should be what you expect)
```rust
#[lang = "EffectsCompat"]
pub trait Compat<#[rustc_runtime] const RUNTIME: bool> {}
impl Compat<false> for NoRuntime {}
impl Compat<true> for Runtime {}
impl<#[rustc_runtime] const RUNTIME: bool> Compat<RUNTIME> for Maybe {}
```
Now, there are many places a `~const` bound can appear while interacting with the system of encoding effects through an associated type. Here's a list of them:
* Bounds on a trait/impl method
* Bounds on an associated type in a trait
* Super traits
* Where clauses on trait/impl
We'll take a look at how these can be desugared.
## bounds on method
What happens to `~const` bounds on methods? Take the example
```rust
#[const_trait]
trait Foo {
fn method<T: ~const Bar>();
}
```
We'd desugar it as
```rust
trait Foo {
type Effects;
fn method<const RUNTIME: bool, T: Bar>() where
Self::Effects: Compat<RUNTIME>,
<T as Bar>::Effects: Compat<RUNTIME>;
}
```
Thus, when we are calling from a runtime context, `T: Bar` must be compatible with calling from runtime, and vice versa when in a const context. The `Compat` bound does all the work in checking that effects requirements are satisfied.
## where clauses, the `TyCompat` trait and the `Min` trait
Suppose we have a where clause on a trait.
```rust
#[const_trait] trait Foo<T> where T: ~const Bar {}
```
This clearly means that the current effect must be a subset of the effect on `T: Bar`. Therefore, we can use a `TyCompat` trait (naming bikesheddable) to encode this:
```rust
trait Foo<T> where T: Bar {
type Effects: TyCompat<<(<T as Bar>::Effects,) as Min>::Output>;
}
```
Where we can define TyCompat as
```rust
trait TyCompat<Super> {}
impl TyCompat<Maybe> for NoRuntime {}
impl TyCompat<Maybe> for Runtime {}
impl<T> TyCompat<T> for T {}
```
Where clauses on impls will cause the impl's effect to be the minimum of all effects.
```rust
impl<T> const Foo<T> for Uwu<T> where T: ~const Bar + ~const Baz {}
// desugars into
impl<T> Foo<T> for Uwu<T> where T: Bar {
type Effects = <(<T as Bar>::Effects, <T as Baz>::Effects) as Min>::Output;
}
```
The `Min` trait represents an operator for intersection, with for example `(Maybe, Runtime): Min<Output = Runtime>`, `(Maybe, Maybe): Min<Output = Maybe>`, and `(Runtime, NoRuntime): !Min`.
## assoc type bounds in traits
Bounds on associated types need to relate to the effects on the current trait. If we had
```rust
#[const_trait]
trait Foo {
type Assoc: ~const Bar;
}
```
We would expect `Assoc` to fully allow the environment `impl Foo for Self` has. That is, if `Foo` is implemented with `Maybe` (both runtime and const allowed), then `Self::Assoc` should also be implemented with `Maybe`. If `Foo` is implemented with `Runtime` or `NoRuntime`, then `Self::Assoc` should be implemented with the same effect or `Maybe` which is more relaxed.
We can encode this with a trait bound, to desugar this into `type Assoc: Bar<Effects: TyCompat<Self::Effects>>`.
## super traits
```rust
#[const_trait] trait Bar {}
#[const_trait] trait Foo: ~const Bar {}
```
would desugar into
```rust
trait Bar { type Effects; }
trait Foo: Bar {
type Effects: TyCompat<<(<Self as Bar>::Effects,) as Min>::Output>;
}
```