diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 8f06b9a69bd15abebdb7bb2599beaa42e743b079..f7871952452128b4dc78b173bf1fad043893a7dd 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -453,9 +453,6 @@ pub enum UnsupportedOpInfo { ReadForeignStatic(DefId), /// Could not find MIR for a function. NoMirFor(DefId), - /// Modified a static during const-eval. - /// FIXME: move this to `ConstEvalErrKind` through a machine hook. - ModifiedStatic, /// Encountered a pointer where we needed raw bytes. ReadPointerAsBytes, /// Encountered raw bytes where we needed a pointer. @@ -471,12 +468,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "tried to read from foreign (extern) static {:?}", did) } NoMirFor(did) => write!(f, "could not load MIR for {:?}", did), - ModifiedStatic => write!( - f, - "tried to modify a static's initial value from another static's \ - initializer" - ), - ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 75842fd554941fb8d4d5be581a75d6a1a7bd7f6b..1dc2f2a6f248b00ade6dd45ff2d9e49a66da59e1 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -984,6 +984,7 @@ pub struct GlobalCtxt<'tcx> { /// Stores the value of constants (and deduplicates the actual memory) allocation_interner: ShardedHashMap<&'tcx Allocation, ()>, + /// Stores memory for globals (statics/consts). pub alloc_map: Lock>, layout_interner: ShardedHashMap<&'tcx LayoutDetails, ()>, diff --git a/src/librustc_mir/const_eval/error.rs b/src/librustc_mir/const_eval/error.rs index dc23eba643e3e17a7c365206dbc7cae99ca63811..aa30f43df93500018eebc19b9fb7abb95303dc7d 100644 --- a/src/librustc_mir/const_eval/error.rs +++ b/src/librustc_mir/const_eval/error.rs @@ -12,6 +12,7 @@ pub enum ConstEvalErrKind { NeedsRfc(String), ConstAccessesStatic, + ModifiedGlobal, AssertFailure(AssertKind), Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, } @@ -33,6 +34,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) } ConstAccessesStatic => write!(f, "constant accesses static"), + ModifiedGlobal => { + write!(f, "modifying a static's initial value from another static's initializer") + } AssertFailure(ref msg) => write!(f, "{:?}", msg), Panic { msg, line, col, file } => { write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col) diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs index ab88a92ea7bc210bae9d1cd76f9bd61d7a4a9e8a..8f4501cc3fb69ace1fd213244a418c1bab5d4275 100644 --- a/src/librustc_mir/const_eval/machine.rs +++ b/src/librustc_mir/const_eval/machine.rs @@ -8,8 +8,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc::mir::AssertMessage; -use rustc_span::source_map::Span; +use rustc_ast::ast::Mutability; use rustc_span::symbol::Symbol; +use rustc_span::{def_id::DefId, Span}; use crate::interpret::{ self, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, OpTy, @@ -167,7 +168,7 @@ fn may_leak(self) -> bool { } impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { - type MemoryKinds = !; + type MemoryKind = !; type PointerTag = (); type ExtraFnVal = !; @@ -177,7 +178,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { type MemoryMap = FxHashMap, Allocation)>; - const STATIC_KIND: Option = None; // no copying of statics allowed + const GLOBAL_KIND: Option = None; // no copying of globals allowed // We do not check for alignment to avoid having to carry an `Align` // in `ConstValue::ByRef`. @@ -317,7 +318,7 @@ fn init_allocation_extra<'b>( } #[inline(always)] - fn tag_static_base_pointer(_memory_extra: &MemoryExtra, _id: AllocId) -> Self::PointerTag {} + fn tag_global_base_pointer(_memory_extra: &MemoryExtra, _id: AllocId) -> Self::PointerTag {} fn box_alloc( _ecx: &mut InterpCx<'mir, 'tcx, Self>, @@ -345,11 +346,19 @@ fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { Ok(()) } - fn before_access_static( + fn before_access_global( memory_extra: &MemoryExtra, - _allocation: &Allocation, + alloc_id: AllocId, + allocation: &Allocation, + def_id: Option, + is_write: bool, ) -> InterpResult<'tcx> { - if memory_extra.can_access_statics { + if is_write && allocation.mutability == Mutability::Not { + Err(err_ub!(WriteToReadOnly(alloc_id)).into()) + } else if is_write { + Err(ConstEvalErrKind::ModifiedGlobal.into()) + } else if memory_extra.can_access_statics || def_id.is_none() { + // `def_id.is_none()` indicates this is not a static, but a const or so. Ok(()) } else { Err(ConstEvalErrKind::ConstAccessesStatic.into()) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index ac593d0845a7df52051836a3379cc1181d84f888..c50146f295adb67e8517934a7408f619f2732e08 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -253,8 +253,8 @@ pub fn force_bits( /// This represents a *direct* access to that memory, as opposed to access /// through a pointer that was created by the program. #[inline(always)] - pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer { - self.memory.tag_static_base_pointer(ptr) + pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer { + self.memory.tag_global_base_pointer(ptr) } #[inline(always)] diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 90b8a4932991e2b44499c2a4105a257c4ecb1dba..b9ed69842f1a9b28beaffdedf30170b241f6b149 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -16,7 +16,7 @@ pub trait CompileTimeMachine<'mir, 'tcx> = Machine< 'mir, 'tcx, - MemoryKinds = !, + MemoryKind = !, PointerTag = (), ExtraFnVal = !, FrameExtra = (), @@ -104,7 +104,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {} } // Set allocation mutability as appropriate. This is used by LLVM to put things into - // read-only memory, and also by Miri when evluating other constants/statics that + // 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. diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 88cb74ebf8c98bca2b1874dd578d8bd0ca7000d3..cc87c2916862be2e88b414ae56b07a530f4c2482 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -7,7 +7,7 @@ use rustc::mir; use rustc::ty::{self, Ty}; -use rustc_span::Span; +use rustc_span::{def_id::DefId, Span}; use super::{ AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, @@ -79,7 +79,7 @@ fn get_mut(&mut self, k: K) -> Option<&mut V> { /// and some use case dependent behaviour can instead be applied. pub trait Machine<'mir, 'tcx>: Sized { /// Additional memory kinds a machine wishes to distinguish from the builtin ones - type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static; + type MemoryKind: ::std::fmt::Debug + MayLeak + Eq + 'static; /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" /// . @@ -105,16 +105,17 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Memory's allocation map type MemoryMap: AllocMap< AllocId, - (MemoryKind, Allocation), + (MemoryKind, Allocation), > + Default + Clone; - /// The memory kind to use for copied statics -- or None if statics should not be mutated - /// and thus any such attempt will cause a `ModifiedStatic` error to be raised. + /// The memory kind to use for copied global memory (held in `tcx`) -- + /// or None if such memory should not be mutated and thus any such attempt will cause + /// a `ModifiedStatic` error to be raised. /// Statics are copied under two circumstances: When they are mutated, and when - /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation + /// `tag_allocation` (see below) returns an owned allocation /// that is added to the memory so that the work is not done twice. - const STATIC_KIND: Option; + const GLOBAL_KIND: Option; /// Whether memory accesses should be alignment-checked. const CHECK_ALIGN: bool; @@ -207,11 +208,15 @@ fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx Ok(()) } - /// Called before a `Static` value is accessed. + /// Called before a global allocation is accessed. + /// `def_id` is `Some` if this is the "lazy" allocation of a static. #[inline] - fn before_access_static( + fn before_access_global( _memory_extra: &Self::MemoryExtra, + _alloc_id: AllocId, _allocation: &Allocation, + _def_id: Option, + _is_write: bool, ) -> InterpResult<'tcx> { Ok(()) } @@ -231,10 +236,10 @@ fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId { /// it contains (in relocations) tagged. The way we construct allocations is /// to always first construct it without extra and then add the extra. /// This keeps uniform code paths for handling both allocations created by CTFE - /// for statics, and allocations created by Miri during evaluation. + /// for globals, and allocations created by Miri during evaluation. /// /// `kind` is the kind of the allocation being tagged; it can be `None` when - /// it's a static and `STATIC_KIND` is `None`. + /// it's a global and `GLOBAL_KIND` is `None`. /// /// This should avoid copying if no work has to be done! If this returns an owned /// allocation (because a copy had to be done to add tags or metadata), machine memory will @@ -243,20 +248,20 @@ fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId { /// /// Also return the "base" tag to use for this allocation: the one that is used for direct /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent - /// with `tag_static_base_pointer`. + /// with `tag_global_base_pointer`. fn init_allocation_extra<'b>( memory_extra: &Self::MemoryExtra, id: AllocId, alloc: Cow<'b, Allocation>, - kind: Option>, + kind: Option>, ) -> (Cow<'b, Allocation>, Self::PointerTag); - /// Return the "base" tag for the given *static* allocation: the one that is used for direct - /// accesses to this static/const/fn allocation. If `id` is not a static allocation, + /// Return the "base" tag for the given *global* allocation: the one that is used for direct + /// accesses to this static/const/fn allocation. If `id` is not a global allocation, /// this will return an unusable tag (i.e., accesses will be UB)! /// /// Expects `id` to be already canonical, if needed. - fn tag_static_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; + fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; /// Executes a retagging operation #[inline] diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 277a77af3fd56efc5d448ef179bda2c2ef173cc2..110f2ffd9d78c8045affa8d9e6545e415d1785c3 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -80,12 +80,12 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// Allocations local to this instance of the miri engine. The kind /// helps ensure that the same mechanism is used for allocation and /// deallocation. When an allocation is not found here, it is a - /// static and looked up in the `tcx` for read access. Some machines may - /// have to mutate this map even on a read-only access to a static (because + /// global and looked up in the `tcx` for read access. Some machines may + /// have to mutate this map even on a read-only access to a global (because /// they do pointer provenance tracking and the allocations in `tcx` have /// the wrong type), so we let the machine override this type. - /// Either way, if the machine allows writing to a static, doing so will - /// create a copy of the static allocation here. + /// Either way, if the machine allows writing to a global, doing so will + /// create a copy of the global allocation here. // FIXME: this should not be public, but interning currently needs access to it pub(super) alloc_map: M::MemoryMap, @@ -130,9 +130,9 @@ pub fn new(tcx: TyCtxtAt<'tcx>, extra: M::MemoryExtra) -> Self { /// This represents a *direct* access to that memory, as opposed to access /// through a pointer that was created by the program. #[inline] - pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer { + pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer { let id = M::canonical_alloc_id(self, ptr.alloc_id); - ptr.with_tag(M::tag_static_base_pointer(&self.extra, id)) + ptr.with_tag(M::tag_global_base_pointer(&self.extra, id)) } pub fn create_fn_alloc( @@ -149,23 +149,23 @@ pub fn create_fn_alloc( id } }; - self.tag_static_base_pointer(Pointer::from(id)) + self.tag_global_base_pointer(Pointer::from(id)) } pub fn allocate( &mut self, size: Size, align: Align, - kind: MemoryKind, + kind: MemoryKind, ) -> Pointer { let alloc = Allocation::undef(size, align); self.allocate_with(alloc, kind) } - pub fn allocate_static_bytes( + pub fn allocate_bytes( &mut self, bytes: &[u8], - kind: MemoryKind, + kind: MemoryKind, ) -> Pointer { let alloc = Allocation::from_byte_aligned_bytes(bytes); self.allocate_with(alloc, kind) @@ -174,13 +174,13 @@ pub fn allocate_static_bytes( pub fn allocate_with( &mut self, alloc: Allocation, - kind: MemoryKind, + kind: MemoryKind, ) -> Pointer { let id = self.tcx.alloc_map.lock().reserve(); debug_assert_ne!( Some(kind), - M::STATIC_KIND.map(MemoryKind::Machine), - "dynamically allocating static memory" + M::GLOBAL_KIND.map(MemoryKind::Machine), + "dynamically allocating global memory" ); let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); @@ -193,7 +193,7 @@ pub fn reallocate( old_size_and_align: Option<(Size, Align)>, new_size: Size, new_align: Align, - kind: MemoryKind, + kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { if ptr.offset.bytes() != 0 { throw_ub_format!( @@ -215,9 +215,9 @@ pub fn reallocate( Ok(new_ptr) } - /// Deallocate a local, or do nothing if that local has been made into a static + /// Deallocate a local, or do nothing if that local has been made into a global. pub fn deallocate_local(&mut self, ptr: Pointer) -> InterpResult<'tcx> { - // The allocation might be already removed by static interning. + // The allocation might be already removed by global interning. // This can only really happen in the CTFE instance, not in miri. if self.alloc_map.contains_key(&ptr.alloc_id) { self.deallocate(ptr, None, MemoryKind::Stack) @@ -230,7 +230,7 @@ pub fn deallocate( &mut self, ptr: Pointer, old_size_and_align: Option<(Size, Align)>, - kind: MemoryKind, + kind: MemoryKind, ) -> InterpResult<'tcx> { trace!("deallocating: {}", ptr.alloc_id); @@ -244,7 +244,7 @@ pub fn deallocate( let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { Some(alloc) => alloc, None => { - // Deallocating static memory -- always an error + // Deallocating global memory -- always an error return Err(match self.tcx.alloc_map.lock().get(ptr.alloc_id) { Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"), Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => { @@ -403,43 +403,45 @@ pub fn ptr_may_be_null(&self, ptr: Pointer) -> bool { /// Allocation accessors impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { - /// Helper function to obtain the global (tcx) allocation for a static. + /// Helper function to obtain a global (tcx) allocation. /// This attempts to return a reference to an existing allocation if /// one can be found in `tcx`. That, however, is only possible if `tcx` and /// this machine use the same pointer tag, so it is indirected through /// `M::tag_allocation`. - /// - /// Notice that every static has two `AllocId` that will resolve to the same - /// thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, - /// and the other one is maps to `GlobalAlloc::Memory`, this is returned by - /// `const_eval_raw` and it is the "resolved" ID. - /// The resolved ID is never used by the interpreted progrma, it is hidden. - /// The `GlobalAlloc::Memory` branch here is still reachable though; when a static - /// contains a reference to memory that was created during its evaluation (i.e., not to - /// another static), those inner references only exist in "resolved" form. - /// - /// Assumes `id` is already canonical. - fn get_static_alloc( + fn get_global_alloc( memory_extra: &M::MemoryExtra, tcx: TyCtxtAt<'tcx>, id: AllocId, + is_write: bool, ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> { let alloc = tcx.alloc_map.lock().get(id); - let alloc = match alloc { - Some(GlobalAlloc::Memory(mem)) => Cow::Borrowed(mem), + let (alloc, def_id) = match alloc { + Some(GlobalAlloc::Memory(mem)) => { + // Memory of a constant or promoted or anonymous memory referenced by a static. + (mem, None) + } Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { - // We got a "lazy" static that has not been computed yet. + // Notice that every static has two `AllocId` that will resolve to the same + // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, + // and the other one is maps to `GlobalAlloc::Memory`, this is returned by + // `const_eval_raw` and it is the "resolved" ID. + // The resolved ID is never used by the interpreted progrma, it is hidden. + // The `GlobalAlloc::Memory` branch here is still reachable though; when a static + // contains a reference to memory that was created during its evaluation (i.e., not + // to another static), those inner references only exist in "resolved" form. + // + // Assumes `id` is already canonical. if tcx.is_foreign_item(def_id) { - trace!("get_static_alloc: foreign item {:?}", def_id); + trace!("get_global_alloc: foreign item {:?}", def_id); throw_unsup!(ReadForeignStatic(def_id)) } - trace!("get_static_alloc: Need to compute {:?}", def_id); + trace!("get_global_alloc: Need to compute {:?}", def_id); let instance = Instance::mono(tcx.tcx, def_id); let gid = GlobalId { instance, promoted: None }; - // use the raw query here to break validation cycles. Later uses of the static - // will call the full query anyway + // Use the raw query here to break validation cycles. Later uses of the static + // will call the full query anyway. let raw_const = tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| { // no need to report anything, the const_eval call takes care of that @@ -454,18 +456,19 @@ fn get_static_alloc( let id = raw_const.alloc_id; let allocation = tcx.alloc_map.lock().unwrap_memory(id); - M::before_access_static(memory_extra, allocation)?; - Cow::Borrowed(allocation) + (allocation, Some(def_id)) } }; + M::before_access_global(memory_extra, id, alloc, def_id, is_write)?; + let alloc = Cow::Borrowed(alloc); // We got tcx memory. Let the machine initialize its "extra" stuff. let (alloc, tag) = M::init_allocation_extra( memory_extra, id, // always use the ID we got as input, not the "hidden" one. alloc, - M::STATIC_KIND.map(MemoryKind::Machine), + M::GLOBAL_KIND.map(MemoryKind::Machine), ); - debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id)); + debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id)); Ok(alloc) } @@ -478,10 +481,11 @@ pub fn get_raw( let id = M::canonical_alloc_id(self, id); // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from - // `get_static_alloc` that we can actually use directly without inserting anything anywhere. + // `get_global_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `InterpResult<'tcx, &Allocation>`. let a = self.alloc_map.get_or(id, || { - let alloc = Self::get_static_alloc(&self.extra, self.tcx, id).map_err(Err)?; + let alloc = Self::get_global_alloc(&self.extra, self.tcx, id, /*is_write*/ false) + .map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { // We got a ref, cheaply return that as an "error" so that the @@ -490,8 +494,8 @@ pub fn get_raw( } Cow::Owned(alloc) => { // Need to put it into the map and return a ref to that - let kind = M::STATIC_KIND.expect( - "I got an owned allocation that I have to copy but the machine does \ + let kind = M::GLOBAL_KIND.expect( + "I got a global allocation that I have to copy but the machine does \ not expect that to happen", ); Ok((MemoryKind::Machine(kind), alloc)) @@ -515,16 +519,17 @@ pub fn get_raw_mut( let tcx = self.tcx; let memory_extra = &self.extra; let a = self.alloc_map.get_mut_or(id, || { - // Need to make a copy, even if `get_static_alloc` is able + // Need to make a copy, even if `get_global_alloc` is able // to give us a cheap reference. - let alloc = Self::get_static_alloc(memory_extra, tcx, id)?; + let alloc = Self::get_global_alloc(memory_extra, tcx, id, /*is_write*/ true)?; if alloc.mutability == Mutability::Not { throw_ub!(WriteToReadOnly(id)) } - match M::STATIC_KIND { - Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())), - None => throw_unsup!(ModifiedStatic), - } + let kind = M::GLOBAL_KIND.expect( + "I got a global allocation that I have to copy but the machine does \ + not expect that to happen", + ); + Ok((MemoryKind::Machine(kind), alloc.into_owned())) }); // Unpack the error type manually because type inference doesn't // work otherwise (and we cannot help it because `impl Trait`) @@ -553,7 +558,7 @@ pub fn get_size_and_align( // # Regular allocations // Don't use `self.get_raw` here as that will // a) cause cycles in case `id` refers to a static - // b) duplicate a static's allocation in miri + // b) duplicate a global's allocation in miri if let Some((_, alloc)) = self.alloc_map.get(id) { return Ok((alloc.size, alloc.align)); } @@ -728,7 +733,7 @@ pub fn dump_allocs(&self, mut allocs: Vec) { ); } Err(()) => { - // static alloc? + // global alloc? match self.tcx.alloc_map.lock().get(id) { Some(GlobalAlloc::Memory(alloc)) => { self.dump_alloc_helper( diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4c82172ae4517cb1958bdd4254c978f0e2c7381e..90fb7eb2bb3ac0cdf60f0c35b4a41c8d620dd5e9 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -517,7 +517,7 @@ pub(super) fn eval_operands( layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let tag_scalar = |scalar| match scalar { - Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_static_base_pointer(ptr)), + Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_global_base_pointer(ptr)), Scalar::Raw { data, size } => Scalar::Raw { data, size }, }; // Early-return cases. @@ -547,7 +547,7 @@ pub(super) fn eval_operands( let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc); // We rely on mutability being set correctly in that allocation to prevent writes // where none should happen. - let ptr = self.tag_static_base_pointer(Pointer::new(id, offset)); + let ptr = self.tag_global_base_pointer(Pointer::new(id, offset)); Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi)) } ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x).into()), @@ -559,7 +559,7 @@ pub(super) fn eval_operands( Size::from_bytes(start as u64), // offset: `start` ); Operand::Immediate(Immediate::new_slice( - self.tag_static_base_pointer(ptr).into(), + self.tag_global_base_pointer(ptr).into(), (end - start) as u64, // len: `end - start` self, )) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 027e33abc7bb193b19909b964eb9dea33da2c695..6cf11c071e4f7edc767faaedd9bf0a13677a71b9 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -290,7 +290,7 @@ impl<'mir, 'tcx, Tag, M> InterpCx<'mir, 'tcx, M> Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, M: Machine<'mir, 'tcx, PointerTag = Tag>, // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 - M::MemoryMap: AllocMap, Allocation)>, + M::MemoryMap: AllocMap, Allocation)>, M::AllocExtra: AllocationExtra, { /// Take a value, which represents a (thin or wide) reference, and make it a place. @@ -1015,7 +1015,7 @@ pub fn force_allocation( pub fn allocate( &mut self, layout: TyLayout<'tcx>, - kind: MemoryKind, + kind: MemoryKind, ) -> MPlaceTy<'tcx, M::PointerTag> { let ptr = self.memory.allocate(layout.size, layout.align.abi, kind); MPlaceTy::from_aligned_ptr(ptr, layout) @@ -1025,9 +1025,9 @@ pub fn allocate( pub fn allocate_str( &mut self, str: &str, - kind: MemoryKind, + kind: MemoryKind, ) -> MPlaceTy<'tcx, M::PointerTag> { - let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind); + let ptr = self.memory.allocate_bytes(str.as_bytes(), kind); let meta = Scalar::from_uint(str.len() as u128, self.pointer_size()); let mplace = MemPlace { ptr: ptr.into(), @@ -1118,7 +1118,7 @@ pub fn raw_const_to_mplace( ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { // This must be an allocation in `tcx` assert!(self.tcx.alloc_map.lock().get(raw.alloc_id).is_some()); - let ptr = self.tag_static_base_pointer(Pointer::from(raw.alloc_id)); + let ptr = self.tag_global_base_pointer(Pointer::from(raw.alloc_id)); let layout = self.layout_of(raw.ty)?; Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 548c637efb260435f07855f64f33735f9c3e32f3..cbb79637076bc3b31bcfcf3f4daef64a2eb86fc3 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -25,7 +25,7 @@ use rustc_hir::HirId; use rustc_index::vec::IndexVec; use rustc_session::lint; -use rustc_span::Span; +use rustc_span::{def_id::DefId, Span}; use rustc_trait_selection::traits; use crate::const_eval::error_to_const_error; @@ -162,7 +162,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAn struct ConstPropMachine; impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { - type MemoryKinds = !; + type MemoryKind = !; type PointerTag = (); type ExtraFnVal = !; @@ -172,7 +172,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { type MemoryMap = FxHashMap, Allocation)>; - const STATIC_KIND: Option = None; + const GLOBAL_KIND: Option = None; const CHECK_ALIGN: bool = false; @@ -247,7 +247,7 @@ fn init_allocation_extra<'b>( } #[inline(always)] - fn tag_static_base_pointer(_memory_extra: &(), _id: AllocId) -> Self::PointerTag {} + fn tag_global_base_pointer(_memory_extra: &(), _id: AllocId) -> Self::PointerTag {} fn box_alloc( _ecx: &mut InterpCx<'mir, 'tcx, Self>, @@ -270,14 +270,23 @@ fn access_local( l.access() } - fn before_access_static( + fn before_access_global( _memory_extra: &(), + _alloc_id: AllocId, allocation: &Allocation, + def_id: Option, + is_write: bool, ) -> InterpResult<'tcx> { - // if the static allocation is mutable or if it has relocations (it may be legal to mutate - // the memory behind that in the future), then we can't const prop it - if allocation.mutability == Mutability::Mut || allocation.relocations().len() > 0 { - throw_machine_stop_str!("can't eval mutable statics in ConstProp") + if is_write { + throw_machine_stop_str!("can't write to global"); + } + // If the static allocation is mutable or if it has relocations (it may be legal to mutate + // the memory behind that in the future), then we can't const prop it. + if allocation.mutability == Mutability::Mut { + throw_machine_stop_str!("can't eval mutable globals in ConstProp"); + } + if def_id.is_some() && allocation.relocations().len() > 0 { + throw_machine_stop_str!("can't eval statics with pointers in ConstProp"); } Ok(()) diff --git a/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr b/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr index cb4d35b9a18095e96cecc8b9e68da0d8658c8a95..bf5e476d80045711821a4f014efab4ed776bb231 100644 --- a/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr +++ b/src/test/ui/consts/const-eval/assign-to-static-within-other-static.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/assign-to-static-within-other-static.rs:10:5 | LL | FOO = 5; - | ^^^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^^^ modifying a static's initial value from another static's initializer error: aborting due to previous error diff --git a/src/test/ui/consts/miri_unleashed/mutating_global.rs b/src/test/ui/consts/miri_unleashed/mutating_global.rs new file mode 100644 index 0000000000000000000000000000000000000000..acc6fb026cd696352e91f36eea106f3612343070 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutating_global.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you + +// Make sure we cannot mutate globals. + +static mut GLOBAL: i32 = 0; + +const MUTATING_GLOBAL: () = { + unsafe { + GLOBAL = 99 //~ ERROR any use of this value will cause an error + //~^ WARN skipping const checks + //~| WARN skipping const checks + } +}; + +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/mutating_global.stderr b/src/test/ui/consts/miri_unleashed/mutating_global.stderr new file mode 100644 index 0000000000000000000000000000000000000000..4e67d2c0fb85e88721f290c0c482c20b39004f0c --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutating_global.stderr @@ -0,0 +1,29 @@ +warning: skipping const checks + --> $DIR/mutating_global.rs:9:9 + | +LL | GLOBAL = 99 + | ^^^^^^ + +warning: skipping const checks + --> $DIR/mutating_global.rs:9:9 + | +LL | GLOBAL = 99 + | ^^^^^^ + +error: any use of this value will cause an error + --> $DIR/mutating_global.rs:9:9 + | +LL | / const MUTATING_GLOBAL: () = { +LL | | unsafe { +LL | | GLOBAL = 99 + | | ^^^^^^^^^^^ modifying a static's initial value from another static's initializer +LL | | +LL | | +LL | | } +LL | | }; + | |__- + | + = note: `#[deny(const_err)]` on by default + +error: aborting due to previous error + diff --git a/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr b/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr index b43fbc86f99f2c3e6f47107c3f33dcf6c0b4ba5c..8db75dd63cf2a0e5357f7e65bf3ee69bd4d52a98 100644 --- a/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr +++ b/src/test/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/static_mut_containing_mut_ref2.rs:7:45 | LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer error: aborting due to previous error diff --git a/src/test/ui/consts/static_mut_containing_mut_ref3.stderr b/src/test/ui/consts/static_mut_containing_mut_ref3.stderr index e88e49b097af26b7d0cbe13936c58de0a7153b5e..91f9dbd8d0b9efc8b1e385eebc9b604455af2daf 100644 --- a/src/test/ui/consts/static_mut_containing_mut_ref3.stderr +++ b/src/test/ui/consts/static_mut_containing_mut_ref3.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/static_mut_containing_mut_ref3.rs:3:31 | LL | static mut BAR: () = unsafe { FOO.0 = 99; }; - | ^^^^^^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^^^^^^ modifying a static's initial value from another static's initializer error: aborting due to previous error diff --git a/src/test/ui/write-to-static-mut-in-static.stderr b/src/test/ui/write-to-static-mut-in-static.stderr index 4349f6e89c1195e26537a70125c1b2a7934585a5..6c2bd13d433ad7f99ad93a8f72ca141130d79037 100644 --- a/src/test/ui/write-to-static-mut-in-static.stderr +++ b/src/test/ui/write-to-static-mut-in-static.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/write-to-static-mut-in-static.rs:2:33 | LL | pub static mut B: () = unsafe { A = 1; }; - | ^^^^^ tried to modify a static's initial value from another static's initializer + | ^^^^^ modifying a static's initial value from another static's initializer error[E0391]: cycle detected when const-evaluating `C` --> $DIR/write-to-static-mut-in-static.rs:5:34