]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_middle/src/mir/interpret/value.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / mir / interpret / value.rs
index 66ff6990e8cdbed6ccd0dc9d61185db968b8c1f9..62b71b10d5a1b98c7a0b94b100d46829dfac1fc7 100644 (file)
@@ -6,11 +6,13 @@ use rustc_apfloat::{
     Float,
 };
 use rustc_macros::HashStable;
-use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
+use rustc_target::abi::{HasDataLayout, Size};
 
 use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt};
 
-use super::{AllocId, AllocRange, Allocation, InterpResult, Pointer, PointerArithmetic};
+use super::{
+    AllocId, AllocRange, Allocation, InterpResult, Pointer, PointerArithmetic, Provenance,
+};
 
 /// Represents the result of const evaluation via the `eval_to_allocation` query.
 #[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
@@ -47,12 +49,6 @@ pub enum ConstValue<'tcx> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(ConstValue<'_>, 32);
 
-impl From<Scalar> for ConstValue<'tcx> {
-    fn from(s: Scalar) -> Self {
-        Self::Scalar(s)
-    }
-}
-
 impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> {
     type Lifted = ConstValue<'tcx>;
     fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ConstValue<'tcx>> {
@@ -70,7 +66,7 @@ impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> {
 
 impl<'tcx> ConstValue<'tcx> {
     #[inline]
-    pub fn try_to_scalar(&self) -> Option<Scalar> {
+    pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
         match *self {
             ConstValue::ByRef { .. } | ConstValue::Slice { .. } => None,
             ConstValue::Scalar(val) => Some(val),
@@ -120,16 +116,23 @@ impl<'tcx> ConstValue<'tcx> {
 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 16 bytes in
 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
 /// of a simple value or a pointer into another `Allocation`
+///
+/// These variants would be private if there was a convenient way to achieve that in Rust.
+/// Do *not* match on a `Scalar`! Use the various `to_*` methods instead.
 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable)]
-pub enum Scalar<Tag = ()> {
+pub enum Scalar<Tag = AllocId> {
     /// The raw bytes of a simple value.
     Int(ScalarInt),
 
     /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
     /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
     /// relocation and its associated offset together as a `Pointer` here.
-    Ptr(Pointer<Tag>),
+    ///
+    /// We also store the size of the pointer, such that a `Scalar` always knows how big it is.
+    /// The size is always the pointer size of the current target, but this is not information
+    /// that we always have readily available.
+    Ptr(Pointer<Tag>, u8),
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
@@ -137,20 +140,20 @@ static_assert_size!(Scalar, 24);
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
 // all the Miri types.
-impl<Tag: fmt::Debug> fmt::Debug for Scalar<Tag> {
+impl<Tag: Provenance> fmt::Debug for Scalar<Tag> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Scalar::Ptr(ptr) => write!(f, "{:?}", ptr),
+            Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr),
             Scalar::Int(int) => write!(f, "{:?}", int),
         }
     }
 }
 
-impl<Tag: fmt::Debug> fmt::Display for Scalar<Tag> {
+impl<Tag: Provenance> fmt::Display for Scalar<Tag> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Scalar::Ptr(ptr) => write!(f, "pointer to {}", ptr),
-            Scalar::Int { .. } => fmt::Debug::fmt(self, f),
+            Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr),
+            Scalar::Int(int) => write!(f, "{:?}", int),
         }
     }
 }
@@ -169,83 +172,34 @@ impl<Tag> From<Double> for Scalar<Tag> {
     }
 }
 
-impl Scalar<()> {
-    /// Tag this scalar with `new_tag` if it is a pointer, leave it unchanged otherwise.
-    ///
-    /// Used by `MemPlace::replace_tag`.
-    #[inline]
-    pub fn with_tag<Tag>(self, new_tag: Tag) -> Scalar<Tag> {
-        match self {
-            Scalar::Ptr(ptr) => Scalar::Ptr(ptr.with_tag(new_tag)),
-            Scalar::Int(int) => Scalar::Int(int),
-        }
+impl<Tag> From<ScalarInt> for Scalar<Tag> {
+    #[inline(always)]
+    fn from(ptr: ScalarInt) -> Self {
+        Scalar::Int(ptr)
     }
 }
 
-impl<'tcx, Tag> Scalar<Tag> {
+impl<Tag> Scalar<Tag> {
     pub const ZST: Self = Scalar::Int(ScalarInt::ZST);
 
-    /// Erase the tag from the scalar, if any.
-    ///
-    /// Used by error reporting code to avoid having the error type depend on `Tag`.
-    #[inline]
-    pub fn erase_tag(self) -> Scalar {
-        match self {
-            Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_tag()),
-            Scalar::Int(int) => Scalar::Int(int),
-        }
-    }
-
-    #[inline]
-    pub fn null_ptr(cx: &impl HasDataLayout) -> Self {
-        Scalar::Int(ScalarInt::null(cx.data_layout().pointer_size))
-    }
-
     #[inline(always)]
-    fn ptr_op(
-        self,
-        dl: &TargetDataLayout,
-        f_int: impl FnOnce(u64) -> InterpResult<'tcx, u64>,
-        f_ptr: impl FnOnce(Pointer<Tag>) -> InterpResult<'tcx, Pointer<Tag>>,
-    ) -> InterpResult<'tcx, Self> {
-        match self {
-            Scalar::Int(int) => Ok(Scalar::Int(int.ptr_sized_op(dl, f_int)?)),
-            Scalar::Ptr(ptr) => Ok(Scalar::Ptr(f_ptr(ptr)?)),
-        }
-    }
-
-    #[inline]
-    pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
-        let dl = cx.data_layout();
-        self.ptr_op(dl, |int| dl.offset(int, i.bytes()), |ptr| ptr.offset(i, dl))
+    pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
+        Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap())
     }
 
-    #[inline]
-    pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
-        let dl = cx.data_layout();
-        self.ptr_op(
-            dl,
-            |int| Ok(dl.overflowing_offset(int, i.bytes()).0),
-            |ptr| Ok(ptr.wrapping_offset(i, dl)),
-        )
-        .unwrap()
-    }
-
-    #[inline]
-    pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
-        let dl = cx.data_layout();
-        self.ptr_op(dl, |int| dl.signed_offset(int, i), |ptr| ptr.signed_offset(i, dl))
+    /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer).
+    pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self {
+        match ptr.into_parts() {
+            (Some(tag), offset) => Scalar::from_pointer(Pointer::new(tag, offset), cx),
+            (None, offset) => {
+                Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap())
+            }
+        }
     }
 
     #[inline]
-    pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
-        let dl = cx.data_layout();
-        self.ptr_op(
-            dl,
-            |int| Ok(dl.overflowing_signed_offset(int, i).0),
-            |ptr| Ok(ptr.wrapping_signed_offset(i, dl)),
-        )
-        .unwrap()
+    pub fn null_ptr(cx: &impl HasDataLayout) -> Self {
+        Scalar::Int(ScalarInt::null(cx.pointer_size()))
     }
 
     #[inline]
@@ -332,74 +286,71 @@ impl<'tcx, Tag> Scalar<Tag> {
         Scalar::Int(f.into())
     }
 
-    /// This is very rarely the method you want!  You should dispatch on the type
-    /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`.
-    /// This method only exists for the benefit of low-level memory operations
-    /// as well as the implementation of the `force_*` methods.
+    /// This is almost certainly not the method you want!  You should dispatch on the type
+    /// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed.
+    ///
+    /// This method only exists for the benefit of low-level operations that truly need to treat the
+    /// scalar in whatever form it is.
     #[inline]
-    pub fn to_bits_or_ptr(
-        self,
-        target_size: Size,
-        cx: &impl HasDataLayout,
-    ) -> Result<u128, Pointer<Tag>> {
+    pub fn to_bits_or_ptr_internal(self, target_size: Size) -> Result<u128, Pointer<Tag>> {
         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
         match self {
             Scalar::Int(int) => Ok(int.assert_bits(target_size)),
-            Scalar::Ptr(ptr) => {
-                assert_eq!(target_size, cx.data_layout().pointer_size);
+            Scalar::Ptr(ptr, sz) => {
+                assert_eq!(target_size.bytes(), u64::from(sz));
                 Err(ptr)
             }
         }
     }
+}
 
-    /// This method is intentionally private!
-    /// It is just a helper for other methods in this file.
+impl<'tcx, Tag: Provenance> Scalar<Tag> {
+    /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you
+    /// likely want to use instead.
+    ///
+    /// Will perform ptr-to-int casts if needed and possible.
+    /// If that fails, we know the offset is relative, so we return an "erased" Scalar
+    /// (which is useful for error messages but not much else).
     #[inline]
-    fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
-        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
+    pub fn try_to_int(self) -> Result<ScalarInt, Scalar<AllocId>> {
         match self {
-            Scalar::Int(int) => int.to_bits(target_size).map_err(|size| {
-                err_ub!(ScalarSizeMismatch {
-                    target_size: target_size.bytes(),
-                    data_size: size.bytes(),
-                })
-                .into()
-            }),
-            Scalar::Ptr(_) => throw_unsup!(ReadPointerAsBytes),
+            Scalar::Int(int) => Ok(int),
+            Scalar::Ptr(ptr, sz) => {
+                if Tag::OFFSET_IS_ADDR {
+                    Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap())
+                } else {
+                    // We know `offset` is relative, since `OFFSET_IS_ADDR == false`.
+                    let (tag, offset) = ptr.into_parts();
+                    Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id(), offset), sz))
+                }
+            }
         }
     }
 
     #[inline(always)]
-    pub fn assert_bits(self, target_size: Size) -> u128 {
-        self.to_bits(target_size).expect("expected Raw bits but got a Pointer")
-    }
-
-    #[inline]
     pub fn assert_int(self) -> ScalarInt {
-        match self {
-            Scalar::Ptr(_) => bug!("expected an int but got an abstract pointer"),
-            Scalar::Int(int) => int,
-        }
+        self.try_to_int().unwrap()
     }
 
+    /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in
+    /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type.
     #[inline]
-    pub fn assert_ptr(self) -> Pointer<Tag> {
-        match self {
-            Scalar::Ptr(p) => p,
-            Scalar::Int { .. } => bug!("expected a Pointer but got Raw bits"),
-        }
-    }
-
-    /// Do not call this method!  Dispatch based on the type instead.
-    #[inline]
-    pub fn is_bits(self) -> bool {
-        matches!(self, Scalar::Int { .. })
+    pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
+        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
+        self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsBytes))?.to_bits(target_size).map_err(
+            |size| {
+                err_ub!(ScalarSizeMismatch {
+                    target_size: target_size.bytes(),
+                    data_size: size.bytes(),
+                })
+                .into()
+            },
+        )
     }
 
-    /// Do not call this method!  Dispatch based on the type instead.
-    #[inline]
-    pub fn is_ptr(self) -> bool {
-        matches!(self, Scalar::Ptr(_))
+    #[inline(always)]
+    pub fn assert_bits(self, target_size: Size) -> u128 {
+        self.to_bits(target_size).unwrap()
     }
 
     pub fn to_bool(self) -> InterpResult<'tcx, bool> {
@@ -507,22 +458,8 @@ impl<'tcx, Tag> Scalar<Tag> {
     }
 }
 
-impl<Tag> From<Pointer<Tag>> for Scalar<Tag> {
-    #[inline(always)]
-    fn from(ptr: Pointer<Tag>) -> Self {
-        Scalar::Ptr(ptr)
-    }
-}
-
-impl<Tag> From<ScalarInt> for Scalar<Tag> {
-    #[inline(always)]
-    fn from(ptr: ScalarInt) -> Self {
-        Scalar::Int(ptr)
-    }
-}
-
 #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)]
-pub enum ScalarMaybeUninit<Tag = ()> {
+pub enum ScalarMaybeUninit<Tag = AllocId> {
     Scalar(Scalar<Tag>),
     Uninit,
 }
@@ -537,16 +474,9 @@ impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> {
     }
 }
 
-impl<Tag> From<Pointer<Tag>> for ScalarMaybeUninit<Tag> {
-    #[inline(always)]
-    fn from(s: Pointer<Tag>) -> Self {
-        ScalarMaybeUninit::Scalar(s.into())
-    }
-}
-
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
 // all the Miri types.
-impl<Tag: fmt::Debug> fmt::Debug for ScalarMaybeUninit<Tag> {
+impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             ScalarMaybeUninit::Uninit => write!(f, "<uninitialized>"),
@@ -555,7 +485,7 @@ impl<Tag: fmt::Debug> fmt::Debug for ScalarMaybeUninit<Tag> {
     }
 }
 
-impl<Tag: fmt::Debug> fmt::Display for ScalarMaybeUninit<Tag> {
+impl<Tag: Provenance> fmt::Display for ScalarMaybeUninit<Tag> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"),
@@ -564,16 +494,15 @@ impl<Tag: fmt::Debug> fmt::Display for ScalarMaybeUninit<Tag> {
     }
 }
 
-impl<'tcx, Tag> ScalarMaybeUninit<Tag> {
-    /// Erase the tag from the scalar, if any.
-    ///
-    /// Used by error reporting code to avoid having the error type depend on `Tag`.
+impl<Tag> ScalarMaybeUninit<Tag> {
     #[inline]
-    pub fn erase_tag(self) -> ScalarMaybeUninit {
-        match self {
-            ScalarMaybeUninit::Scalar(s) => ScalarMaybeUninit::Scalar(s.erase_tag()),
-            ScalarMaybeUninit::Uninit => ScalarMaybeUninit::Uninit,
-        }
+    pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
+        ScalarMaybeUninit::Scalar(Scalar::from_pointer(ptr, cx))
+    }
+
+    #[inline]
+    pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self {
+        ScalarMaybeUninit::Scalar(Scalar::from_maybe_pointer(ptr, cx))
     }
 
     #[inline]
@@ -583,7 +512,9 @@ impl<'tcx, Tag> ScalarMaybeUninit<Tag> {
             ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)),
         }
     }
+}
 
+impl<'tcx, Tag: Provenance> ScalarMaybeUninit<Tag> {
     #[inline(always)]
     pub fn to_bool(self) -> InterpResult<'tcx, bool> {
         self.check_init()?.to_bool()