rust-lang / project-const-traits

Const Traits Project Group
https://rust-lang.github.io/project-const-traits/
2 stars 0 forks source link

Don't force the user to specify effects for projections #3

Closed fee1-dead closed 1 week ago

fee1-dead commented 2 months ago

This is a problem exclusively for when there is a const RUNTIME: bool param added to all const traits.

Suppose we have the following function:

const fn foo<T: ~const Add>(a: T, b: T) -> T::Output { a + b }

We'd expect this to pass compilation, but if Add has a runtime parameter, T::Output needs to exclusively refer to the not-const version of the trait for backwards compatibility, which means this code would not compile. In fact, it is required to write the function like so, if traits had booleans attached to them:

const fn foo<T: ~const Add>(a: T, b: T) -> <T as ~const Add>::Output { a + b }

This can add a lot of surface syntax noise as people, expectedly, use projections a lot. And constifying functions would lead to these projections all having to specify which context they are in:

But we shouldn't need to do this! We can always be sure that the same type T implementing a trait Foo will always have <T as Foo>::X == <T as const Foo>::X if they do implement both the non-const and always-const version of the trait. This is due to the fact that we always store the "which context this type is in" in types themselves, leading to them being locked to a specific context.

For example, let's say in the future const closures are implemented. We always know which context this closure is in based on its type. (Note that closures will never implement both non-const and always-const, only exactly one context, see https://github.com/rust-lang/rust/issues/119718.) That means the same closure (suppose we bind its constness to a generic param, such that it will eventually figure out what constness it is) used in a const context and in a non-const context will be two different types (imagine one with <true> attached to it and the other with <false>, therefore we never have to worry about different kinds of projections giving us different types.

fee1-dead commented 2 months ago

On second thought, my last two paragraphs could be wrong. There are types that are not locked to specific contexts like u32 and trait impls for them could easily project to different types should we start allowing things like function pointers to be specified as always const or not:

#[const_trait]
trait Foo {
    type Bar: ~const FnOnce();
}

impl const Foo for u32 {
    type Bar = ~const fn();
}
compiler-errors commented 1 week ago

I believe this is also finished? Since fee1-dead's rewrite to use associated consts, effects are fully "late-bound", so this problem doesn't matter anymore.

I do think we should open a follow-up issue about WTF to do with ~const fn(), b/c that comes with its own set of really weird problems (also because it would be a distinct type from fn(), and that has tons of ecosystem implications).