Try โ€‚โ€‰HackMD

feature(not_so_min_generic_const_exprs)

summary

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.

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:

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More โ†’

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.

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More โ†’
.

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.