diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index c612d6ad1bb24..c1ce11a982188 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -53,6 +53,94 @@ pub struct Allocation { pub extra: Extra, } + +pub trait AllocationExtra: ::std::fmt::Debug + Clone { + /// Hook to initialize the extra data when an allocation gets created. + fn memory_allocated( + _size: Size, + _memory_extra: &MemoryExtra + ) -> Self; + + /// Hook for performing extra checks on a memory read access. + /// + /// Takes read-only access to the allocation so we can keep all the memory read + /// operations take `&self`. Use a `RefCell` in `AllocExtra` if you + /// need to mutate. + #[inline(always)] + fn memory_read( + _alloc: &Allocation, + _ptr: Pointer, + _size: Size, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Hook for performing extra checks on a memory write access. + #[inline(always)] + fn memory_written( + _alloc: &mut Allocation, + _ptr: Pointer, + _size: Size, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Hook for performing extra checks on a memory deallocation. + /// `size` will be the size of the allocation. + #[inline(always)] + fn memory_deallocated( + _alloc: &mut Allocation, + _ptr: Pointer, + _size: Size, + ) -> EvalResult<'tcx> { + Ok(()) + } +} + +impl AllocationExtra<(), ()> for () { + #[inline(always)] + fn memory_allocated( + _size: Size, + _memory_extra: &() + ) -> Self { + () + } +} + +impl Allocation { + /// Creates a read-only allocation initialized by the given bytes + pub fn from_bytes(slice: &[u8], align: Align, extra: Extra) -> Self { + let mut undef_mask = UndefMask::new(Size::ZERO); + undef_mask.grow(Size::from_bytes(slice.len() as u64), true); + Self { + bytes: slice.to_owned(), + relocations: Relocations::new(), + undef_mask, + align, + mutability: Mutability::Immutable, + extra, + } + } + + pub fn from_byte_aligned_bytes(slice: &[u8], extra: Extra) -> Self { + Allocation::from_bytes(slice, Align::from_bytes(1).unwrap(), extra) + } + + pub fn undef(size: Size, align: Align, extra: Extra) -> Self { + assert_eq!(size.bytes() as usize as u64, size.bytes()); + Allocation { + bytes: vec![0; size.bytes() as usize], + relocations: Relocations::new(), + undef_mask: UndefMask::new(size), + align, + mutability: Mutability::Mutable, + extra, + } + } +} + +impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {} + /// Alignment and bounds checks impl<'tcx, Tag, Extra> Allocation { /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end @@ -81,7 +169,7 @@ impl<'tcx, Tag, Extra> Allocation { } /// Byte accessors -impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { +impl<'tcx, Tag: Copy, Extra> Allocation { /// The last argument controls whether we error out when there are undefined /// or pointer bytes. You should never call this, call `get_bytes` or /// `get_bytes_with_undef_and_ptr` instead, @@ -89,13 +177,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// This function also guarantees that the resulting pointer will remain stable /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies /// on that. - fn get_bytes_internal( + fn get_bytes_internal( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, check_defined_and_ptr: bool, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { self.check_bounds(cx, ptr, size)?; if check_defined_and_ptr { @@ -115,35 +206,44 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } #[inline] - pub fn get_bytes( + pub fn get_bytes( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { self.get_bytes_internal(cx, ptr, size, true) } /// It is the caller's responsibility to handle undefined and pointer bytes. /// However, this still checks that there are no relocations on the *edges*. #[inline] - pub fn get_bytes_with_undef_and_ptr( + pub fn get_bytes_with_undef_and_ptr( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { self.get_bytes_internal(cx, ptr, size, false) } /// Just calling this already marks everything as defined and removes relocations, /// so be sure to actually put data there! - pub fn get_bytes_mut( + pub fn get_bytes_mut( &mut self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, - ) -> EvalResult<'tcx, &mut [u8]> { + ) -> EvalResult<'tcx, &mut [u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_bounds(cx, ptr, size)?; @@ -160,14 +260,17 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } /// Reading and writing -impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { +impl<'tcx, Tag: Copy, Extra> Allocation { /// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached /// before a `0` is found. - pub fn read_c_str( + pub fn read_c_str( &self, cx: &impl HasDataLayout, ptr: Pointer, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); let offset = ptr.offset.bytes() as usize; match self.bytes[offset..].iter().position(|&c| c == 0) { @@ -184,13 +287,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a /// relocation. If `allow_ptr_and_undef` is `false`, also enforces that the memory in the /// given range contains neither relocations nor undef bytes. - pub fn check_bytes( + pub fn check_bytes( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, allow_ptr_and_undef: bool, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { // Check bounds and relocations on the edges self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Check undef and ptr @@ -204,25 +310,31 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Writes `src` to the memory starting at `ptr.offset`. /// /// Will do bounds checks on the allocation. - pub fn write_bytes( + pub fn write_bytes( &mut self, cx: &impl HasDataLayout, ptr: Pointer, src: &[u8], - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?; bytes.clone_from_slice(src); Ok(()) } /// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`. - pub fn write_repeat( + pub fn write_repeat( &mut self, cx: &impl HasDataLayout, ptr: Pointer, val: u8, count: Size - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { let bytes = self.get_bytes_mut(cx, ptr, count)?; for b in bytes { *b = val; @@ -238,12 +350,15 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// being valid for ZSTs /// /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn read_scalar( + pub fn read_scalar( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size - ) -> EvalResult<'tcx, ScalarMaybeUndef> { + ) -> EvalResult<'tcx, ScalarMaybeUndef> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { // get_bytes_unchecked tests relocation edges let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Undef check happens *after* we established that the alignment is correct. @@ -273,11 +388,14 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn read_ptr_sized( + pub fn read_ptr_sized( &self, cx: &impl HasDataLayout, ptr: Pointer, - ) -> EvalResult<'tcx, ScalarMaybeUndef> { + ) -> EvalResult<'tcx, ScalarMaybeUndef> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { self.read_scalar(cx, ptr, cx.data_layout().pointer_size) } @@ -289,13 +407,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// being valid for ZSTs /// /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn write_scalar( + pub fn write_scalar( &mut self, cx: &impl HasDataLayout, ptr: Pointer, val: ScalarMaybeUndef, type_size: Size, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { let val = match val { ScalarMaybeUndef::Scalar(scalar) => scalar, ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false), @@ -336,12 +457,15 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn write_ptr_sized( + pub fn write_ptr_sized( &mut self, cx: &impl HasDataLayout, ptr: Pointer, val: ScalarMaybeUndef - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra + { let ptr_size = cx.data_layout().pointer_size; self.write_scalar(cx, ptr.into(), val, ptr_size) } @@ -465,79 +589,7 @@ impl<'tcx, Tag, Extra> Allocation { } } -pub trait AllocationExtra: ::std::fmt::Debug + Default + Clone { - /// Hook for performing extra checks on a memory read access. - /// - /// Takes read-only access to the allocation so we can keep all the memory read - /// operations take `&self`. Use a `RefCell` in `AllocExtra` if you - /// need to mutate. - #[inline] - fn memory_read( - _alloc: &Allocation, - _ptr: Pointer, - _size: Size, - ) -> EvalResult<'tcx> { - Ok(()) - } - - /// Hook for performing extra checks on a memory write access. - #[inline] - fn memory_written( - _alloc: &mut Allocation, - _ptr: Pointer, - _size: Size, - ) -> EvalResult<'tcx> { - Ok(()) - } - - /// Hook for performing extra checks on a memory deallocation. - /// `size` will be the size of the allocation. - #[inline] - fn memory_deallocated( - _alloc: &mut Allocation, - _ptr: Pointer, - _size: Size, - ) -> EvalResult<'tcx> { - Ok(()) - } -} - -impl AllocationExtra<()> for () {} - -impl Allocation { - /// Creates a read-only allocation initialized by the given bytes - pub fn from_bytes(slice: &[u8], align: Align) -> Self { - let mut undef_mask = UndefMask::new(Size::ZERO); - undef_mask.grow(Size::from_bytes(slice.len() as u64), true); - Self { - bytes: slice.to_owned(), - relocations: Relocations::new(), - undef_mask, - align, - mutability: Mutability::Immutable, - extra: Extra::default(), - } - } - - pub fn from_byte_aligned_bytes(slice: &[u8]) -> Self { - Allocation::from_bytes(slice, Align::from_bytes(1).unwrap()) - } - - pub fn undef(size: Size, align: Align) -> Self { - assert_eq!(size.bytes() as usize as u64, size.bytes()); - Allocation { - bytes: vec![0; size.bytes() as usize], - relocations: Relocations::new(), - undef_mask: UndefMask::new(size), - align, - mutability: Mutability::Mutable, - extra: Extra::default(), - } - } -} - -impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {} - +/// Relocations #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Relocations(SortedMap); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 1b947c276f3b3..25ef6ddc005e3 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1055,7 +1055,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Allocates a byte or string literal for `mir::interpret`, read-only pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId { // create an allocation that just contains these bytes - let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes); + let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes, ()); let alloc = self.intern_const_alloc(alloc); self.alloc_map.lock().allocate(alloc) } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 1bc3b322717e5..291b5c170ef36 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -65,6 +65,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( return_place: None, return_to_block: StackPopCleanup::Goto(None), // never pop stmt: 0, + extra: (), }); Ok(ecx) } @@ -353,9 +354,12 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> for CompileTimeInterpreter<'a, 'mir, 'tcx> { type MemoryKinds = !; - type AllocExtra = (); type PointerTag = (); + type FrameExtra = (); + type MemoryExtra = (); + type AllocExtra = (); + type MemoryMap = FxHashMap, Allocation)>; const STATIC_KIND: Option = None; // no copying of statics allowed @@ -432,16 +436,18 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> } fn find_foreign_static( - _tcx: TyCtxtAt<'a, 'tcx, 'tcx>, _def_id: DefId, + _tcx: TyCtxtAt<'a, 'tcx, 'tcx>, + _memory_extra: &(), ) -> EvalResult<'tcx, Cow<'tcx, Allocation>> { err!(ReadForeignStatic) } #[inline(always)] - fn adjust_static_allocation( - alloc: &'_ Allocation - ) -> Cow<'_, Allocation> { + fn adjust_static_allocation<'b>( + alloc: &'b Allocation, + _memory_extra: &(), + ) -> Cow<'b, Allocation> { // We do not use a tag so we can just cheaply forward the reference Cow::Borrowed(alloc) } @@ -487,6 +493,22 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> ) -> EvalResult<'tcx, Pointer> { Ok(ptr) } + + #[inline(always)] + fn stack_push( + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Called immediately before a stack frame gets popped + #[inline(always)] + fn stack_pop( + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + _extra: (), + ) -> EvalResult<'tcx> { + Ok(()) + } } /// Project to a field of a (variant of a) const diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 2eb5f7c853f8c..d36d530fe78b2 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -49,7 +49,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> { pub(crate) memory: Memory<'a, 'mir, 'tcx, M>, /// The virtual call stack. - pub(crate) stack: Vec>, + pub(crate) stack: Vec>, /// A cache for deduplicating vtables pub(super) vtables: FxHashMap<(Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>), AllocId>, @@ -57,7 +57,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> { /// A stack frame. #[derive(Clone)] -pub struct Frame<'mir, 'tcx: 'mir, Tag=()> { +pub struct Frame<'mir, 'tcx: 'mir, Tag=(), Extra=()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// @@ -96,6 +96,9 @@ pub struct Frame<'mir, 'tcx: 'mir, Tag=()> { /// The index of the currently evaluated statement. pub stmt: usize, + + /// Extra data for the machine + pub extra: Extra, } #[derive(Clone, Debug, Eq, PartialEq, Hash)] @@ -196,7 +199,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc } #[inline(always)] - pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag>] { + pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { &self.stack } @@ -207,12 +210,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc } #[inline(always)] - pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag> { + pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { self.stack.last().expect("no call frames exist") } #[inline(always)] - pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag> { + pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { self.stack.last_mut().expect("no call frames exist") } @@ -294,7 +297,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc pub fn layout_of_local( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag>, + frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, local: mir::Local ) -> EvalResult<'tcx, TyLayout<'tcx>> { let local_ty = frame.mir.local_decls[local].ty; @@ -424,6 +427,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc ::log_settings::settings().indentation += 1; // first push a stack frame so we have access to the local substs + let extra = M::stack_push(self)?; self.stack.push(Frame { mir, block: mir::START_BLOCK, @@ -435,6 +439,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc span, instance, stmt: 0, + extra, }); // don't allocate at all for trivial constants @@ -504,6 +509,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc let frame = self.stack.pop().expect( "tried to pop a stack frame, but there were none", ); + M::stack_pop(self, frame.extra)?; // Abort early if we do not want to clean up: We also avoid validation in that case, // because this is CTFE and the final value will be thoroughly validated anyway. match frame.return_to_block { diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 57640dc48f13f..2c78807df452f 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -77,8 +77,16 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { /// The `default()` is used for pointers to consts, statics, vtables and functions. type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static; + /// Extra data stored in every call frame. + type FrameExtra; + + /// Extra data stored in memory. A reference to this is available when `AllocExtra` + /// gets initialized, so you can e.g. have an `Rc` here if there is global state you + /// need access to in the `AllocExtra` hooks. + type MemoryExtra: Default; + /// Extra data stored in every allocation. - type AllocExtra: AllocationExtra; + type AllocExtra: AllocationExtra; /// Memory's allocation map type MemoryMap: @@ -135,8 +143,9 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { /// the machine memory. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) fn find_foreign_static( - tcx: TyCtxtAt<'a, 'tcx, 'tcx>, def_id: DefId, + tcx: TyCtxtAt<'a, 'tcx, 'tcx>, + memory_extra: &Self::MemoryExtra, ) -> EvalResult<'tcx, Cow<'tcx, Allocation>>; /// Called to turn an allocation obtained from the `tcx` into one that has @@ -146,9 +155,10 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { /// allocation (because a copy had to be done to add tags or metadata), machine memory will /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) - fn adjust_static_allocation( - alloc: &'_ Allocation - ) -> Cow<'_, Allocation>; + fn adjust_static_allocation<'b>( + alloc: &'b Allocation, + memory_extra: &Self::MemoryExtra, + ) -> Cow<'b, Allocation>; /// Called for all binary operations on integer(-like) types when one operand is a pointer /// value, and for the `Offset` operation that is inherently about pointers. @@ -206,4 +216,15 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { ) -> EvalResult<'tcx> { Ok(()) } + + /// Called immediately before a new stack frame got pushed + fn stack_push( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + ) -> EvalResult<'tcx, Self::FrameExtra>; + + /// Called immediately after a stack frame gets popped + fn stack_pop( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + extra: Self::FrameExtra, + ) -> EvalResult<'tcx>; } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index c673b57a66f5f..5c293bac74ad9 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -73,6 +73,9 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> { /// that do not exist any more. dead_alloc_map: FxHashMap, + /// Extra data added by the machine. + pub extra: M::MemoryExtra, + /// Lets us implement `HasDataLayout`, which is awfully convenient. pub(super) tcx: TyCtxtAt<'a, 'tcx, 'tcx>, } @@ -88,13 +91,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout // FIXME: Really we shouldn't clone memory, ever. Snapshot machinery should instead // carefully copy only the reachable parts. -impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> - Clone for Memory<'a, 'mir, 'tcx, M> +impl<'a, 'mir, 'tcx, M> + Clone +for + Memory<'a, 'mir, 'tcx, M> +where + M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=(), MemoryExtra=()>, + M::MemoryMap: AllocMap, Allocation)>, { fn clone(&self) -> Self { Memory { alloc_map: self.alloc_map.clone(), dead_alloc_map: self.dead_alloc_map.clone(), + extra: (), tcx: self.tcx, } } @@ -103,8 +112,9 @@ impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>) -> Self { Memory { - alloc_map: Default::default(), + alloc_map: M::MemoryMap::default(), dead_alloc_map: FxHashMap::default(), + extra: M::MemoryExtra::default(), tcx, } } @@ -133,7 +143,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { align: Align, kind: MemoryKind, ) -> EvalResult<'tcx, Pointer> { - Ok(Pointer::from(self.allocate_with(Allocation::undef(size, align), kind)?)) + let extra = AllocationExtra::memory_allocated(size, &self.extra); + Ok(Pointer::from(self.allocate_with(Allocation::undef(size, align, extra), kind)?)) } pub fn reallocate( @@ -309,15 +320,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// this machine use the same pointer tag, so it is indirected through /// `M::static_with_default_tag`. fn get_static_alloc( - tcx: TyCtxtAt<'a, 'tcx, 'tcx>, id: AllocId, + tcx: TyCtxtAt<'a, 'tcx, 'tcx>, + memory_extra: &M::MemoryExtra, ) -> EvalResult<'tcx, Cow<'tcx, Allocation>> { let alloc = tcx.alloc_map.lock().get(id); let def_id = match alloc { Some(AllocType::Memory(mem)) => { // We got tcx memory. Let the machine figure out whether and how to // turn that into memory with the right pointer tag. - return Ok(M::adjust_static_allocation(mem)) + return Ok(M::adjust_static_allocation(mem, memory_extra)) } Some(AllocType::Function(..)) => { return err!(DerefFunctionPointer) @@ -331,7 +343,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // We got a "lazy" static that has not been computed yet, do some work trace!("static_alloc: Need to compute {:?}", def_id); if tcx.is_foreign_item(def_id) { - return M::find_foreign_static(tcx, def_id); + return M::find_foreign_static(def_id, tcx, memory_extra); } let instance = Instance::mono(tcx.tcx, def_id); let gid = GlobalId { @@ -351,7 +363,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { let allocation = tcx.alloc_map.lock().unwrap_memory(raw_const.alloc_id); // We got tcx memory. Let the machine figure out whether and how to // turn that into memory with the right pointer tag. - M::adjust_static_allocation(allocation) + M::adjust_static_allocation(allocation, memory_extra) }) } @@ -361,7 +373,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // `get_static_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `EvalResult<'tcx, &Allocation>`. let a = self.alloc_map.get_or(id, || { - let alloc = Self::get_static_alloc(self.tcx, id).map_err(Err)?; + let alloc = Self::get_static_alloc(id, self.tcx, &self.extra).map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { // We got a ref, cheaply return that as an "error" so that the @@ -390,10 +402,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { id: AllocId, ) -> EvalResult<'tcx, &mut Allocation> { 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 // to give us a cheap reference. - let alloc = Self::get_static_alloc(tcx, id)?; + let alloc = Self::get_static_alloc(id, tcx, memory_extra)?; if alloc.mutability == Mutability::Immutable { return err!(ModifiedConstantMemory); } @@ -601,7 +614,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// Interning (for CTFE) impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M> where - M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=()>, + M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=(), MemoryExtra=()>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 M::MemoryMap: AllocMap, Allocation)>, { /// mark an allocation as static and initialized, either mutable or not diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 539bc6d965fd6..83ceadada65ce 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -471,7 +471,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> /// When you know the layout of the local in advance, you can pass it as last argument pub fn access_local( &self, - frame: &super::Frame<'mir, 'tcx, M::PointerTag>, + frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, local: mir::Local, layout: Option>, ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 0fd1a993cbd90..1b47530eaec65 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -295,10 +295,12 @@ impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { // separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 impl<'a, 'mir, 'tcx, Tag, M> EvalContext<'a, 'mir, 'tcx, M> where + // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static, M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 M::MemoryMap: AllocMap, Allocation)>, - M::AllocExtra: AllocationExtra, + M::AllocExtra: AllocationExtra, { /// Take a value, which represents a (thin or fat) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 4b63335ad964c..f9ce7b4319fac 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -323,6 +323,7 @@ impl_stable_hash_for!(impl<'tcx, 'mir: 'tcx> for struct Frame<'mir, 'tcx> { locals, block, stmt, + extra, }); impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx> @@ -340,6 +341,7 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx> locals, block, stmt, + extra: _, } = self; FrameSnapshot { diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index ed4cb65ea74b1..d98d05bc01b85 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -312,12 +312,16 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> } } ty::RawPtr(..) => { - // No undef allowed here. Eventually this should be consistent with - // the integer types. - let _ptr = try_validation!(value.to_scalar_ptr(), - "undefined address in pointer", self.path); - let _meta = try_validation!(value.to_meta(), - "uninitialized data in fat pointer metadata", self.path); + if self.const_mode { + // Integers/floats in CTFE: For consistency with integers, we do not + // accept undef. + let _ptr = try_validation!(value.to_scalar_ptr(), + "undefined address in raw pointer", self.path); + let _meta = try_validation!(value.to_meta(), + "uninitialized data in raw fat pointer metadata", self.path); + } else { + // Remain consistent with `usize`: Accept anything. + } } _ if ty.is_box() || ty.is_region_ptr() => { // Handle fat pointers.