Miri interning: replace ICEs by proper errors, make intern_shallow type signature more precise

This commit is contained in:
Ralf Jung 2020-04-29 10:00:22 +02:00
parent b3269536d0
commit c400f758e5
16 changed files with 274 additions and 287 deletions

View file

@ -871,6 +871,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Our result will later be validated anyway, and there seems no good reason
// to have to fail early here. This is also more consistent with
// `Memory::get_static_alloc` which has to use `const_eval_raw` to avoid cycles.
// FIXME: We can hit delay_span_bug if this is an invalid const, interning finds
// that problem, but we never run validation to show an error. Can we ensure
// this does not happen?
let val = self.tcx.const_eval_raw(param_env.and(gid))?;
self.raw_const_to_mplace(val)
}

View file

@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_middle::mir::interpret::{ErrorHandled, InterpResult};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, query::TyCtxtAt, Ty};
use rustc_ast::ast::Mutability;
@ -29,43 +29,44 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> {
/// The ectx from which we intern.
ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
/// Previously encountered safe references.
ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, InternMode)>,
/// A list of all encountered allocations. After type-based interning, we traverse this list to
/// also intern allocations that are only referenced by a raw pointer or inside a union.
leftover_allocations: &'rt mut FxHashSet<AllocId>,
/// The root node of the value that we're looking at. This field is never mutated and only used
/// The root kind of the value that we're looking at. This field is never mutated and only used
/// for sanity assertions that will ICE when `const_qualif` screws up.
mode: InternMode,
/// This field stores the mutability of the value *currently* being checked.
/// When encountering a mutable reference, we determine the pointee mutability
/// taking into account the mutability of the context: `& &mut i32` is entirely immutable,
/// despite the nested mutable reference!
/// The field gets updated when an `UnsafeCell` is encountered.
mutability: Mutability,
/// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect
/// the intern mode of references we encounter.
inside_unsafe_cell: bool,
/// This flag is to avoid triggering UnsafeCells are not allowed behind references in constants
/// for promoteds.
/// It's a copy of `mir::Body`'s ignore_interior_mut_in_const_validation field
ignore_interior_mut_in_const_validation: bool,
ignore_interior_mut_in_const: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
/// Mutable references must in fact be immutable due to their surrounding immutability in a
/// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
/// that will actually be treated as mutable.
Static,
/// UnsafeCell is OK in the value of a constant: `const FOO = Cell::new(0)` creates
/// a new cell every time it is used.
/// A static and its current mutability. Below shared references inside a `static mut`,
/// this is *immutable*, and below mutable references inside an `UnsafeCell`, this
/// is *mutable*.
Static(hir::Mutability),
/// The "base value" of a const, which can have `UnsafeCell` (as in `const FOO: Cell<i32>`),
/// but that interior mutability is simply ignored.
ConstBase,
/// `UnsafeCell` ICEs.
Const,
/// The "inner values" of a const with references, where `UnsafeCell` is an error.
ConstInner,
}
/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;
fn mutable_memory_in_const(tcx: TyCtxtAt<'_>, kind: &str) {
tcx.sess.span_err(tcx.span, &format!("mutable memory ({}) is not allowed in constant", kind));
}
/// Intern an allocation without looking at its children.
/// `mode` is the mode of the environment where we found this pointer.
/// `mutablity` is the mutability of the place to be interned; even if that says
@ -75,12 +76,11 @@ struct IsStaticOrFn;
fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
leftover_allocations: &'rt mut FxHashSet<AllocId>,
mode: InternMode,
alloc_id: AllocId,
mutability: Mutability,
mode: InternMode,
ty: Option<Ty<'tcx>>,
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
trace!("InternVisitor::intern {:?} with {:?}", alloc_id, mutability,);
trace!("intern_shallow {:?} with {:?}", alloc_id, mode);
// remove allocation
let tcx = ecx.tcx;
let (kind, mut alloc) = match ecx.memory.alloc_map.remove(&alloc_id) {
@ -89,8 +89,9 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
// Pointer not found in local memory map. It is either a pointer to the global
// map, or dangling.
// If the pointer is dangling (neither in local nor global memory), we leave it
// to validation to error. The `delay_span_bug` ensures that we don't forget such
// a check in validation.
// to validation to error -- it has the much better error messages, pointing out where
// in the value the dangling reference lies.
// The `delay_span_bug` ensures that we don't forget such a check in validation.
if tcx.get_global_alloc(alloc_id).is_none() {
tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
}
@ -107,28 +108,28 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
// Set allocation mutability as appropriate. This is used by LLVM to put things into
// read-only memory, and also by Miri when evaluating other globals that
// access this one.
if mode == InternMode::Static {
// When `ty` is `None`, we assume no interior mutability.
if let InternMode::Static(mutability) = mode {
// For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume
// no interior mutability.
let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx.tcx, ecx.param_env, ecx.tcx.span));
// For statics, allocation mutability is the combination of the place mutability and
// the type mutability.
// The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
if mutability == Mutability::Not && frozen {
let immutable = mutability == Mutability::Not && frozen;
if immutable {
alloc.mutability = Mutability::Not;
} else {
// Just making sure we are not "upgrading" an immutable allocation to mutable.
assert_eq!(alloc.mutability, Mutability::Mut);
}
} else {
// We *could* be non-frozen at `ConstBase`, for constants like `Cell::new(0)`.
// But we still intern that as immutable as the memory cannot be changed once the
// initial value was computed.
// Constants are never mutable.
assert_eq!(
mutability,
Mutability::Not,
"Something went very wrong: mutability requested for a constant"
);
// No matter what, *constants are never mutable*. Mutating them is UB.
// See const_eval::machine::MemoryExtra::can_access_statics for why
// immutability is so important.
// There are no sensible checks we can do here; grep for `mutable_memory_in_const` to
// find the checks we are doing elsewhere to avoid even getting here for memory
// that "wants" to be mutable.
alloc.mutability = Mutability::Not;
};
// link the alloc id to the actual allocation
@ -142,10 +143,16 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> InternVisitor<'rt, 'mir
fn intern_shallow(
&mut self,
alloc_id: AllocId,
mutability: Mutability,
mode: InternMode,
ty: Option<Ty<'tcx>>,
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
intern_shallow(self.ecx, self.leftover_allocations, self.mode, alloc_id, mutability, ty)
intern_shallow(
self.ecx,
self.leftover_allocations,
alloc_id,
mode,
ty,
)
}
}
@ -166,22 +173,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
) -> InterpResult<'tcx> {
if let Some(def) = mplace.layout.ty.ty_adt_def() {
if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
if self.mode == InternMode::ConstInner && !self.ignore_interior_mut_in_const {
// We do not actually make this memory mutable. But in case the user
// *expected* it to be mutable, make sure we error. This is just a
// sanity check to prevent users from accidentally exploiting the UB
// they caused. It also helps us to find cases where const-checking
// failed to prevent an `UnsafeCell` (but as `ignore_interior_mut_in_const`
// shows that part is not airtight).
mutable_memory_in_const(self.ecx.tcx, "`UnsafeCell`");
}
// We are crossing over an `UnsafeCell`, we can mutate again. This means that
// References we encounter inside here are interned as pointing to mutable
// allocations.
let old = std::mem::replace(&mut self.mutability, Mutability::Mut);
if !self.ignore_interior_mut_in_const_validation {
assert_ne!(
self.mode,
InternMode::Const,
"UnsafeCells are not allowed behind references in constants. This should \
have been prevented statically by const qualification. If this were \
allowed one would be able to change a constant at one use site and other \
use sites could observe that mutation.",
);
}
// Remember the `old` value to handle nested `UnsafeCell`.
let old = std::mem::replace(&mut self.inside_unsafe_cell, true);
let walked = self.walk_aggregate(mplace, fields);
self.mutability = old;
self.inside_unsafe_cell = old;
return walked;
}
}
@ -191,23 +198,26 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
fn visit_value(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
// Handle Reference types, as these are the only relocations supported by const eval.
// Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
let tcx = self.ecx.tcx;
let ty = mplace.layout.ty;
if let ty::Ref(_, referenced_ty, mutability) = ty.kind {
if let ty::Ref(_, referenced_ty, ref_mutability) = ty.kind {
let value = self.ecx.read_immediate(mplace.into())?;
let mplace = self.ecx.ref_to_mplace(value)?;
assert_eq!(mplace.layout.ty, referenced_ty);
// Handle trait object vtables.
if let ty::Dynamic(..) =
self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind
tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind
{
// Validation has already errored on an invalid vtable pointer so we can safely not
// do anything if this is not a real pointer.
// Validation will error (with a better message) on an invalid vtable pointer
// so we can safely not do anything if this is not a real pointer.
if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() {
// Explicitly choose `Immutable` here, since vtables are immutable, even
// Explicitly choose const mode here, since vtables are immutable, even
// if the reference of the fat pointer is mutable.
self.intern_shallow(vtable.alloc_id, Mutability::Not, None)?;
self.intern_shallow(vtable.alloc_id, InternMode::ConstInner, None)?;
} else {
self.ecx().tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
// Let validation show the error message, but make sure it *does* error.
tcx.sess.delay_span_bug(
tcx.span,
"vtables pointers cannot be integer pointers",
);
}
@ -215,54 +225,66 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
// Check if we have encountered this pointer+layout combination before.
// Only recurse for allocation-backed pointers.
if let Scalar::Ptr(ptr) = mplace.ptr {
// We do not have any `frozen` logic here, because it's essentially equivalent to
// the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
// and we check that in `visit_aggregate`.
// This is not an inherent limitation, but one that we know to be true, because
// const qualification enforces it. We can lift it in the future.
match (self.mode, mutability) {
// immutable references are fine everywhere
(_, hir::Mutability::Not) => {}
// all is "good and well" in the unsoundness of `static mut`
// Compute the mode with which we intern this.
let ref_mode = match self.mode {
InternMode::Static(mutbl) => {
// In statics, merge outer mutability with reference mutability and
// take into account whether we are in an `UnsafeCell`.
// mutable references are ok in `static`. Either they are treated as immutable
// because they are behind an immutable one, or they are behind an `UnsafeCell`
// and thus ok.
(InternMode::Static, hir::Mutability::Mut) => {}
// we statically prevent `&mut T` via `const_qualif` and double check this here
(InternMode::ConstBase | InternMode::Const, hir::Mutability::Mut) => {
match referenced_ty.kind {
ty::Array(_, n)
if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
ty::Slice(_)
if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? == 0 => {}
_ => bug!("const qualif failed to prevent mutable references"),
// The only way a mutable reference actually works as a mutable reference is
// by being in a `static mut` directly or behind another mutable reference.
// If there's an immutable reference or we are inside a `static`, then our
// mutable reference is equivalent to an immutable one. As an example:
// `&&mut Foo` is semantically equivalent to `&&Foo`
match ref_mutability {
_ if self.inside_unsafe_cell => {
// Inside an `UnsafeCell` is like inside a `static mut`, the "outer"
// mutability does not matter.
InternMode::Static(ref_mutability)
}
Mutability::Not => {
// A shared reference, things become immutable.
// We do *not* consier `freeze` here -- that is done more precisely
// when traversing the referenced data (by tracking `UnsafeCell`).
InternMode::Static(Mutability::Not)
}
Mutability::Mut => {
// Mutable reference.
InternMode::Static(mutbl)
}
}
}
}
// Compute the mutability with which we'll start visiting the allocation. This is
// what gets changed when we encounter an `UnsafeCell`.
//
// The only way a mutable reference actually works as a mutable reference is
// by being in a `static mut` directly or behind another mutable reference.
// If there's an immutable reference or we are inside a static, then our
// mutable reference is equivalent to an immutable one. As an example:
// `&&mut Foo` is semantically equivalent to `&&Foo`
let mutability = self.mutability.and(mutability);
// Recursing behind references changes the intern mode for constants in order to
// cause assertions to trigger if we encounter any `UnsafeCell`s.
let mode = match self.mode {
InternMode::ConstBase => InternMode::Const,
other => other,
InternMode::ConstBase | InternMode::ConstInner => {
// Ignore `UnsafeCell`, everything is immutable. Do some sanity checking
// for mutable references that we encounter -- they must all be ZST.
// This helps to prevent users from accidentally exploiting UB that they
// caused (by somehow getting a mutable reference in a `const`).
if ref_mutability == Mutability::Mut {
match referenced_ty.kind {
ty::Array(_, n)
if n.eval_usize(tcx.tcx, self.ecx.param_env) == 0 => {}
ty::Slice(_)
if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? == 0 => {}
_ => mutable_memory_in_const(tcx, "`&mut`"),
}
} else {
// A shared reference. We cannot check `freeze` here due to references
// like `&dyn Trait` that are actually immutable. We do check for
// concrete `UnsafeCell` when traversing the pointee though (if it is
// a new allocation, not yet interned).
}
// Go on with the "inner" rules.
InternMode::ConstInner
}
};
match self.intern_shallow(ptr.alloc_id, mutability, Some(mplace.layout.ty))? {
match self.intern_shallow(ptr.alloc_id, ref_mode, Some(referenced_ty))? {
// No need to recurse, these are interned already and statics may have
// cycles, so we don't want to recurse there
Some(IsStaticOrFn) => {}
// intern everything referenced by this value. The mutability is taken from the
// reference. It is checked above that mutable references only happen in
// `static mut`
None => self.ref_tracking.track((mplace, mutability, mode), || ()),
None => self.ref_tracking.track((mplace, ref_mode), || ()),
}
}
Ok(())
@ -273,6 +295,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
}
}
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
pub enum InternKind {
/// The `mutability` of the static, ignoring the type which may have interior mutability.
Static(hir::Mutability),
@ -285,48 +308,48 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
ecx: &mut InterpCx<'mir, 'tcx, M>,
intern_kind: InternKind,
ret: MPlaceTy<'tcx>,
ignore_interior_mut_in_const_validation: bool,
ignore_interior_mut_in_const: bool,
) -> InterpResult<'tcx>
where
'tcx: 'mir,
{
let tcx = ecx.tcx;
let (base_mutability, base_intern_mode) = match intern_kind {
// `static mut` doesn't care about interior mutability, it's mutable anyway
InternKind::Static(mutbl) => (mutbl, InternMode::Static),
let base_intern_mode = match intern_kind {
InternKind::Static(mutbl) => InternMode::Static(mutbl),
// FIXME: what about array lengths, array initializers?
InternKind::Constant | InternKind::ConstProp => (Mutability::Not, InternMode::ConstBase),
InternKind::Promoted => (Mutability::Not, InternMode::ConstBase),
InternKind::Constant | InternKind::ConstProp | InternKind::Promoted =>
InternMode::ConstBase,
};
// Type based interning.
// `ref_tracking` tracks typed references we have seen and still need to crawl for
// `ref_tracking` tracks typed references we have already interned and still need to crawl for
// more typed information inside them.
// `leftover_allocations` collects *all* allocations we see, because some might not
// be available in a typed way. They get interned at the end.
let mut ref_tracking = RefTracking::new((ret, base_mutability, base_intern_mode));
let mut ref_tracking = RefTracking::empty();
let leftover_allocations = &mut FxHashSet::default();
// start with the outermost allocation
intern_shallow(
ecx,
leftover_allocations,
base_intern_mode,
// The outermost allocation must exist, because we allocated it with
// `Memory::allocate`.
ret.ptr.assert_ptr().alloc_id,
base_mutability,
base_intern_mode,
Some(ret.layout.ty),
)?;
while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
ref_tracking.track((ret, base_intern_mode), || ());
while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() {
let interned = InternVisitor {
ref_tracking: &mut ref_tracking,
ecx,
mode,
leftover_allocations,
mutability,
ignore_interior_mut_in_const_validation,
ignore_interior_mut_in_const,
inside_unsafe_cell: false,
}
.visit_value(mplace);
if let Err(error) = interned {
@ -366,26 +389,28 @@ where
InternKind::Static(_) => {}
// Raw pointers in promoteds may only point to immutable things so we mark
// everything as immutable.
// It is UB to mutate through a raw pointer obtained via an immutable reference.
// It is UB to mutate through a raw pointer obtained via an immutable reference:
// Since all references and pointers inside a promoted must by their very definition
// be created from an immutable reference (and promotion also excludes interior
// mutability), mutating through them would be UB.
// There's no way we can check whether the user is using raw pointers correctly,
// so all we can do is mark this as immutable here.
InternKind::Promoted => {
// See const_eval::machine::MemoryExtra::can_access_statics for why
// immutability is so important.
alloc.mutability = Mutability::Not;
}
InternKind::Constant | InternKind::ConstProp => {
// If it's a constant, it *must* be immutable.
// We cannot have mutable memory inside a constant.
// We use `delay_span_bug` here, because this can be reached in the presence
// of fancy transmutes.
if alloc.mutability == Mutability::Mut {
// For better errors later, mark the allocation as immutable
// (on top of the delayed ICE).
alloc.mutability = Mutability::Not;
ecx.tcx.sess.delay_span_bug(ecx.tcx.span, "mutable allocation in constant");
}
// If it's a constant, we should not have any "leftovers" as everything
// is tracked by const-checking.
// FIXME: downgrade this to a warning? It rejects some legitimate consts,
// such as `const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;`.
ecx.tcx.sess.span_err(
ecx.tcx.span,
"untyped pointers are not allowed in constant",
);
// For better errors later, mark the allocation as immutable.
alloc.mutability = Mutability::Not;
}
}
let alloc = tcx.intern_const_alloc(alloc);

View file

@ -1,25 +0,0 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
// normalize-stderr-test "alloc[0-9]+" -> "allocN"
#![deny(const_err)] // The `allow` variant is tested by `mutable_const2`.
//~^ NOTE lint level
// Here we check that even though `MUTABLE_BEHIND_RAW` is created from a mutable
// allocation, we intern that allocation as *immutable* and reject writes to it.
// We avoid the `delay_span_bug` ICE by having compilation fail via the `deny` above.
use std::cell::UnsafeCell;
// make sure we do not just intern this as mutable
const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
const MUTATING_BEHIND_RAW: () = { //~ NOTE
// Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time.
unsafe {
*MUTABLE_BEHIND_RAW = 99 //~ ERROR any use of this value will cause an error
//~^ NOTE: which is read-only
// FIXME would be good to match more of the error message here, but looks like we
// normalize *after* checking the annoations here.
}
};
fn main() {}

View file

@ -1,39 +0,0 @@
error: any use of this value will cause an error
--> $DIR/mutable_const.rs:18:9
|
LL | / const MUTATING_BEHIND_RAW: () = {
LL | | // Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time.
LL | | unsafe {
LL | | *MUTABLE_BEHIND_RAW = 99
| | ^^^^^^^^^^^^^^^^^^^^^^^^ writing to allocN which is read-only
... |
LL | | }
LL | | };
| |__-
|
note: the lint level is defined here
--> $DIR/mutable_const.rs:4:9
|
LL | #![deny(const_err)] // The `allow` variant is tested by `mutable_const2`.
| ^^^^^^^^^
warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/mutable_const.rs:13:38
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^
help: skipping check for `const_raw_ptr_deref` feature
--> $DIR/mutable_const.rs:18:9
|
LL | *MUTABLE_BEHIND_RAW = 99
| ^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_const.rs:18:9
|
LL | *MUTABLE_BEHIND_RAW = 99
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted

View file

@ -1,16 +0,0 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
// failure-status: 101
// rustc-env:RUST_BACKTRACE=0
// normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET"
// normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS"
// normalize-stderr-test "interpret/intern.rs:[0-9]+:[0-9]+" -> "interpret/intern.rs:LL:CC"
#![allow(const_err)]
use std::cell::UnsafeCell;
// make sure we do not just intern this as mutable
const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
//~^ ERROR: mutable allocation in constant
fn main() {}

View file

@ -1,29 +0,0 @@
warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/mutable_const2.rs:13:38
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^
warning: 1 warning emitted
error: internal compiler error: mutable allocation in constant
--> $DIR/mutable_const2.rs:13:1
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:366:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: internal compiler error: unexpected panic
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: rustc VERSION running on TARGET
note: compiler flags: FLAGS

View file

@ -17,12 +17,11 @@ struct Foo<T>(T);
// this is fine for the same reason as `BAR`.
static BOO: &mut Foo<()> = &mut Foo(());
// interior mutability is fine
struct Meh {
x: &'static UnsafeCell<i32>,
}
unsafe impl Sync for Meh {}
static MEH: Meh = Meh {
x: &UnsafeCell::new(42),
};

View file

@ -1,5 +1,5 @@
error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item
--> $DIR/mutable_references.rs:37:5
--> $DIR/mutable_references.rs:36:5
|
LL | *OH_YES = 99;
| ^^^^^^^^^^^^ cannot assign
@ -22,12 +22,12 @@ help: skipping check for `const_mut_refs` feature
LL | static BOO: &mut Foo<()> = &mut Foo(());
| ^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references.rs:27:8
--> $DIR/mutable_references.rs:26:8
|
LL | x: &UnsafeCell::new(42),
| ^^^^^^^^^^^^^^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_references.rs:31:27
--> $DIR/mutable_references.rs:30:27
|
LL | static OH_YES: &mut i32 = &mut 42;
| ^^^^^^^

View file

@ -0,0 +1,37 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
#![allow(const_err)]
use std::cell::UnsafeCell;
// this test ICEs to ensure that our mutability story is sound
struct Meh {
x: &'static UnsafeCell<i32>,
}
unsafe impl Sync for Meh {}
// the following will never be ok! no interior mut behind consts, because
// all allocs interned here will be marked immutable.
const MUH: Meh = Meh { //~ ERROR: mutable memory (`UnsafeCell`) is not allowed in constant
x: &UnsafeCell::new(42),
};
struct Synced {
x: UnsafeCell<i32>,
}
unsafe impl Sync for Synced {}
// Make sure we also catch this behind a type-erased `dyn Trait` reference.
const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
//~^ ERROR: mutable memory (`UnsafeCell`) is not allowed in constant
// Make sure we also catch mutable references.
const BLUNT: &mut i32 = &mut 42;
//~^ ERROR: mutable memory (`&mut`) is not allowed in constant
fn main() {
unsafe {
*MUH.x.get() = 99;
}
}

View file

@ -0,0 +1,40 @@
error: mutable memory (`UnsafeCell`) is not allowed in constant
--> $DIR/mutable_references_err.rs:16:1
|
LL | / const MUH: Meh = Meh {
LL | | x: &UnsafeCell::new(42),
LL | | };
| |__^
error: mutable memory (`UnsafeCell`) is not allowed in constant
--> $DIR/mutable_references_err.rs:26:1
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable memory (`&mut`) is not allowed in constant
--> $DIR/mutable_references_err.rs:30:1
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:17:8
|
LL | x: &UnsafeCell::new(42),
| ^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:26:27
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_references_err.rs:30:25
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^
error: aborting due to 3 previous errors; 1 warning emitted

View file

@ -1,29 +0,0 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
// failure-status: 101
// rustc-env:RUST_BACKTRACE=0
// normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET"
// normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS"
// normalize-stderr-test "interpret/intern.rs:[0-9]+:[0-9]+" -> "interpret/intern.rs:LL:CC"
#![allow(const_err)]
use std::cell::UnsafeCell;
// this test ICEs to ensure that our mutability story is sound
struct Meh {
x: &'static UnsafeCell<i32>,
}
unsafe impl Sync for Meh {}
// the following will never be ok!
const MUH: Meh = Meh {
x: &UnsafeCell::new(42),
};
fn main() {
unsafe {
*MUH.x.get() = 99;
}
}

View file

@ -1,25 +0,0 @@
thread 'rustc' panicked at 'assertion failed: `(left != right)`
left: `Const`,
right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites could observe that mutation.', src/librustc_mir/interpret/intern.rs:LL:CC
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: internal compiler error: unexpected panic
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: rustc VERSION running on TARGET
note: compiler flags: FLAGS
warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_ice.rs:22:8
|
LL | x: &UnsafeCell::new(42),
| ^^^^^^^^^^^^^^^^^^^^
warning: 1 warning emitted

View file

@ -0,0 +1,12 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
#![allow(const_err)]
use std::cell::UnsafeCell;
const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
//~^ ERROR: untyped pointers are not allowed in constant
fn main() {}

View file

@ -0,0 +1,16 @@
error: untyped pointers are not allowed in constant
--> $DIR/raw_mutable_const.rs:9:1
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/raw_mutable_const.rs:9:38
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted

View file

@ -0,0 +1,10 @@
#![allow(const_err)] // make sure we hit the `delay_span_bug`
// This is a regression test for a `delay_span_bug` during interning when a constant
// evaluates to a (non-dangling) raw pointer. For now this errors; potentially it
// could also be allowed.
const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;
//~^ ERROR untyped pointers are not allowed in constant
fn main() {}

View file

@ -0,0 +1,8 @@
error: untyped pointers are not allowed in constant
--> $DIR/raw-ptr-const.rs:7:1
|
LL | const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error