Skip to content

Commit

Permalink
fix cycle error when a static and a promoted are mutually recursive
Browse files Browse the repository at this point in the history
This also now allows promoteds everywhere to point to 'extern static', because why not?
We still check that constants cannot transitively reach 'extern static' through references.
(We allow it through raw pointers.)
  • Loading branch information
RalfJung committed Feb 12, 2024
1 parent 084ce5b commit f2c3826
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 24 deletions.
13 changes: 2 additions & 11 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
28 changes: 15 additions & 13 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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,
}
}
Expand Down Expand Up @@ -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 => {}
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/consts/cycle-static-promoted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// check-pass

struct Value {
values: &'static [&'static Value],
}

// This cycle goes through a promoted (the slice).
static VALUE: Value = Value {
values: &[&VALUE],
};

fn main() {}

0 comments on commit f2c3826

Please sign in to comment.