]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_mir/src/interpret/place.rs
CTFE/Miri engine Pointer type overhaul: make Scalar-to-Pointer conversion infallible
[rust.git] / compiler / rustc_mir / src / interpret / place.rs
index 42a304ce41213337d7614c1063c74e4c9ab8604d..b5d26306d4be3d1e2db704bed72f01a7aa6736f2 100644 (file)
@@ -3,7 +3,6 @@
 //! 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`
@@ -35,7 +34,18 @@ pub enum MemPlaceMeta<Tag = ()> {
 }
 
 #[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> {
@@ -53,21 +63,22 @@ fn has_meta(self) -> bool {
         }
     }
 
-    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
@@ -76,10 +87,21 @@ pub struct MemPlace<Tag = ()> {
 }
 
 #[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>),
 
@@ -89,16 +111,35 @@ pub enum Place<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>;
@@ -109,14 +150,21 @@ fn deref(&self) -> &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>;
@@ -134,34 +182,32 @@ fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
 }
 
 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"
@@ -177,7 +223,7 @@ pub fn offset(
         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,
         })
@@ -187,19 +233,13 @@ pub fn offset(
 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,
@@ -212,12 +252,15 @@ pub fn offset(
     }
 
     #[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() {
@@ -244,19 +287,14 @@ pub(super) fn vtable(&self) -> Scalar<Tag> {
 }
 
 // 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)),
         }
     }
@@ -264,12 +302,15 @@ pub fn try_as_mplace(
     #[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 {
@@ -279,7 +320,7 @@ pub fn assert_mem_place(self) -> MemPlace<Tag> {
     }
 }
 
-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 }
@@ -290,7 +331,7 @@ pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
 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.
@@ -307,14 +348,12 @@ pub fn ref_to_mplace(
             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
@@ -333,8 +372,9 @@ pub fn deref_operand(
     ) -> 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]
@@ -359,38 +399,20 @@ pub(super) fn get_alloc_mut(
         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
@@ -558,10 +580,7 @@ pub(super) fn mplace_projection(
                 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)?
             }
 
@@ -1020,7 +1039,7 @@ pub fn allocate(
         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.
@@ -1125,7 +1144,7 @@ pub fn raw_const_to_mplace(
         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.
@@ -1134,7 +1153,7 @@ pub(super) fn unpack_dyn_trait(
         &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)?;