Change:
<!-- Thank you for filing a regression report! A regression is something that changed between versions of Rust but was not supposed to. Please provide a short summary of the regression, along with any information you feel is relevant to replicate it. --I tried this code: I expected to see this happen: compile fine, with the static containing a slice that references . Instead, this happened: <!-- Provide the most recent version this worked on, for example: It most recently worked on: Rust 1.47 --It most recently worked on: <!-- Provide the version you are using that has the regression. --:
searched nightlies: from nightly-2024-02-10 to nightly-2024-02-11 regressed nightly: nightly-2024-02-11 searched commit range: regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.8</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with: cc
Context for how I found this: I use this technique in in code generation for benchmarks of generic functions. The simple example I gave is just a minimal reproduction of the issue.
Hm, that is very strange. Not sure if there is a good way to get the backtrace of where in the code the cyclic queries are made? My first guess is that it is the new call to here: But... surely we can ask for the type of a static while evaluating that very static, that can't be cyclic?
Ah no, probably it is related to the fact that we now recursively validate when a constant points to a static. Previously we stopped there. I guess we should not do that for promoteds.
WG-prioritization assigning priority (). label -I-prioritize +P-high -E-needs-bisection +T-compiler

Discussion:
let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) { <del> Some(_) if cid.promoted.is_some() => { // Promoteds in statics are consts that re allowed to point to statics. CtfeValidationMode::Const { allow_immutable_unsafe_cell: false, allow_extern_static_ptrs: true, } } </del> <ins> _ if cid.promoted.is_some() => CtfeValidationMode::Promoted, </ins> Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static` None => { // In normal `const` (not promoted), the outermost allocation is always only copied, // so having `UnsafeCell` in there is okay despite them being in immutable memory. let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner; <del> CtfeValidationMode::Const { allow_immutable_unsafe_cell, allow_extern_static_ptrs: false, } </del> <ins> CtfeValidationMode::Const { allow_immutable_unsafe_cell } </ins> } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;

pub enum CtfeValidationMode { /// Validation of a `static` Static { mutbl: Mutability }, <del> /// Validation of a `const` (including promoteds). </del> <ins> /// Validation of a promoted. Promoted, /// Validation of a `const`. </ins> /// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the /// case for the top-level allocation of a `const`, where this is fine because the allocation will be /// copied at each use site). <del> Const { allow_immutable_unsafe_cell: bool, allow_extern_static_ptrs: bool }, </del> <ins> Const { allow_immutable_unsafe_cell: bool }, </ins> } impl CtfeValidationMode { fn allow_immutable_unsafe_cell(self) -> bool { match self { CtfeValidationMode::Static { .. } => false, <ins> CtfeValidationMode::Promoted { .. } => false, </ins> CtfeValidationMode::Const { allow_immutable_unsafe_cell, .. } => { allow_immutable_unsafe_cell }

fn may_contain_mutable_ref(self) -> bool { match self { CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut, <ins> CtfeValidationMode::Promoted { .. } => false, </ins> CtfeValidationMode::Const { .. } => false, } }

throw_validation_failure!(self.path, MutableRefToImmutable); } } <ins> // Mode-specific checks </ins> match self.ctfe_mode { <del> Some(CtfeValidationMode::Static { .. }) => { </del> <ins> Some( CtfeValidationMode::Static { .. } | CtfeValidationMode::Promoted { .. }, ) => { </ins> // We skip recursively checking other statics. These statics must be sound by // themselves, and the only way to get broken statics here is by using // unsafe code. // The reasons we don't check other statics is twofold. For one, in all // sound cases, the static was already validated on its own, and second, we // trigger cycle errors if we try to compute the value of the other static <del> // and that static refers back to us. </del> <ins> // and that static refers back to us (potentially through a promoted). </ins> // This could miss some UB, but that's fine. return Ok(()); } <del> Some(CtfeValidationMode::Const { allow_extern_static_ptrs, .. }) => { </del> <ins> Some(CtfeValidationMode::Const { .. }) => { </ins> // For consts on the other hand we have to recursively check; // pattern matching assumes a valid value. However we better make // sure this is not mutable. if is_mut { throw_validation_failure!(self.path, ConstRefToMutable); } <ins> // We can't recursively validate `extern static`, so we better reject them. </ins> if self.ecx.tcx.is_foreign_item(did) { <del> if !allow_extern_static_ptrs { throw_validation_failure!(self.path, ConstRefToExtern); } else { // We can't validate this... return Ok(()); } </del> <ins> throw_validation_failure!(self.path, ConstRefToExtern); </ins> } } None => {}

<ins> // check-pass struct Value { values: &'static [&'static Value], } // This `static` recursively points to itself through a promoted (the slice). static VALUE: Value = Value { values: &[&VALUE], }; fn main() {} </ins>