min_generic_const_exprs
written anonymously by two random people you will never guess who
Closures with anon consts in args cause "closure/generation that references itself" errors https://github.com/rust-lang/rust/issues/85665
#![feature(generic_const_exprs)]
struct Foo<F: Fn([(); N + 1]), const N: usize>(F);
fn bar() {
Foo::<_, 2>(|_|{});
}
AnonConsts mark all params as invariant
#![feature(generic_const_exprs)]
struct Foo<T, U, const N: usize>([(); N + 1])
where
[(); N + 1]:;
ConstEquate
calling typeckanon consts can refer to themselves via their own where
-bounds see https://github.com/rust-lang/project-const-generics/blob/master/design-docs/anon-const-substs.md#consts-in-where-bounds-can-reference-themselves
additionally by requiring each const to be typeck'd to build thir in order to unify consts we can trivially cause cycles if typechecking a const requires solving a ConstEquate
obligation
Self
in trait where clausethe following currently cycles and should be handled somehow:
trait Trait where evaluatable { <Self as Trait>::CONST } {
const CONST: usize;
}
e.g. https://github.com/rust-lang/rust/pull/90529
by using thir to unify consts we run the risk of accidentally exposing impl details of how we lower language things to thir which is concerning
Introduce a feature(min_generic_const_exprs)
that only allows assoc and free constants as anon consts:
// requires generic free constants and generic associated constants
//
// GFCs and GACs
//
// I do want these anyways, so :shrug:
const ADD_ONE<const N: usize> = N + 1;
fn push<T, const N: usize>(a: [T; N], v: T) -> [T; ADD_ONE::<N>] {
// ...
}
or using an associated constant
trait AddOne<const N: usize> {
const ADDED: usize;
}
impl<const N: usize> AddOne<N> for () {
const ADDED: usize = N + 1;
}
fn push<T, const N: usize>(a: [T; N], v: T) -> [T; <() as AddOne<N>>::ADDED] {
// ...
}
fn push<T, const N: usize>(a: [T; N], v: T) -> [T; N + 1] {
//~^ error: `N + 1` is not a free const or an assoc const
}
The following also compiles:
const ADD<const LHS: usize, const RHS: usize> = LHS + RHS;
fn foo<const N: usize>() -> [(); ADD<N, 3>] {
[(); ADD<N, { 1 + 2 }>]
}
where
-bound cycles as we do not require typeck
to unify conststrait Trait where evaluatable { <Self as Trait>::CONST }
still cyclesN + 1
is not allowed) but this will be solved by feature(generic_const_exprs)
fn push<T, const N: usize>(a: [T; N], v: T) -> [T; const<N> { N + 1 }] {
// ...
}
const<N> where x { expr }
has completely separate generics from its parent.
We can still keep feature(generic_const_exprs)
to allow anon const expressions which inherit their parents generics like N + 1
.
ADD_ONE::<N>
ADD_ONE
constconst<GENERICS> WHERE_CLAUSES
we can avoid any unused_substs
issuesfeature(min_generic_const_exprs)
language future compatby keeping the special case for anon consts which only consist of named constants, or by correctly dealing with both unused substs and allowing the evaluation of subtrees of AbstractConst
s, feature(generic_const_exprs)
should not introduce any breaking changes after feature(min_generic_const_exprs)
has been stabilized.
feature(min_generic_const_exprs)
library concernsIssue: By requiring the use of named constants which only unify with themselves, constants for the same expr, e.g. N + 1
, which are defined in separate libraries, do not unify.
Solution: Add a module to the std called std::constants
and add constants for pretty much all common ops, e.g. std::constants::ADD
. Add lints when redefining these constants.
Issue: If we later stabilize feature(generic_const_exprs)
we need ADD<N, 1>
to unify with N + 1
. Otherwise we wouldn't be able to update the std api.
Solution: Add an attribute, e.g. #[transparent_const]
, which, when applied to named constants, causes rustc to "unwrap" that constant when used in types. With this #[transparent_const] const ADD<const N: usize, const M: usize>: usize = N + M
unifies with addition.
Issue: Unifying associated constants with other constants, consider the following example
trait ReturnsArray {
const RETURN_LENGTH<const N: usize>;
fn foo<const N: usize>(arr: [u8; N]) -> [u8; RETURN_LENGTH::<N>];
}
impl ReturnsArray for () {
const RETURN_LENGTH<const N: usize> = ADD<N, 1>;
fn foo<const N: usize>(arr: [u8; N]) -> [u8; RETURN_LENGTH::<N>] {
// `ADD<N, 1>` and `RETURN_LENGTH::<N>` don't trivially unify.
// so this would error.
arr.push(0);
}
}
Solution we probably again need something like#[transparent_const]
or even do this implicitly if the definition of an associated constant is another associated consts or sth.