This document should be considered an "extension" to lcnr's min_generic_const_exprs proposal. 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.
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:
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)
completionFoo<T, const N: T>
which adt_const_params does not addAlso note that we only desugar this for generic type level consts, foo::<{ 1 }>
would still be an anon const with no paramenv or generics.
ConstKind::Unevaluated
-> ConstKind::AbstractConst
or whateverallow 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
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
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.
#[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)
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
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
This document does not affect this
This document does not affect this
This document does not affect this
This document does not affect this
This document does not affect this
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 ???
where
-clauses pcg-#37Sometimes 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).
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.
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.
This document does not affect this
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.
(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
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
This document does not affect this
This document does not affect this
This document does not affect this
This document does not affect this
This changed desugaring might make it easier to implement "only basic ops are silent".
This document does not affect this I think
lcnr thinks this will be nicer/easier to implement with this document because (dont know didnt understand)
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.
This is solved because we can trivially equate abstract consts now with "normal" type relation logic without first having to call the typeck query.