rust
rust copied to clipboard
Recursive references fail in const eval but succeed in const eval behind a fn ptr
I tried this code:
pub struct Foo {
next: &'static Foo,
}
pub struct Bar {
next: fn() -> &'static Bar,
}
// Compiles
pub static FOO: Foo = Foo {
next: &FOO
};
// Cycle
pub const FOO_CONST: Foo = Foo {
next: &FOO_CONST
};
// Cycle
pub const FOO_CONST_REF: &'static Foo = &Foo {
next: FOO_CONST_REF
};
// Compiles
pub const BAR_CONST: Bar = Bar {
next: || &BAR_CONST
};
I expected to see this happen: FOO_CONST and FOO_CONST_REF should not trigger a cycle, OR BAR_CONST should also trigger a cycle.
Instead, this happened: FOO and BAR_CONST do not trigger cycles, but FOO_CONST and FOO_CONST_REF does.
Compiling playground v0.0.1 (/playground)
error[E0391]: cycle detected when const checking `FOO_CONST`
--> src/lib.rs:16:12
|
16 | next: &FOO_CONST
| ^^^^^^^^^
|
= note: ...which immediately requires const checking `FOO_CONST` again
note: cycle used when promoting constants in MIR for `FOO_CONST`
--> src/lib.rs:15:1
|
15 | pub const FOO_CONST: Foo = Foo {
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
For more information about this error, try `rustc --explain E0391`.
error: could not compile `playground` (lib) due to 1 previous error
It feels odd that a reference requires const checking, but an inline closure does not.
Meta
This isn't a regression, this gives various cycle errors on a random sample of rustc versions between the current nightly & 1.40.
The issue is that we need to check whether FOO_CONST doesn't contain UnsafeCell behind a reference. In order to do that, we need to check all constants used in the body, that includes the constant itself. While we could probably fix the direct recursion, indirect recursion via another constant would then suffer the same issue.
I do not believe this is fixable. Any fix that I can think of would be backwards incompatible and cause a lot of inconvenient breakage like const FOO: Option<SomethingVeryNotConst> = None; to stop compiling.
I'm not sure how rustc internals work, take this with a grain of salt.
If treated similar to an uninhabited type (but only in a const context) transmutation that would create an UnsafeCell would result in UB which is (immediately) caught during constant evaluation at the transmutation site, instead of happening downstream.
In your example Option::Some would be (transitively) "uninhabitable" without affecting Option::None. Referenced constants then evaluate out-of-band like closures/fn ptrs.
But again, not an expert here. Just spitballing.