未验证 提交 b8093bad 编写于 作者: M Mazdak Farrokhzad 提交者: GitHub

Rollup merge of #70241 - RalfJung:global-mem, r=oli-obk

Miri: move ModifiedStatic to ConstEval errors

Also generally adjust terminology from "static" to "global" where appropriate (to avoid confusion with specifically `static` items).
...@@ -453,9 +453,6 @@ pub enum UnsupportedOpInfo { ...@@ -453,9 +453,6 @@ pub enum UnsupportedOpInfo {
ReadForeignStatic(DefId), ReadForeignStatic(DefId),
/// Could not find MIR for a function. /// Could not find MIR for a function.
NoMirFor(DefId), 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. /// Encountered a pointer where we needed raw bytes.
ReadPointerAsBytes, ReadPointerAsBytes,
/// Encountered raw bytes where we needed a pointer. /// Encountered raw bytes where we needed a pointer.
...@@ -471,12 +468,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ...@@ -471,12 +468,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "tried to read from foreign (extern) static {:?}", did) write!(f, "tried to read from foreign (extern) static {:?}", did)
} }
NoMirFor(did) => write!(f, "could not load MIR for {:?}", 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",), ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
} }
......
...@@ -984,6 +984,7 @@ pub struct GlobalCtxt<'tcx> { ...@@ -984,6 +984,7 @@ pub struct GlobalCtxt<'tcx> {
/// Stores the value of constants (and deduplicates the actual memory) /// Stores the value of constants (and deduplicates the actual memory)
allocation_interner: ShardedHashMap<&'tcx Allocation, ()>, allocation_interner: ShardedHashMap<&'tcx Allocation, ()>,
/// Stores memory for globals (statics/consts).
pub alloc_map: Lock<interpret::AllocMap<'tcx>>, pub alloc_map: Lock<interpret::AllocMap<'tcx>>,
layout_interner: ShardedHashMap<&'tcx LayoutDetails, ()>, layout_interner: ShardedHashMap<&'tcx LayoutDetails, ()>,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
pub enum ConstEvalErrKind { pub enum ConstEvalErrKind {
NeedsRfc(String), NeedsRfc(String),
ConstAccessesStatic, ConstAccessesStatic,
ModifiedGlobal,
AssertFailure(AssertKind<u64>), AssertFailure(AssertKind<u64>),
Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
} }
...@@ -33,6 +34,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ...@@ -33,6 +34,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
} }
ConstAccessesStatic => write!(f, "constant accesses static"), 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), AssertFailure(ref msg) => write!(f, "{:?}", msg),
Panic { msg, line, col, file } => { Panic { msg, line, col, file } => {
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col) write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
......
...@@ -8,8 +8,9 @@ ...@@ -8,8 +8,9 @@
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc::mir::AssertMessage; use rustc::mir::AssertMessage;
use rustc_span::source_map::Span; use rustc_ast::ast::Mutability;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_span::{def_id::DefId, Span};
use crate::interpret::{ use crate::interpret::{
self, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, OpTy, self, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, OpTy,
...@@ -167,7 +168,7 @@ fn may_leak(self) -> bool { ...@@ -167,7 +168,7 @@ fn may_leak(self) -> bool {
} }
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
type MemoryKinds = !; type MemoryKind = !;
type PointerTag = (); type PointerTag = ();
type ExtraFnVal = !; type ExtraFnVal = !;
...@@ -177,7 +178,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter { ...@@ -177,7 +178,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>; type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, 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` // We do not check for alignment to avoid having to carry an `Align`
// in `ConstValue::ByRef`. // in `ConstValue::ByRef`.
...@@ -317,7 +318,7 @@ fn init_allocation_extra<'b>( ...@@ -317,7 +318,7 @@ fn init_allocation_extra<'b>(
} }
#[inline(always)] #[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( fn box_alloc(
_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ecx: &mut InterpCx<'mir, 'tcx, Self>,
...@@ -345,11 +346,19 @@ fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { ...@@ -345,11 +346,19 @@ fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
Ok(()) Ok(())
} }
fn before_access_static( fn before_access_global(
memory_extra: &MemoryExtra, memory_extra: &MemoryExtra,
_allocation: &Allocation, alloc_id: AllocId,
allocation: &Allocation,
def_id: Option<DefId>,
is_write: bool,
) -> InterpResult<'tcx> { ) -> 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(()) Ok(())
} else { } else {
Err(ConstEvalErrKind::ConstAccessesStatic.into()) Err(ConstEvalErrKind::ConstAccessesStatic.into())
......
...@@ -253,8 +253,8 @@ pub fn force_bits( ...@@ -253,8 +253,8 @@ pub fn force_bits(
/// This represents a *direct* access to that memory, as opposed to access /// This represents a *direct* access to that memory, as opposed to access
/// through a pointer that was created by the program. /// through a pointer that was created by the program.
#[inline(always)] #[inline(always)]
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> { pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
self.memory.tag_static_base_pointer(ptr) self.memory.tag_global_base_pointer(ptr)
} }
#[inline(always)] #[inline(always)]
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
pub trait CompileTimeMachine<'mir, 'tcx> = Machine< pub trait CompileTimeMachine<'mir, 'tcx> = Machine<
'mir, 'mir,
'tcx, 'tcx,
MemoryKinds = !, MemoryKind = !,
PointerTag = (), PointerTag = (),
ExtraFnVal = !, ExtraFnVal = !,
FrameExtra = (), FrameExtra = (),
...@@ -104,7 +104,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( ...@@ -104,7 +104,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {} MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {}
} }
// Set allocation mutability as appropriate. This is used by LLVM to put things into // 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. // access this one.
if mode == InternMode::Static { if mode == InternMode::Static {
// When `ty` is `None`, we assume no interior mutability. // When `ty` is `None`, we assume no interior mutability.
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
use rustc::mir; use rustc::mir;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc_span::Span; use rustc_span::{def_id::DefId, Span};
use super::{ use super::{
AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind,
...@@ -79,7 +79,7 @@ fn get_mut(&mut self, k: K) -> Option<&mut V> { ...@@ -79,7 +79,7 @@ fn get_mut(&mut self, k: K) -> Option<&mut V> {
/// and some use case dependent behaviour can instead be applied. /// and some use case dependent behaviour can instead be applied.
pub trait Machine<'mir, 'tcx>: Sized { pub trait Machine<'mir, 'tcx>: Sized {
/// Additional memory kinds a machine wishes to distinguish from the builtin ones /// 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" /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>. /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
...@@ -105,16 +105,17 @@ pub trait Machine<'mir, 'tcx>: Sized { ...@@ -105,16 +105,17 @@ pub trait Machine<'mir, 'tcx>: Sized {
/// Memory's allocation map /// Memory's allocation map
type MemoryMap: AllocMap< type MemoryMap: AllocMap<
AllocId, AllocId,
(MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>), (MemoryKind<Self::MemoryKind>, Allocation<Self::PointerTag, Self::AllocExtra>),
> + Default > + Default
+ Clone; + Clone;
/// The memory kind to use for copied statics -- or None if statics should not be mutated /// The memory kind to use for copied global memory (held in `tcx`) --
/// and thus any such attempt will cause a `ModifiedStatic` error to be raised. /// 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 /// 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. /// that is added to the memory so that the work is not done twice.
const STATIC_KIND: Option<Self::MemoryKinds>; const GLOBAL_KIND: Option<Self::MemoryKind>;
/// Whether memory accesses should be alignment-checked. /// Whether memory accesses should be alignment-checked.
const CHECK_ALIGN: bool; const CHECK_ALIGN: bool;
...@@ -207,11 +208,15 @@ fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx ...@@ -207,11 +208,15 @@ fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx
Ok(()) 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] #[inline]
fn before_access_static( fn before_access_global(
_memory_extra: &Self::MemoryExtra, _memory_extra: &Self::MemoryExtra,
_alloc_id: AllocId,
_allocation: &Allocation, _allocation: &Allocation,
_def_id: Option<DefId>,
_is_write: bool,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
Ok(()) Ok(())
} }
...@@ -231,10 +236,10 @@ fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId { ...@@ -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 /// it contains (in relocations) tagged. The way we construct allocations is
/// to always first construct it without extra and then add the extra. /// to always first construct it without extra and then add the extra.
/// This keeps uniform code paths for handling both allocations created by CTFE /// 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 /// `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 /// 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 /// 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 { ...@@ -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 /// 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 /// 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>( fn init_allocation_extra<'b>(
memory_extra: &Self::MemoryExtra, memory_extra: &Self::MemoryExtra,
id: AllocId, id: AllocId,
alloc: Cow<'b, Allocation>, alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind<Self::MemoryKinds>>, kind: Option<MemoryKind<Self::MemoryKind>>,
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag); ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
/// Return the "base" tag for the given *static* allocation: the one that is used for direct /// 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 static allocation, /// 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)! /// this will return an unusable tag (i.e., accesses will be UB)!
/// ///
/// Expects `id` to be already canonical, if needed. /// 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 /// Executes a retagging operation
#[inline] #[inline]
......
...@@ -80,12 +80,12 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { ...@@ -80,12 +80,12 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
/// Allocations local to this instance of the miri engine. The kind /// Allocations local to this instance of the miri engine. The kind
/// helps ensure that the same mechanism is used for allocation and /// helps ensure that the same mechanism is used for allocation and
/// deallocation. When an allocation is not found here, it is a /// deallocation. When an allocation is not found here, it is a
/// static and looked up in the `tcx` for read access. Some machines may /// 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 static (because /// 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 /// they do pointer provenance tracking and the allocations in `tcx` have
/// the wrong type), so we let the machine override this type. /// the wrong type), so we let the machine override this type.
/// Either way, if the machine allows writing to a static, doing so will /// Either way, if the machine allows writing to a global, doing so will
/// create a copy of the static allocation here. /// create a copy of the global allocation here.
// FIXME: this should not be public, but interning currently needs access to it // FIXME: this should not be public, but interning currently needs access to it
pub(super) alloc_map: M::MemoryMap, pub(super) alloc_map: M::MemoryMap,
...@@ -130,9 +130,9 @@ pub fn new(tcx: TyCtxtAt<'tcx>, extra: M::MemoryExtra) -> Self { ...@@ -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 /// This represents a *direct* access to that memory, as opposed to access
/// through a pointer that was created by the program. /// through a pointer that was created by the program.
#[inline] #[inline]
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> { pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
let id = M::canonical_alloc_id(self, ptr.alloc_id); 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( pub fn create_fn_alloc(
...@@ -149,23 +149,23 @@ pub fn create_fn_alloc( ...@@ -149,23 +149,23 @@ pub fn create_fn_alloc(
id id
} }
}; };
self.tag_static_base_pointer(Pointer::from(id)) self.tag_global_base_pointer(Pointer::from(id))
} }
pub fn allocate( pub fn allocate(
&mut self, &mut self,
size: Size, size: Size,
align: Align, align: Align,
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> Pointer<M::PointerTag> { ) -> Pointer<M::PointerTag> {
let alloc = Allocation::undef(size, align); let alloc = Allocation::undef(size, align);
self.allocate_with(alloc, kind) self.allocate_with(alloc, kind)
} }
pub fn allocate_static_bytes( pub fn allocate_bytes(
&mut self, &mut self,
bytes: &[u8], bytes: &[u8],
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> Pointer<M::PointerTag> { ) -> Pointer<M::PointerTag> {
let alloc = Allocation::from_byte_aligned_bytes(bytes); let alloc = Allocation::from_byte_aligned_bytes(bytes);
self.allocate_with(alloc, kind) self.allocate_with(alloc, kind)
...@@ -174,13 +174,13 @@ pub fn allocate_static_bytes( ...@@ -174,13 +174,13 @@ pub fn allocate_static_bytes(
pub fn allocate_with( pub fn allocate_with(
&mut self, &mut self,
alloc: Allocation, alloc: Allocation,
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> Pointer<M::PointerTag> { ) -> Pointer<M::PointerTag> {
let id = self.tcx.alloc_map.lock().reserve(); let id = self.tcx.alloc_map.lock().reserve();
debug_assert_ne!( debug_assert_ne!(
Some(kind), Some(kind),
M::STATIC_KIND.map(MemoryKind::Machine), M::GLOBAL_KIND.map(MemoryKind::Machine),
"dynamically allocating static memory" "dynamically allocating global memory"
); );
let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind)); let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
self.alloc_map.insert(id, (kind, alloc.into_owned())); self.alloc_map.insert(id, (kind, alloc.into_owned()));
...@@ -193,7 +193,7 @@ pub fn reallocate( ...@@ -193,7 +193,7 @@ pub fn reallocate(
old_size_and_align: Option<(Size, Align)>, old_size_and_align: Option<(Size, Align)>,
new_size: Size, new_size: Size,
new_align: Align, new_align: Align,
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx, Pointer<M::PointerTag>> { ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
if ptr.offset.bytes() != 0 { if ptr.offset.bytes() != 0 {
throw_ub_format!( throw_ub_format!(
...@@ -215,9 +215,9 @@ pub fn reallocate( ...@@ -215,9 +215,9 @@ pub fn reallocate(
Ok(new_ptr) 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<M::PointerTag>) -> InterpResult<'tcx> { pub fn deallocate_local(&mut self, ptr: Pointer<M::PointerTag>) -> 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. // This can only really happen in the CTFE instance, not in miri.
if self.alloc_map.contains_key(&ptr.alloc_id) { if self.alloc_map.contains_key(&ptr.alloc_id) {
self.deallocate(ptr, None, MemoryKind::Stack) self.deallocate(ptr, None, MemoryKind::Stack)
...@@ -230,7 +230,7 @@ pub fn deallocate( ...@@ -230,7 +230,7 @@ pub fn deallocate(
&mut self, &mut self,
ptr: Pointer<M::PointerTag>, ptr: Pointer<M::PointerTag>,
old_size_and_align: Option<(Size, Align)>, old_size_and_align: Option<(Size, Align)>,
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
trace!("deallocating: {}", ptr.alloc_id); trace!("deallocating: {}", ptr.alloc_id);
...@@ -244,7 +244,7 @@ pub fn deallocate( ...@@ -244,7 +244,7 @@ pub fn deallocate(
let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
Some(alloc) => alloc, Some(alloc) => alloc,
None => { 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) { return Err(match self.tcx.alloc_map.lock().get(ptr.alloc_id) {
Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"), Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"),
Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => { Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => {
...@@ -403,43 +403,45 @@ pub fn ptr_may_be_null(&self, ptr: Pointer<M::PointerTag>) -> bool { ...@@ -403,43 +403,45 @@ pub fn ptr_may_be_null(&self, ptr: Pointer<M::PointerTag>) -> bool {
/// Allocation accessors /// Allocation accessors
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { 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 /// 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 /// 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 /// this machine use the same pointer tag, so it is indirected through
/// `M::tag_allocation`. /// `M::tag_allocation`.
/// fn get_global_alloc(
/// 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(
memory_extra: &M::MemoryExtra, memory_extra: &M::MemoryExtra,
tcx: TyCtxtAt<'tcx>, tcx: TyCtxtAt<'tcx>,
id: AllocId, id: AllocId,
is_write: bool,
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> { ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
let alloc = tcx.alloc_map.lock().get(id); let alloc = tcx.alloc_map.lock().get(id);
let alloc = match alloc { let (alloc, def_id) = match alloc {
Some(GlobalAlloc::Memory(mem)) => Cow::Borrowed(mem), 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)), Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
None => throw_ub!(PointerUseAfterFree(id)), None => throw_ub!(PointerUseAfterFree(id)),
Some(GlobalAlloc::Static(def_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) { 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)) 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 instance = Instance::mono(tcx.tcx, def_id);
let gid = GlobalId { instance, promoted: None }; let gid = GlobalId { instance, promoted: None };
// use the raw query here to break validation cycles. Later uses of the static // Use the raw query here to break validation cycles. Later uses of the static
// will call the full query anyway // will call the full query anyway.
let raw_const = let raw_const =
tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| { 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 // no need to report anything, the const_eval call takes care of that
...@@ -454,18 +456,19 @@ fn get_static_alloc( ...@@ -454,18 +456,19 @@ fn get_static_alloc(
let id = raw_const.alloc_id; let id = raw_const.alloc_id;
let allocation = tcx.alloc_map.lock().unwrap_memory(id); let allocation = tcx.alloc_map.lock().unwrap_memory(id);
M::before_access_static(memory_extra, allocation)?; (allocation, Some(def_id))
Cow::Borrowed(allocation)
} }
}; };
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. // We got tcx memory. Let the machine initialize its "extra" stuff.
let (alloc, tag) = M::init_allocation_extra( let (alloc, tag) = M::init_allocation_extra(
memory_extra, memory_extra,
id, // always use the ID we got as input, not the "hidden" one. id, // always use the ID we got as input, not the "hidden" one.
alloc, 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) Ok(alloc)
} }
...@@ -478,10 +481,11 @@ pub fn get_raw( ...@@ -478,10 +481,11 @@ pub fn get_raw(
let id = M::canonical_alloc_id(self, id); let id = M::canonical_alloc_id(self, id);
// The error type of the inner closure here is somewhat funny. We have two // 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 // 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<M::PointerTag>>`. // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`.
let a = self.alloc_map.get_or(id, || { 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 { match alloc {
Cow::Borrowed(alloc) => { Cow::Borrowed(alloc) => {
// We got a ref, cheaply return that as an "error" so that the // We got a ref, cheaply return that as an "error" so that the
...@@ -490,8 +494,8 @@ pub fn get_raw( ...@@ -490,8 +494,8 @@ pub fn get_raw(
} }
Cow::Owned(alloc) => { Cow::Owned(alloc) => {
// Need to put it into the map and return a ref to that // Need to put it into the map and return a ref to that
let kind = M::STATIC_KIND.expect( let kind = M::GLOBAL_KIND.expect(
"I got an owned allocation that I have to copy but the machine does \ "I got a global allocation that I have to copy but the machine does \
not expect that to happen", not expect that to happen",
); );
Ok((MemoryKind::Machine(kind), alloc)) Ok((MemoryKind::Machine(kind), alloc))
...@@ -515,16 +519,17 @@ pub fn get_raw_mut( ...@@ -515,16 +519,17 @@ pub fn get_raw_mut(
let tcx = self.tcx; let tcx = self.tcx;
let memory_extra = &self.extra; let memory_extra = &self.extra;
let a = self.alloc_map.get_mut_or(id, || { 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. // 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 { if alloc.mutability == Mutability::Not {
throw_ub!(WriteToReadOnly(id)) throw_ub!(WriteToReadOnly(id))
} }
match M::STATIC_KIND { let kind = M::GLOBAL_KIND.expect(
Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())), "I got a global allocation that I have to copy but the machine does \
None => throw_unsup!(ModifiedStatic), not expect that to happen",
} );
Ok((MemoryKind::Machine(kind), alloc.into_owned()))
}); });
// Unpack the error type manually because type inference doesn't // Unpack the error type manually because type inference doesn't
// work otherwise (and we cannot help it because `impl Trait`) // work otherwise (and we cannot help it because `impl Trait`)
...@@ -553,7 +558,7 @@ pub fn get_size_and_align( ...@@ -553,7 +558,7 @@ pub fn get_size_and_align(
// # Regular allocations // # Regular allocations
// Don't use `self.get_raw` here as that will // Don't use `self.get_raw` here as that will
// a) cause cycles in case `id` refers to a static // 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) { if let Some((_, alloc)) = self.alloc_map.get(id) {
return Ok((alloc.size, alloc.align)); return Ok((alloc.size, alloc.align));
} }
...@@ -728,7 +733,7 @@ pub fn dump_allocs(&self, mut allocs: Vec<AllocId>) { ...@@ -728,7 +733,7 @@ pub fn dump_allocs(&self, mut allocs: Vec<AllocId>) {
); );
} }
Err(()) => { Err(()) => {
// static alloc? // global alloc?
match self.tcx.alloc_map.lock().get(id) { match self.tcx.alloc_map.lock().get(id) {
Some(GlobalAlloc::Memory(alloc)) => { Some(GlobalAlloc::Memory(alloc)) => {
self.dump_alloc_helper( self.dump_alloc_helper(
......
...@@ -517,7 +517,7 @@ pub(super) fn eval_operands( ...@@ -517,7 +517,7 @@ pub(super) fn eval_operands(
layout: Option<TyLayout<'tcx>>, layout: Option<TyLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
let tag_scalar = |scalar| match scalar { 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 }, Scalar::Raw { data, size } => Scalar::Raw { data, size },
}; };
// Early-return cases. // Early-return cases.
...@@ -547,7 +547,7 @@ pub(super) fn eval_operands( ...@@ -547,7 +547,7 @@ pub(super) fn eval_operands(
let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc); let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc);
// We rely on mutability being set correctly in that allocation to prevent writes // We rely on mutability being set correctly in that allocation to prevent writes
// where none should happen. // 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)) Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi))
} }
ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x).into()), ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x).into()),
...@@ -559,7 +559,7 @@ pub(super) fn eval_operands( ...@@ -559,7 +559,7 @@ pub(super) fn eval_operands(
Size::from_bytes(start as u64), // offset: `start` Size::from_bytes(start as u64), // offset: `start`
); );
Operand::Immediate(Immediate::new_slice( 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` (end - start) as u64, // len: `end - start`
self, self,
)) ))
......
...@@ -290,7 +290,7 @@ impl<'mir, 'tcx, Tag, M> InterpCx<'mir, 'tcx, M> ...@@ -290,7 +290,7 @@ impl<'mir, 'tcx, Tag, M> InterpCx<'mir, 'tcx, M>
Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static,
M: Machine<'mir, 'tcx, PointerTag = Tag>, M: Machine<'mir, 'tcx, PointerTag = Tag>,
// FIXME: Working around https://github.com/rust-lang/rust/issues/24159 // FIXME: Working around https://github.com/rust-lang/rust/issues/24159
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>, M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKind>, Allocation<Tag, M::AllocExtra>)>,
M::AllocExtra: AllocationExtra<Tag>, M::AllocExtra: AllocationExtra<Tag>,
{ {
/// Take a value, which represents a (thin or wide) reference, and make it a place. /// Take a value, which represents a (thin or wide) reference, and make it a place.
...@@ -1015,7 +1015,7 @@ pub fn force_allocation( ...@@ -1015,7 +1015,7 @@ pub fn force_allocation(
pub fn allocate( pub fn allocate(
&mut self, &mut self,
layout: TyLayout<'tcx>, layout: TyLayout<'tcx>,
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> MPlaceTy<'tcx, M::PointerTag> { ) -> MPlaceTy<'tcx, M::PointerTag> {
let ptr = self.memory.allocate(layout.size, layout.align.abi, kind); let ptr = self.memory.allocate(layout.size, layout.align.abi, kind);
MPlaceTy::from_aligned_ptr(ptr, layout) MPlaceTy::from_aligned_ptr(ptr, layout)
...@@ -1025,9 +1025,9 @@ pub fn allocate( ...@@ -1025,9 +1025,9 @@ pub fn allocate(
pub fn allocate_str( pub fn allocate_str(
&mut self, &mut self,
str: &str, str: &str,
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKind>,
) -> MPlaceTy<'tcx, M::PointerTag> { ) -> 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 meta = Scalar::from_uint(str.len() as u128, self.pointer_size());
let mplace = MemPlace { let mplace = MemPlace {
ptr: ptr.into(), ptr: ptr.into(),
...@@ -1118,7 +1118,7 @@ pub fn raw_const_to_mplace( ...@@ -1118,7 +1118,7 @@ pub fn raw_const_to_mplace(
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
// This must be an allocation in `tcx` // This must be an allocation in `tcx`
assert!(self.tcx.alloc_map.lock().get(raw.alloc_id).is_some()); 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)?; let layout = self.layout_of(raw.ty)?;
Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
} }
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
use rustc_hir::HirId; use rustc_hir::HirId;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_session::lint; use rustc_session::lint;
use rustc_span::Span; use rustc_span::{def_id::DefId, Span};
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
use crate::const_eval::error_to_const_error; 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 ...@@ -162,7 +162,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAn
struct ConstPropMachine; struct ConstPropMachine;
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
type MemoryKinds = !; type MemoryKind = !;
type PointerTag = (); type PointerTag = ();
type ExtraFnVal = !; type ExtraFnVal = !;
...@@ -172,7 +172,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { ...@@ -172,7 +172,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>; type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
const STATIC_KIND: Option<!> = None; const GLOBAL_KIND: Option<!> = None;
const CHECK_ALIGN: bool = false; const CHECK_ALIGN: bool = false;
...@@ -247,7 +247,7 @@ fn init_allocation_extra<'b>( ...@@ -247,7 +247,7 @@ fn init_allocation_extra<'b>(
} }
#[inline(always)] #[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( fn box_alloc(
_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ecx: &mut InterpCx<'mir, 'tcx, Self>,
...@@ -270,14 +270,23 @@ fn access_local( ...@@ -270,14 +270,23 @@ fn access_local(
l.access() l.access()
} }
fn before_access_static( fn before_access_global(
_memory_extra: &(), _memory_extra: &(),
_alloc_id: AllocId,
allocation: &Allocation<Self::PointerTag, Self::AllocExtra>, allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
def_id: Option<DefId>,
is_write: bool,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
// if the static allocation is mutable or if it has relocations (it may be legal to mutate if is_write {
// the memory behind that in the future), then we can't const prop it throw_machine_stop_str!("can't write to global");
if allocation.mutability == Mutability::Mut || allocation.relocations().len() > 0 { }
throw_machine_stop_str!("can't eval mutable statics in ConstProp") // 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(()) Ok(())
......
...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer ...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer
--> $DIR/assign-to-static-within-other-static.rs:10:5 --> $DIR/assign-to-static-within-other-static.rs:10:5
| |
LL | FOO = 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 error: aborting due to previous error
......
// 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() {}
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
...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer ...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer
--> $DIR/static_mut_containing_mut_ref2.rs:7:45 --> $DIR/static_mut_containing_mut_ref2.rs:7:45
| |
LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; 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 error: aborting due to previous error
......
...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer ...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer
--> $DIR/static_mut_containing_mut_ref3.rs:3:31 --> $DIR/static_mut_containing_mut_ref3.rs:3:31
| |
LL | static mut BAR: () = unsafe { FOO.0 = 99; }; 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 error: aborting due to previous error
......
...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer ...@@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer
--> $DIR/write-to-static-mut-in-static.rs:2:33 --> $DIR/write-to-static-mut-in-static.rs:2:33
| |
LL | pub static mut B: () = unsafe { A = 1; }; 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` error[E0391]: cycle detected when const-evaluating `C`
--> $DIR/write-to-static-mut-in-static.rs:5:34 --> $DIR/write-to-static-mut-in-static.rs:5:34
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册