//! All high-level functions to write to memory work on places as destinations.
use std::convert::TryFrom;
-use std::fmt::Debug;
use std::hash::Hash;
use rustc_ast::Mutability;
use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants};
use super::{
- alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, ConstAlloc, ImmTy, Immediate,
- InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer,
- PointerArithmetic, Scalar, ScalarMaybeUninit,
+ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
+ ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy,
+ Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit,
};
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
+#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)]
/// Information required for the sound usage of a `MemPlace`.
-pub enum MemPlaceMeta<Tag = ()> {
+pub enum MemPlaceMeta<Tag = AllocId> {
/// The unsized payload (e.g. length for slices or vtable pointer for trait objects).
Meta(Scalar<Tag>),
/// `Sized` types or unsized `extern type`
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(MemPlaceMeta, 24);
+//FIXME rustc_data_structures::static_assert_size!(MemPlaceMeta, 24);
+
+impl<Tag: Provenance> std::fmt::Debug for MemPlaceMeta<Tag> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use MemPlaceMeta::*;
+ match self {
+ Meta(s) => f.debug_tuple("Meta").field(s).finish(),
+ None => f.debug_tuple("None").finish(),
+ Poison => f.debug_tuple("Poison").finish(),
+ }
+ }
+}
impl<Tag> MemPlaceMeta<Tag> {
pub fn unwrap_meta(self) -> Scalar<Tag> {
}
}
- pub fn erase_tag(self) -> MemPlaceMeta<()> {
+ pub fn erase_for_fmt(self) -> MemPlaceMeta
+ where
+ Tag: Provenance,
+ {
match self {
- Self::Meta(s) => MemPlaceMeta::Meta(s.erase_tag()),
+ Self::Meta(s) => MemPlaceMeta::Meta(s.erase_for_fmt()),
Self::None => MemPlaceMeta::None,
Self::Poison => MemPlaceMeta::Poison,
}
}
}
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
-pub struct MemPlace<Tag = ()> {
- /// A place may have an integral pointer for ZSTs, and since it might
- /// be turned back into a reference before ever being dereferenced.
- /// However, it may never be uninit.
- pub ptr: Scalar<Tag>,
+#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)]
+pub struct MemPlace<Tag = AllocId> {
+ /// The pointer can be a pure integer, with the `None` tag.
+ pub ptr: Pointer<Option<Tag>>,
pub align: Align,
/// Metadata for unsized places. Interpretation is up to the type.
/// Must not be present for sized types, but can be missing for unsized types
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(MemPlace, 56);
+//FIXME rustc_data_structures::static_assert_size!(MemPlace, 56);
+
+impl<Tag: Provenance> std::fmt::Debug for MemPlace<Tag> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let MemPlace { ptr, align, meta } = self;
+ f.debug_struct("MemPlace")
+ .field("ptr", ptr)
+ .field("align", align)
+ .field("meta", meta)
+ .finish()
+ }
+}
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)]
-pub enum Place<Tag = ()> {
+#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)]
+pub enum Place<Tag = AllocId> {
/// A place referring to a value allocated in the `Memory` system.
Ptr(MemPlace<Tag>),
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Place, 64);
+//FIXME rustc_data_structures::static_assert_size!(Place, 64);
+
+impl<Tag: Provenance> std::fmt::Debug for Place<Tag> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use Place::*;
+ match self {
+ Ptr(p) => f.debug_tuple("Ptr").field(p).finish(),
+ Local { frame, local } => {
+ f.debug_struct("Local").field("frame", frame).field("local", local).finish()
+ }
+ }
+ }
+}
-#[derive(Copy, Clone, Debug)]
-pub struct PlaceTy<'tcx, Tag = ()> {
+#[derive(Copy, Clone)]
+pub struct PlaceTy<'tcx, Tag = AllocId> {
place: Place<Tag>, // Keep this private; it helps enforce invariants.
pub layout: TyAndLayout<'tcx>,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(PlaceTy<'_>, 80);
+//FIXME rustc_data_structures::static_assert_size!(PlaceTy<'_>, 80);
+
+impl<'tcx, Tag: Provenance> std::fmt::Debug for PlaceTy<'tcx, Tag> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let PlaceTy { place, layout } = self;
+ f.debug_struct("PlaceTy").field("place", place).field("layout", layout).finish()
+ }
+}
impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> {
type Target = Place<Tag>;
}
/// A MemPlace with its layout. Constructing it is only possible in this module.
-#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
-pub struct MPlaceTy<'tcx, Tag = ()> {
+#[derive(Copy, Clone, Hash, Eq, PartialEq)]
+pub struct MPlaceTy<'tcx, Tag = AllocId> {
mplace: MemPlace<Tag>,
pub layout: TyAndLayout<'tcx>,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 72);
+//FIXME rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 72);
+
+impl<'tcx, Tag: Provenance> std::fmt::Debug for MPlaceTy<'tcx, Tag> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let MPlaceTy { mplace, layout } = self;
+ f.debug_struct("MPlaceTy").field("mplace", mplace).field("layout", layout).finish()
+ }
+}
impl<'tcx, Tag> std::ops::Deref for MPlaceTy<'tcx, Tag> {
type Target = MemPlace<Tag>;
}
impl<Tag> MemPlace<Tag> {
- /// Replace ptr tag, maintain vtable tag (if any)
- #[inline]
- pub fn replace_tag(self, new_tag: Tag) -> Self {
- MemPlace { ptr: self.ptr.erase_tag().with_tag(new_tag), align: self.align, meta: self.meta }
- }
-
#[inline]
- pub fn erase_tag(self) -> MemPlace {
- MemPlace { ptr: self.ptr.erase_tag(), align: self.align, meta: self.meta.erase_tag() }
+ pub fn erase_for_fmt(self) -> MemPlace
+ where
+ Tag: Provenance,
+ {
+ MemPlace {
+ ptr: self.ptr.map_erase_for_fmt(),
+ align: self.align,
+ meta: self.meta.erase_for_fmt(),
+ }
}
#[inline(always)]
- fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
+ pub fn from_ptr(ptr: Pointer<Option<Tag>>, align: Align) -> Self {
MemPlace { ptr, align, meta: MemPlaceMeta::None }
}
- #[inline(always)]
- pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
- Self::from_scalar_ptr(ptr.into(), align)
- }
-
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
/// This is the inverse of `ref_to_mplace`.
#[inline(always)]
- pub fn to_ref(self) -> Immediate<Tag> {
+ pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Tag> {
match self.meta {
- MemPlaceMeta::None => Immediate::Scalar(self.ptr.into()),
- MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
+ MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
+ MemPlaceMeta::Meta(meta) => {
+ Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx).into(), meta.into())
+ }
MemPlaceMeta::Poison => bug!(
"MPlaceTy::dangling may never be used to produce a \
place that will have the address of its pointee taken"
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
Ok(MemPlace {
- ptr: self.ptr.ptr_offset(offset, cx)?,
+ ptr: self.ptr.offset(offset, cx)?,
align: self.align.restrict_for_offset(offset),
meta,
})
impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> {
/// Produces a MemPlace that works for ZST but nothing else
#[inline]
- pub fn dangling(layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
+ pub fn dangling(layout: TyAndLayout<'tcx>) -> Self {
let align = layout.align.abi;
- let ptr = Scalar::from_machine_usize(align.bytes(), cx);
+ let ptr = Pointer::new(None, Size::from_bytes(align.bytes())); // no provenance, absolute address
// `Poison` this to make sure that the pointer value `ptr` is never observable by the program.
MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout }
}
- /// Replace ptr tag, maintain vtable tag (if any)
- #[inline]
- pub fn replace_tag(&self, new_tag: Tag) -> Self {
- MPlaceTy { mplace: self.mplace.replace_tag(new_tag), layout: self.layout }
- }
-
#[inline]
pub fn offset(
&self,
}
#[inline]
- fn from_aligned_ptr(ptr: Pointer<Tag>, layout: TyAndLayout<'tcx>) -> Self {
+ fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self {
MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout }
}
#[inline]
- pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
+ pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64>
+ where
+ Tag: Provenance,
+ {
if self.layout.is_unsized() {
// We need to consult `meta` metadata
match self.layout.ty.kind() {
}
// These are defined here because they produce a place.
-impl<'tcx, Tag: Debug + Copy> OpTy<'tcx, Tag> {
+impl<'tcx, Tag: Copy> OpTy<'tcx, Tag> {
#[inline(always)]
/// Note: do not call `as_ref` on the resulting place. This function should only be used to
/// read from the resulting mplace, not to get its address back.
- pub fn try_as_mplace(
- &self,
- cx: &impl HasDataLayout,
- ) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
+ pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
match **self {
Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }),
- Operand::Immediate(_) if self.layout.is_zst() => {
- Ok(MPlaceTy::dangling(self.layout, cx))
- }
+ Operand::Immediate(_) if self.layout.is_zst() => Ok(MPlaceTy::dangling(self.layout)),
Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)),
}
}
#[inline(always)]
/// Note: do not call `as_ref` on the resulting place. This function should only be used to
/// read from the resulting mplace, not to get its address back.
- pub fn assert_mem_place(&self, cx: &impl HasDataLayout) -> MPlaceTy<'tcx, Tag> {
- self.try_as_mplace(cx).unwrap()
+ pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag>
+ where
+ Tag: Provenance,
+ {
+ self.try_as_mplace().unwrap()
}
}
-impl<Tag: Debug> Place<Tag> {
+impl<Tag: Provenance> Place<Tag> {
#[inline]
pub fn assert_mem_place(self) -> MemPlace<Tag> {
match self {
}
}
-impl<'tcx, Tag: Debug> PlaceTy<'tcx, Tag> {
+impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> {
#[inline]
pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout }
impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M>
where
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
- Tag: Debug + Copy + Eq + Hash + 'static,
+ Tag: Provenance + Eq + Hash + 'static,
M: Machine<'mir, 'tcx, PointerTag = Tag>,
{
/// Take a value, which represents a (thin or wide) reference, and make it a place.
val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty;
let layout = self.layout_of(pointee_type)?;
let (ptr, meta) = match **val {
- Immediate::Scalar(ptr) => (ptr.check_init()?, MemPlaceMeta::None),
- Immediate::ScalarPair(ptr, meta) => {
- (ptr.check_init()?, MemPlaceMeta::Meta(meta.check_init()?))
- }
+ Immediate::Scalar(ptr) => (ptr, MemPlaceMeta::None),
+ Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta.check_init()?)),
};
let mplace = MemPlace {
- ptr,
+ ptr: self.scalar_to_ptr(ptr.check_init()?),
// We could use the run-time alignment here. For now, we do not, because
// the point of tracking the alignment here is to make sure that the *static*
// alignment information emitted with the loads is correct. The run-time
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
let val = self.read_immediate(src)?;
trace!("deref to {} on {:?}", val.layout.ty, *val);
- let place = self.ref_to_mplace(&val)?;
- self.mplace_access_checked(place, None)
+ let mplace = self.ref_to_mplace(&val)?;
+ self.check_mplace_access(mplace)?;
+ Ok(mplace)
}
#[inline]
self.memory.get_mut(place.ptr, size, place.align)
}
- /// Return the "access-checked" version of this `MPlace`, where for non-ZST
- /// this is definitely a `Pointer`.
- ///
- /// `force_align` must only be used when correct alignment does not matter,
- /// like in Stacked Borrows.
- pub fn mplace_access_checked(
- &self,
- mut place: MPlaceTy<'tcx, M::PointerTag>,
- force_align: Option<Align>,
- ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ /// Check if this mplace is dereferencable and sufficiently aligned.
+ pub fn check_mplace_access(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
let (size, align) = self
- .size_and_align_of_mplace(&place)?
- .unwrap_or((place.layout.size, place.layout.align.abi));
- assert!(place.mplace.align <= align, "dynamic alignment less strict than static one?");
- let align = force_align.unwrap_or(align);
- // Record new (stricter, unless forced) alignment requirement in place.
- place.mplace.align = align;
- // When dereferencing a pointer, it must be non-null, aligned, and live.
- if let Some(ptr) = self.memory.check_ptr_access(place.ptr, size, align)? {
- place.mplace.ptr = ptr.into();
- }
- Ok(place)
- }
-
- /// Force `place.ptr` to a `Pointer`.
- /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
- pub(super) fn force_mplace_ptr(
- &self,
- mut place: MPlaceTy<'tcx, M::PointerTag>,
- ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
- place.mplace.ptr = self.force_ptr(place.mplace.ptr)?.into();
- Ok(place)
+ .size_and_align_of_mplace(&mplace)?
+ .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
+ assert!(mplace.mplace.align <= align, "dynamic alignment less strict than static one?");
+ let align = M::enforce_alignment(&self.memory.extra).then_some(align);
+ self.memory.check_ptr_access_align(
+ mplace.ptr,
+ size,
+ align.unwrap_or(Align::ONE),
+ CheckInAllocMsg::MemoryAccessTest, // FIXME sth more specific?
+ )?;
+ Ok(())
}
/// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
let layout = self.layout_of(self.tcx.types.usize)?;
let n = self.access_local(self.frame(), local, Some(layout))?;
let n = self.read_scalar(&n)?;
- let n = u64::try_from(
- self.force_bits(n.check_init()?, self.tcx.data_layout.pointer_size)?,
- )
- .unwrap();
+ let n = n.to_machine_usize(self)?;
self.mplace_index(base, n)?
}
kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'static, MPlaceTy<'tcx, M::PointerTag>> {
let ptr = self.memory.allocate(layout.size, layout.align.abi, kind)?;
- Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
+ Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
}
/// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation.
let _ = self.tcx.global_alloc(raw.alloc_id);
let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
let layout = self.layout_of(raw.ty)?;
- Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
+ Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
}
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
&self,
mplace: &MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
- let vtable = mplace.vtable(); // also sanity checks the type
+ let vtable = self.scalar_to_ptr(mplace.vtable()); // also sanity checks the type
let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
let layout = self.layout_of(ty)?;