# 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.