rust icon indicating copy to clipboard operation
rust copied to clipboard

Recursive references fail in const eval but succeed in const eval behind a fn ptr

Open ds84182 opened this issue 1 year ago • 1 comments

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.

ds84182 avatar Feb 02 '24 00:02 ds84182

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.

oli-obk avatar Feb 02 '24 09:02 oli-obk

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.

ds84182 avatar Feb 02 '24 21:02 ds84182