# feature(not_so_min_generic_const_exprs)
## summary
This document should be considered an "extension" to lcnr's [min_generic_const_exprs proposal](https://lcnr.de/blog/generic-const-expressions). It builds off the proposal of only allowing const items (i.e. `ADD:<usize, N, 1>` instead of `N + 1`) by adding a desugaring of `N + 1` and `foo(..)` to const items to allow nice user facing syntax for things that seem like they should "obviously" work.
## changes
### add `AbstractConst` variant to `ty::ConstKind`
Instead of storing generic type level constants as `ConstKind::Unevaluated` we should add a `ConstKind::AbstractConst` and directly lower type level generic exprs to it instead of anonymous const items:
```rust
enum ConstKind<'tcx> {
Param(ty::ParamConst),
Infer(InferConst<'tcx>),
Bound(ty::DebruijnIndex, ty::BoundVar),
Placeholder(ty::PlaceholderConst<'tcx>),
Unevaluated(Unevaluated<'tcx>),
Value(ty::ValTree<'tcx>),
Error(ty::DelaySpanBugEmitted),
// -new
AbstractConst(Expr<'tcx>),
}
// note actual impl may use different types but this is the general idea
enum Expr<'tcx>' {
Binop(Binop, Const<'tcx>, Const<'tcx>),
UnOp(Unop, Const<'tcx>),
FnCall(&'tcx [Const<'tcx>]),
Cast(CastKind, Const<'tcx>, Ty<'tcx>),
}
```
Ideally this `AbstractConst` variant would not be required and we could _actually_ desugar to some lang item consts such as:
`const ADD<T: const Add<U>, U, const LHS: T, const RHS: U>: T::Output`
`const CALL<Args, const FN: impl const Fn<Args> -> _, const ARGS: Args>: FN::Output`
However this would require:
- `feature(adt_const_params)` completion
- a way to do `Foo<T, const N: T>` which adt_const_params does not add
- const trait impls
Also note that we only desugar this for _generic_ type level consts, `foo::<{ 1 }>` would still be an anon const with no paramenv or generics.
### let normalization do `ConstKind::Unevaluated` -> `ConstKind::AbstractConst` or whatever
allow normalization to normalize `ConstKind::Unevaluated` to the various other `ConstKind`'s instead of the current situation of keeping stuff `Unevaluated` "forever" and computing the `AbstractConst` repr on demand which will then have an unnormalized body
note: this makes `ConstKind::Unevaluated` similar/equivalent to `TyKind::Projection`
### do not create "anon const" items
instead of creating 'anon const' items that get independently typeckd using hir typeck. we should instead during astconv create `ConstKind::AbstractConst` representing the hir for the 'anon const'
the representation for the const arg in `-> [(); N + 1_usize]` would be:
```
Const(
TyKind::Projection(<usize as Add<usize>>::Output),
ConstKind::AbstractConst(
Expr::Binop(Add,
Const(TyKind::Uint(usize), ConstKind::Param(N),
Const(TyKind::Uint(usize), ConstKind::Value(1)),
)
)
)
```
this is also why `-> [(); N + 1]` would be an error as the rhs of `Expr::Binop` would have a `Const` of:
```
Const(TyKind::Infer(_), ConstKind::Value(1))
```
and we do not allow inference vars in type signatures.
This is a bit unfortunate as if we could involve trait solving machinery during astconv we could resolve that variable and allow this to compile. Doing so seems kind of difficult so for a "min generic const exprs" this restriction seems acceptable to me, we can also expend more effort later (before stabilisation at latest probably) to see if we can make it work :thinking_face:
## issue triage
### non structurally eq subtrees pcg-47
When unifying generic constants we have to unify each "subtree" as wel as the toplevel `AbstractConst` node. In order to unify a generic const it must be `StructuralEq`, this means that the types of all subexpressions in a generic constant must also be `StructuralEq` i.e.
```rust
#[derive(Eq, PartialEq)]
struct Foo;
impl Add<f32> for Foo {
type Output = usize;
}
const fn bar(n: Foo) -> f32 { 10.0 }
fn foo<const N: Foo>() -> [(); N + bar(N)] { .. }
```
Involves a subtree `bar(N)` of type `f32` which is not `StructuralEq` so we have no way to unify this with another generic constant.
This can be solved be making typeck/wfck ensure that all subtrees (and the root) of `ConstKind::Abstract`'s implement `StructuralEq`. I wouldn't expect this to be too hard to implement. (This feels like it would end up doing a lot of redundant work but that can be worked on later)
### TypeId unsoundness pcg-#46
This document does not affect this as the issue is relatively orthogonal to generic_const_exprs and is instead a problem with higher ranked eq
### WithOptConstParam is sus pcg-#45
Unfortunately `WithOptConstParam` cannot be removed with these changes as `foo::<{ 1 }>` still lowers to an anon const + `ConstKind::Unevaluated` instead of `ConstKind::AbstractConst`.
This document does not affect this
### checking type of const params pcg-#44
This document does not affect this
### Ambiguous generic arguments pcg-#42
This document does not affect this
### Evaluatable bounds pcg-#41
This document does not affect this
### Discarding evaluatable bounds pcg-#40
This document does not affect this
### opaque and transparent constants pcg-#39 / pcg-#31
This document does not affect this
### Subtree evaluation, Associativity, Commutativity pcg-#38
Subtree evaluation is non problematic with this representation for generic constants as we can easily tell when a subtree is concrete (i.e. should be attempted to be evaluated). Additionally this document requires a const evaluator for evaluating `ConstKind::AbstractConst` since we no longer have a `DefId` for the anon const in `ConstKind` so subtree evaluation should "fall out" of the impl for this design.
Associativity ???
Commutativity ???
### Evaluating with inconsistent `where`-clauses pcg-#37
Sometimes during typeck we can end up evaluating constants when where clauses the body depends on holding, don't actually hold.
A straightforward solution to this of "just" attempting to fulfill the predicates in `tcx.param_env(def)` before evaluating `ConstKind::Unevaluated(def, _)` would not work because the anon const desugaring of `generic_const_exprs` results in infinitely recursive where clauses that can never be proven.
With the desugaring changed to `ConstKind::AbstractConst` we no longer have these recursive anon consts so we can attempt to prove the param env of any unevaluateds. We still have to handle proving wfness of `ConstKind::AbstractConst`'s subtrees (and root) before evaluating (even though it would be redundant as it should be handled elsewhere).
### self referential where clauses pcg-#36
With the desugaring changed to no longer be a new definition with separate generics and paramenv this is no longer an issue. We can still encounter cyclic where clauses if they were explicitly written by users in a type signature but this is not very important as they would be "real" cycle errors.
### anon consts in binders pcg-#35
Not sure about this- I would expect things to Just Work here but the issue is not very descriptive about the actual problems we had.
### Valid const parameter types pcg-#34
This document does not affect this
### unused substs on anon consts pcg-#33
Unused substs are no longer an issue as with the new `ConstKind::AbstractConst` lowering as we no longer store a `ConstKind::Unevaluted` with all of the parent substs and `ConstKind::AbstractConst` only mentions generic parameters that are actually used.
### Overly restrictive variance pcg-#32
(note: this issue is not talking about unused substs' interaction with variance, but about the fact that lifetime params are invariant)
This document does not affect this
### leaking ast/hir/thir/mir lowering details during unification pcg-#30
We would now directly lower `hir -> Abstractconst` rather than `thir->AbstractConst` so it would be impossible to leak lowering details of `hir->thir`. This document does not help with `ast->hir` lowering details however.
We may end up still needing `thir->ConstKind::AbstractConst` if we do not require an attribute on assoc consts specifying they be transparent/normalizable to a `ConstKind::AbstractConst` which is a bit unfortunate :thinking_face:.
### Structural Equality pcg-#29
This document does not affect this
### Generic const parameter types pcg-#28
This document does not affect this
### Functions as const parameters pcg-#27
This document does not affect this
### disjoint impls being exhaustive pcg-#26
This document does not affect this
### silent ctfe errors during selection pcg-#25
This changed desugaring might make it easier to implement "only basic ops are silent".
### requirements for const eval pcg-#24
This document does not affect this I think
### constraining const parameters in impl headers through anon consts pcg-#23
lcnr thinks this will be nicer/easier to implement with this document because (dont know didnt understand)
### valtrees and padding pcg-#20
With desugaring (or atleast emulating the behaviour of the desugaring) to const items this is solved as `foo(expr)` results in something like `CALL::<foo, EXPR>` which results in padding being cleared as `EXPR` is turned into a valtree.
### param env construction cycles rust-lang#98956
This is solved because we can trivially equate abstract consts now with "normal" type relation logic without first having to call the typeck query.