use std::hash::Hash;
use rustc_middle::mir;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;
use super::{
AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
- LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
+ LocalValue, MemPlace, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
StackPopUnwind,
};
/// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
type PointerTag: Provenance + Eq + Hash + 'static;
+ /// When getting the AllocId of a pointer, some extra data is also obtained from the tag
+ /// that is passed to memory access hooks so they can do things with it.
+ type TagExtra: Copy + 'static;
+
/// Machines can define extra (non-instance) things that represent values of function pointers.
/// For example, Miri uses this to return a function pointer from `dlsym`
/// that can later be called to execute the right thing.
/// 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;
-
/// Extra data stored in every allocation.
type AllocExtra: Debug + Clone + 'static;
const PANIC_ON_ALLOC_FAIL: bool;
/// Whether memory accesses should be alignment-checked.
- fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool;
+ fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
/// Whether, when checking alignment, we should `force_int` and thus support
/// custom alignment logic based on whatever the integer address happens to be.
- fn force_int_for_alignment_check(memory_extra: &Self::MemoryExtra) -> bool;
+ ///
+ /// Requires PointerTag::OFFSET_IS_ADDR to be true.
+ fn force_int_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
/// Whether to enforce the validity invariant
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
/// `def_id` is `Some` if this is the "lazy" allocation of a static.
#[inline]
fn before_access_global(
- _memory_extra: &Self::MemoryExtra,
+ _tcx: TyCtxt<'tcx>,
+ _machine: &Self,
_alloc_id: AllocId,
_allocation: ConstAllocation<'tcx>,
_static_def_id: Option<DefId>,
/// Return the root pointer for the given `extern static`.
fn extern_static_base_pointer(
- mem: &Memory<'mir, 'tcx, Self>,
+ ecx: &InterpCx<'mir, 'tcx, Self>,
def_id: DefId,
) -> InterpResult<'tcx, Pointer<Self::PointerTag>>;
///
/// Not called on `extern` or thread-local statics (those use the methods above).
fn tag_alloc_base_pointer(
- mem: &Memory<'mir, 'tcx, Self>,
+ ecx: &InterpCx<'mir, 'tcx, Self>,
ptr: Pointer,
) -> Pointer<Self::PointerTag>;
/// "Int-to-pointer cast"
- fn ptr_from_addr(
- mem: &Memory<'mir, 'tcx, Self>,
+ fn ptr_from_addr_cast(
+ ecx: &InterpCx<'mir, 'tcx, Self>,
+ addr: u64,
+ ) -> Pointer<Option<Self::PointerTag>>;
+
+ // FIXME: Transmuting an integer to a pointer should just always return a `None`
+ // provenance, but that causes problems with function pointers in Miri.
+ /// Hook for returning a pointer from a transmute-like operation on an addr.
+ fn ptr_from_addr_transmute(
+ ecx: &InterpCx<'mir, 'tcx, Self>,
addr: u64,
) -> Pointer<Option<Self::PointerTag>>;
- /// Convert a pointer with provenance into an allocation-offset pair.
+ /// Marks a pointer as exposed, allowing it's provenance
+ /// to be recovered. "Pointer-to-int cast"
+ fn expose_ptr(
+ ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ ptr: Pointer<Self::PointerTag>,
+ ) -> InterpResult<'tcx>;
+
+ /// Convert a pointer with provenance into an allocation-offset pair
+ /// and extra provenance info.
+ ///
+ /// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`.
+ ///
+ /// When this fails, that means the pointer does not point to a live allocation.
fn ptr_get_alloc(
- mem: &Memory<'mir, 'tcx, Self>,
+ ecx: &InterpCx<'mir, 'tcx, Self>,
ptr: Pointer<Self::PointerTag>,
- ) -> (AllocId, Size);
+ ) -> Option<(AllocId, Size, Self::TagExtra)>;
/// Called to initialize the "extra" state of an allocation and make the pointers
/// it contains (in relocations) tagged. The way we construct allocations is
/// 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 init_allocation_extra<'b>(
- mem: &Memory<'mir, 'tcx, Self>,
+ ecx: &InterpCx<'mir, 'tcx, Self>,
id: AllocId,
alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind<Self::MemoryKind>>,
/// need to mutate.
#[inline(always)]
fn memory_read(
- _memory_extra: &Self::MemoryExtra,
+ _tcx: TyCtxt<'tcx>,
+ _machine: &Self,
_alloc_extra: &Self::AllocExtra,
- _tag: Self::PointerTag,
+ _tag: (AllocId, Self::TagExtra),
_range: AllocRange,
) -> InterpResult<'tcx> {
Ok(())
/// Hook for performing extra checks on a memory write access.
#[inline(always)]
fn memory_written(
- _memory_extra: &mut Self::MemoryExtra,
+ _tcx: TyCtxt<'tcx>,
+ _machine: &mut Self,
_alloc_extra: &mut Self::AllocExtra,
- _tag: Self::PointerTag,
+ _tag: (AllocId, Self::TagExtra),
_range: AllocRange,
) -> InterpResult<'tcx> {
Ok(())
/// Hook for performing extra operations on a memory deallocation.
#[inline(always)]
fn memory_deallocated(
- _memory_extra: &mut Self::MemoryExtra,
+ _tcx: TyCtxt<'tcx>,
+ _machine: &mut Self,
_alloc_extra: &mut Self::AllocExtra,
- _tag: Self::PointerTag,
+ _tag: (AllocId, Self::TagExtra),
_range: AllocRange,
) -> InterpResult<'tcx> {
Ok(())
// (CTFE and ConstProp) use the same instance. Here, we share that code.
pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
type PointerTag = AllocId;
+ type TagExtra = ();
+
type ExtraFnVal = !;
type MemoryMap =
type FrameExtra = ();
#[inline(always)]
- fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool {
+ fn enforce_alignment(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
// We do not check for alignment to avoid having to carry an `Align`
// in `ConstValue::ByRef`.
false
}
#[inline(always)]
- fn force_int_for_alignment_check(_memory_extra: &Self::MemoryExtra) -> bool {
+ fn force_int_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
// We do not support `force_int`.
false
}
#[inline(always)]
fn init_allocation_extra<'b>(
- _mem: &Memory<$mir, $tcx, Self>,
+ _ecx: &InterpCx<$mir, $tcx, Self>,
_id: AllocId,
alloc: Cow<'b, Allocation>,
_kind: Option<MemoryKind<Self::MemoryKind>>,
}
fn extern_static_base_pointer(
- mem: &Memory<$mir, $tcx, Self>,
+ ecx: &InterpCx<$mir, $tcx, Self>,
def_id: DefId,
) -> InterpResult<$tcx, Pointer> {
// Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
- Ok(Pointer::new(mem.tcx.create_static_alloc(def_id), Size::ZERO))
+ Ok(Pointer::new(ecx.tcx.create_static_alloc(def_id), Size::ZERO))
}
#[inline(always)]
fn tag_alloc_base_pointer(
- _mem: &Memory<$mir, $tcx, Self>,
+ _ecx: &InterpCx<$mir, $tcx, Self>,
ptr: Pointer<AllocId>,
) -> Pointer<AllocId> {
ptr
}
#[inline(always)]
- fn ptr_from_addr(_mem: &Memory<$mir, $tcx, Self>, addr: u64) -> Pointer<Option<AllocId>> {
+ fn ptr_from_addr_transmute(
+ _ecx: &InterpCx<$mir, $tcx, Self>,
+ addr: u64,
+ ) -> Pointer<Option<AllocId>> {
Pointer::new(None, Size::from_bytes(addr))
}
#[inline(always)]
- fn ptr_get_alloc(_mem: &Memory<$mir, $tcx, Self>, ptr: Pointer<AllocId>) -> (AllocId, Size) {
+ fn ptr_from_addr_cast(
+ _ecx: &InterpCx<$mir, $tcx, Self>,
+ addr: u64,
+ ) -> Pointer<Option<AllocId>> {
+ Pointer::new(None, Size::from_bytes(addr))
+ }
+
+ #[inline(always)]
+ fn ptr_get_alloc(
+ _ecx: &InterpCx<$mir, $tcx, Self>,
+ ptr: Pointer<AllocId>,
+ ) -> Option<(AllocId, Size, Self::TagExtra)> {
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let (alloc_id, offset) = ptr.into_parts();
- (alloc_id, offset)
+ Some((alloc_id, offset, ()))
}
}