Try   HackMD

WithOptConstParam cycle diagram

struct Foo;
impl Foo {
    fn foo<const N: usize>(self) -> [u8; N] { [0; N] }
    //           ^ called `const_param` in the diagram
}
struct Bar;
impl Bar {
    fn foo<const M: i32>(self) -> [u8; 3] { [0; 3] }
}
fn context() -> [u8; 4] {
    let x = Foo;
    x.foo::<{ 3 + 1 }>()
    //      ^^^^^^^^^ called `constant` in the diagram
}

Without WithOptConstParam we would get the following cycle:

needs the expected return type
resolve `foo`
to unify `constant` with the return type
requires the MIR of `constant`
typeck(constant)
opt_const_param_of(constant)
typeck(context)
evaluate `constant`

Using WithOptConstParam we get the following setup:

typeck(context)
typeck(constant)
to resolve `foo`
requires the MIR of `constant`
once finished
once finished
to only typeck `constant` once
evaluate `constant`
other stuff
opt_const_param_of(constant)
returned `Some(const_param)`
typeck_const_arg(constant, const_param)

While this works, it does get a lot more complex when the anonymous constant contains closures or inline consts. Both of these things are luckily either unstable or not useful.

struct Foo;
impl Foo {
    fn foo<const N: usize>(self) -> [u8; N] { [0; N] }
    //           ^ called `const_param` in the diagram
}
fn context() -> [u8; 4] {
    let x = Foo;
    // either THIS:
    x.foo::<{ const {1} + 3 }>();
    //        ^^^^^^^^^ called `child` in the diagram
    //      ^^^^^^^^^^^^^ called `constant` in the diagram
    
    // or THIS:
    x.foo::<{ (|| 4)() }>();
    //        ^^^^^^ called `child` in the diagram
    //      ^^^^^^^^^^^^^ called `constant` in the diagram
}
needs the expected return type
resolve `foo`
to unify `constant` with the return type
requires the MIR
relies on facts from nested items
closures and inline consts rely on the parents typeck
typeck(constant)
opt_const_param_of(constant)
typeck(context)
evaluate `constant`
mir_borrowck(constant, const_param)
mir_borrowck(child)

The issue is that, when calling mir_borrowck - or some similar query - for the child, we "forget" the const parameter for constant by entering a new query. Changing constants and inline constants to also always use WithOptConstParam to remember the relevant const parameter for their parent seems like quite a lot of effort.

As this isn't a high priority I would prefer to wait for less hacky fix by improving the query system to deal with these cycles.