# `WithOptConstParam` cycle diagram
```rust
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:
```mermaid
flowchart TD
TypeckAnonConst["typeck(constant)"]
OptConstParam["opt_const_param_of(constant)"]
TypeckContext["typeck(context)"]
EvaluateConst["evaluate `constant`"]
TypeckAnonConst -- needs the expected return type --> OptConstParam
OptConstParam -- resolve `foo` --> TypeckContext
TypeckContext -- to unify `constant` with the return type --> EvaluateConst
EvaluateConst -- requires the MIR of `constant` --> TypeckAnonConst
```
Using `WithOptConstParam` we get the following setup:
```mermaid
flowchart TD
subgraph TypeckAnonConst["typeck(constant)"]
OptConstParam["opt_const_param_of(constant)"]
CallTypeckConstArg["returned `Some(const_param)`"]
end
TypeckConstArg["typeck_const_arg(constant, const_param)"]
OptConstParam["opt_const_param_of(constant)"]
subgraph TypeckContext["typeck(context)"]
EvaluateConstant["evaluate `constant`"]
OtherStuff["other stuff"]
end
OptConstParam -- "to resolve `foo`" --> EvaluateConstant
EvaluateConstant -- "requires the MIR of `constant`"--> TypeckConstArg
TypeckConstArg -- "once finished" --> OtherStuff
OtherStuff -- "once finished" --> CallTypeckConstArg
CallTypeckConstArg -- "to only typeck `constant` once" --> TypeckConstArg
```
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.
```rust
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
}
```
```mermaid
flowchart TD
TypeckAnonConst["typeck(constant)"]
OptConstParam["opt_const_param_of(constant)"]
TypeckContext["typeck(context)"]
EvaluateConst["evaluate `constant`"]
BorrowckConst["mir_borrowck(constant, const_param)"]
BorrowckChild["mir_borrowck(child)"]
TypeckAnonConst -- needs the expected return type --> OptConstParam
OptConstParam -- resolve `foo` --> TypeckContext
TypeckContext -- to unify `constant` with the return type --> EvaluateConst
EvaluateConst -- requires the MIR --> BorrowckConst
BorrowckConst -- relies on facts from nested items --> BorrowckChild
BorrowckChild -- closures and inline consts rely on the parents typeck --> TypeckAnonConst
```
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.