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 Unsupported markdown: codespan

to unify Unsupported markdown: codespan with the return type

requires the MIR of Unsupported markdown: codespan

typeck(constant)

opt_const_param_of(constant)

typeck(context)

evaluate Unsupported markdown: codespan

Using WithOptConstParam we get the following setup:

typeck(context)

typeck(constant)

to resolve Unsupported markdown: codespan

requires the MIR of Unsupported markdown: codespan

once finished

once finished

to only typeck Unsupported markdown: codespan once

opt_const_param_of(constant)

returned Unsupported markdown: codespan

typeck_const_arg(constant, const_param)

evaluate Unsupported markdown: codespan

other stuff

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 Unsupported markdown: codespan

to unify Unsupported markdown: codespan 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 Unsupported markdown: codespan

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.