const generics general stuff idk

Where do const expressions appear / come from

  • Types
    • Array [T; C] โ€“ anonymous constants
    • Type parameter S<{C}>
  • Predicates
    • where T: Foo<C>
  • Definitions
    • const N: Ty = C;
  • Expressions
    • const { expr } โ€“ inline constant
      • "basically" equivalent to {const N: Ty = expr; N}
      • except that it can refer to generic parameters from the surrounding scope
    • N (path that refers to a constant)
    • [Expr; C]
    • N::<{C}>
  • Grammar question
    • C = N | Literal | '{' Expr '}'

Generics

  • Extend the set of generics with a const kind

Const values

  • Known value =
    • Integer literals
    • Allocation ==> ValTree eventually
      • note that there are constants that can't be represented with a valtree, those values can appear in the values of statics or constant expressions that don't wind up in types
    • Generic parameter N / Placeholder !C
  • Unevaluated D<Substs...>
    • currently, the Substs can be 'lazy'
    • used because we don't know the set of parameters to start
  • Inference variable ?C

Examples

hir -> ty happens here

  • Foo<22> โ€“ the 22 is a integer literal
  • impl<const N: usize> { ... Foo<N> ... } โ€“ ConstKind::Param
  • impl<const N: usize> { ... Foo<{N}> ... } โ€“ ConstKind::Param
  • ... Foo<_> ... anywhere where types can be inferred โ€“ [ConstKind::Infer] but has feature gate and lcnr doesn't like the way it is implemented
  • const N: usize = 22; ... Foo<{N}> ... โ€“ ConstKind::Unevaluated with the def-id of the parameter (the {N} in Foo<{N}>)
  • Foo< ... > โ€“ as above for all other cases

Inference rules

Unifying two constants:

ConstEquate

ConstEquate(C1, C2)? :=
    ConstEval(C1)? == ConstEval(C2)?

code in trait system

Unclear

how to extend to generic computations

ConstEval

ConstEval(C)? ==> KnownValue

code in rustc_middle::mir::interpret::queries

  • Known => done
  • InfVar => error
  • Uneval(DefId, Substs) =>
    • get the MIR for DefId
    • evaluate it with Substs in scope
    • if this encounters:
      • placeholder:

Niko talking random things

const fn double(n: usize) -> usize { n * 2 }
const fn square(n: usize) -> usize { n * n }

fn main() {
    let mut x = [22; double(2)];
    x = [22; square(2)];
}
impl<const C: usize> {
    double(C) == C * 2
    // this has add'l stability implications:
    //
    //     not just the return value of double(C) but the body
    //     as well
}

Cycle problems

Variance computation

  • We were evaluating constants to compute variance of a type
  • We didn't need to, fixed

Anonymous constants can reference themselves in predicate

fn foo<T, const C: usize>()
where T: PartialEq<[u8; C + 1]>
//                      ^^^^^ anonymous constant

generics(foo) = [T, C]
predicates(foo) = [T: PartialEq<[u8; AC_0]>]

generics(AC_0) = [T, C]
predicates(AC_0) = [T: PartialEq<[u8; AC_0]>] // <-- cycle

Using associated consts in where bounds

https://github.com/rust-lang/rust/issues/79356#issuecomment-772179196

#![feature(generic_const_exprs)]

trait Foo where [(); Self::N]: {
  const N: usize;
}

False inference variable cycles

#![feature(generic_const_exprs)]
#![allow(incomplete_features)]

fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
    todo!()
}

fn main() {
    let mut arr = Default::default();
    arr = bind(arr); //~ ERROR mismatched type
}

// Current solution:
// * 
// * include a subset of predicates

Evaluatable

ConstEvaluatable(Unevaluated<'tcx, ()>)

Match

Structural equality

https://github.com/rust-lang/lang-team/issues/94