diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 0844cdbe99b88..c55d899e4d5a2 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -356,22 +356,13 @@ pub fn const_validate_mplace<'mir, 'tcx>( let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) { - 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, - } - } + _ if cid.promoted.is_some() => CtfeValidationMode::Promoted, 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; - CtfeValidationMode::Const { - allow_immutable_unsafe_cell, - allow_extern_static_ptrs: false, - } + CtfeValidationMode::Const { allow_immutable_unsafe_cell } } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 38aeace02ba4b..05e28db652f21 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -129,17 +129,20 @@ pub enum PathElem { pub enum CtfeValidationMode { /// Validation of a `static` Static { mutbl: Mutability }, - /// Validation of a `const` (including promoteds). + /// Validation of a promoted. + Promoted, + /// Validation of a `const`. /// `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). - Const { allow_immutable_unsafe_cell: bool, allow_extern_static_ptrs: bool }, + Const { allow_immutable_unsafe_cell: bool }, } impl CtfeValidationMode { fn allow_immutable_unsafe_cell(self) -> bool { match self { CtfeValidationMode::Static { .. } => false, + CtfeValidationMode::Promoted { .. } => false, CtfeValidationMode::Const { allow_immutable_unsafe_cell, .. } => { allow_immutable_unsafe_cell } @@ -149,6 +152,7 @@ impl CtfeValidationMode { fn may_contain_mutable_ref(self) -> bool { match self { CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut, + CtfeValidationMode::Promoted { .. } => false, CtfeValidationMode::Const { .. } => false, } } @@ -476,34 +480,32 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' throw_validation_failure!(self.path, MutableRefToImmutable); } } + // Mode-specific checks match self.ctfe_mode { - Some(CtfeValidationMode::Static { .. }) => { + Some( + CtfeValidationMode::Static { .. } + | CtfeValidationMode::Promoted { .. }, + ) => { // 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 - // and that static refers back to us. + // and that static refers back to us (potentially through a promoted). // This could miss some UB, but that's fine. return Ok(()); } - Some(CtfeValidationMode::Const { - allow_extern_static_ptrs, .. - }) => { + Some(CtfeValidationMode::Const { .. }) => { // 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); } + // We can't recursively validate `extern static`, so we better reject them. if self.ecx.tcx.is_foreign_item(did) { - if !allow_extern_static_ptrs { - throw_validation_failure!(self.path, ConstRefToExtern); - } else { - // We can't validate this... - return Ok(()); - } + throw_validation_failure!(self.path, ConstRefToExtern); } } None => {} diff --git a/tests/ui/consts/cycle-static-promoted.rs b/tests/ui/consts/cycle-static-promoted.rs new file mode 100644 index 0000000000000..5838dc58a3a15 --- /dev/null +++ b/tests/ui/consts/cycle-static-promoted.rs @@ -0,0 +1,12 @@ +// 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() {}