]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/mir/interpret/value.rs
Scalar: only convert to/from soft-float types, not to/from hard-floats
[rust.git] / src / librustc / mir / interpret / value.rs
index 72545f23f8e2bf3ac67a39ebef68f6afd60c0e76..1909f3cb998be90584d99480dd7c2e837ed6fd06 100644 (file)
@@ -1,11 +1,12 @@
 use std::fmt;
 use rustc_macros::HashStable;
+use rustc_apfloat::{Float, ieee::{Double, Single}};
 
 use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef};
 use crate::ty::PlaceholderConst;
 use crate::hir::def_id::DefId;
 
-use super::{EvalResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate};
+use super::{InterpResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate};
 
 /// Represents the result of a raw const operation, pre-validation.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)]
@@ -87,11 +88,11 @@ pub fn try_to_ptr(&self) -> Option<Pointer> {
          RustcEncodable, RustcDecodable, Hash, HashStable)]
 pub enum Scalar<Tag=(), Id=AllocId> {
     /// The raw bytes of a simple value.
-    Bits {
-        /// The first `size` bytes are the value.
+    Raw {
+        /// The first `size` bytes of `data` are the value.
         /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
+        data: u128,
         size: u8,
-        bits: u128,
     },
 
     /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
@@ -108,16 +109,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Scalar::Ptr(ptr) =>
                 write!(f, "{:?}", ptr),
-            &Scalar::Bits { bits, size } => {
+            &Scalar::Raw { data, size } => {
+                Scalar::check_data(data, size);
                 if size == 0 {
-                    assert_eq!(bits, 0, "ZST value must be 0");
                     write!(f, "<ZST>")
                 } else {
-                    assert_eq!(truncate(bits, Size::from_bytes(size as u64)), bits,
-                            "Scalar value {:#x} exceeds size of {} bytes", bits, size);
                     // Format as hex number wide enough to fit any value of the given `size`.
-                    // So bits=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
-                    write!(f, "0x{:>0width$x}", bits, width=(size*2) as usize)
+                    // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
+                    write!(f, "0x{:>0width$x}", data, width=(size*2) as usize)
                 }
             }
         }
@@ -128,58 +127,63 @@ impl<Tag> fmt::Display for Scalar<Tag> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Scalar::Ptr(_) => write!(f, "a pointer"),
-            Scalar::Bits { bits, .. } => write!(f, "{}", bits),
+            Scalar::Raw { data, .. } => write!(f, "{}", data),
         }
     }
 }
 
 impl<'tcx> Scalar<()> {
+    #[inline(always)]
+    fn check_data(data: u128, size: u8) {
+        debug_assert_eq!(truncate(data, Size::from_bytes(size as u64)), data,
+                         "Scalar value {:#x} exceeds size of {} bytes", data, size);
+    }
+
+    /// 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::Bits { bits, size } => Scalar::Bits { bits, size },
+            Scalar::Raw { data, size } => Scalar::Raw { data, size },
         }
     }
-
-    #[inline(always)]
-    pub fn with_default_tag<Tag>(self) -> Scalar<Tag>
-        where Tag: Default
-    {
-        self.with_tag(Tag::default())
-    }
 }
 
 impl<'tcx, Tag> Scalar<Tag> {
+    /// 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::Bits { bits, size } => Scalar::Bits { bits, size },
+            Scalar::Raw { data, size } => Scalar::Raw { data, size },
         }
     }
 
     #[inline]
     pub fn ptr_null(cx: &impl HasDataLayout) -> Self {
-        Scalar::Bits {
-            bits: 0,
+        Scalar::Raw {
+            data: 0,
             size: cx.data_layout().pointer_size.bytes() as u8,
         }
     }
 
     #[inline]
     pub fn zst() -> Self {
-        Scalar::Bits { bits: 0, size: 0 }
+        Scalar::Raw { data: 0, size: 0 }
     }
 
     #[inline]
-    pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
+    pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
         let dl = cx.data_layout();
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(size as u64, dl.pointer_size.bytes());
-                Ok(Scalar::Bits {
-                    bits: dl.offset(bits as u64, i.bytes())? as u128,
+                Ok(Scalar::Raw {
+                    data: dl.offset(data as u64, i.bytes())? as u128,
                     size,
                 })
             }
@@ -191,10 +195,10 @@ pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Se
     pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
         let dl = cx.data_layout();
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(size as u64, dl.pointer_size.bytes());
-                Scalar::Bits {
-                    bits: dl.overflowing_offset(bits as u64, i.bytes()).0 as u128,
+                Scalar::Raw {
+                    data: dl.overflowing_offset(data as u64, i.bytes()).0 as u128,
                     size,
                 }
             }
@@ -203,13 +207,13 @@ pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
     }
 
     #[inline]
-    pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
+    pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
         let dl = cx.data_layout();
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(size as u64, dl.pointer_size().bytes());
-                Ok(Scalar::Bits {
-                    bits: dl.signed_offset(bits as u64, i)? as u128,
+                Ok(Scalar::Raw {
+                    data: dl.signed_offset(data as u64, i)? as u128,
                     size,
                 })
             }
@@ -221,10 +225,10 @@ pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'t
     pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
         let dl = cx.data_layout();
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(size as u64, dl.pointer_size.bytes());
-                Scalar::Bits {
-                    bits: dl.overflowing_signed_offset(bits as u64, i128::from(i)).0 as u128,
+                Scalar::Raw {
+                    data: dl.overflowing_signed_offset(data as u64, i128::from(i)).0 as u128,
                     size,
                 }
             }
@@ -232,14 +236,14 @@ pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self
         }
     }
 
-    /// Returns this pointers offset from the allocation base, or from NULL (for
+    /// Returns this pointer's offset from the allocation base, or from NULL (for
     /// integer pointers).
     #[inline]
     pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(size as u64, cx.pointer_size().bytes());
-                Size::from_bytes(bits as u64)
+                Size::from_bytes(data as u64)
             }
             Scalar::Ptr(ptr) => ptr.offset,
         }
@@ -248,9 +252,9 @@ pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
     #[inline]
     pub fn is_null_ptr(self, cx: &impl HasDataLayout) -> bool {
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
-                bits == 0
+                data == 0
             },
             Scalar::Ptr(_) => false,
         }
@@ -258,20 +262,22 @@ pub fn is_null_ptr(self, cx: &impl HasDataLayout) -> bool {
 
     #[inline]
     pub fn from_bool(b: bool) -> Self {
-        Scalar::Bits { bits: b as u128, size: 1 }
+        Scalar::Raw { data: b as u128, size: 1 }
     }
 
     #[inline]
     pub fn from_char(c: char) -> Self {
-        Scalar::Bits { bits: c as u128, size: 4 }
+        Scalar::Raw { data: c as u128, size: 4 }
     }
 
     #[inline]
     pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
         let i = i.into();
-        debug_assert_eq!(truncate(i, size), i,
-                         "Unsigned value {} does not fit in {} bits", i, size.bits());
-        Scalar::Bits { bits: i, size: size.bytes() as u8 }
+        assert_eq!(
+            truncate(i, size), i,
+            "Unsigned value {:#x} does not fit in {} bits", i, size.bits()
+        );
+        Scalar::Raw { data: i, size: size.bytes() as u8 }
     }
 
     #[inline]
@@ -279,38 +285,61 @@ pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
         let i = i.into();
         // `into` performed sign extension, we have to truncate
         let truncated = truncate(i as u128, size);
-        debug_assert_eq!(sign_extend(truncated, size) as i128, i,
-                         "Signed value {} does not fit in {} bits", i, size.bits());
-        Scalar::Bits { bits: truncated, size: size.bytes() as u8 }
+        assert_eq!(
+            sign_extend(truncated, size) as i128, i,
+            "Signed value {:#x} does not fit in {} bits", i, size.bits()
+        );
+        Scalar::Raw { data: truncated, size: size.bytes() as u8 }
+    }
+
+    #[inline]
+    pub fn from_f32(f: Single) -> Self {
+        Scalar::Raw { data: f.to_bits() as u128, size: 4 }
     }
 
     #[inline]
-    pub fn from_f32(f: f32) -> Self {
-        Scalar::Bits { bits: f.to_bits() as u128, size: 4 }
+    pub fn from_f64(f: Double) -> Self {
+        Scalar::Raw { data: f.to_bits() as u128, size: 8 }
     }
 
     #[inline]
-    pub fn from_f64(f: f64) -> Self {
-        Scalar::Bits { bits: f.to_bits() as u128, size: 8 }
+    pub fn to_bits_or_ptr(
+        self,
+        target_size: Size,
+        cx: &impl HasDataLayout,
+    ) -> Result<u128, Pointer<Tag>> {
+        match self {
+            Scalar::Raw { data, size } => {
+                assert_eq!(target_size.bytes(), size as u64);
+                assert_ne!(size, 0, "you should never look at the bits of a ZST");
+                Scalar::check_data(data, size);
+                Ok(data)
+            }
+            Scalar::Ptr(ptr) => {
+                assert_eq!(target_size, cx.data_layout().pointer_size);
+                Err(ptr)
+            }
+        }
     }
 
     #[inline]
-    pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
+    pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
         match self {
-            Scalar::Bits { bits, size } => {
+            Scalar::Raw { data, size } => {
                 assert_eq!(target_size.bytes(), size as u64);
-                assert_ne!(size, 0, "to_bits cannot be used with zsts");
-                Ok(bits)
+                assert_ne!(size, 0, "you should never look at the bits of a ZST");
+                Scalar::check_data(data, size);
+                Ok(data)
             }
             Scalar::Ptr(_) => err!(ReadPointerAsBytes),
         }
     }
 
     #[inline]
-    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
+    pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
         match self {
-            Scalar::Bits { bits: 0, .. } => err!(InvalidNullPointerUsage),
-            Scalar::Bits { .. } => err!(ReadBytesAsPointer),
+            Scalar::Raw { data: 0, .. } => err!(InvalidNullPointerUsage),
+            Scalar::Raw { .. } => err!(ReadBytesAsPointer),
             Scalar::Ptr(p) => Ok(p),
         }
     }
@@ -318,7 +347,7 @@ pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
     #[inline]
     pub fn is_bits(self) -> bool {
         match self {
-            Scalar::Bits { .. } => true,
+            Scalar::Raw { .. } => true,
             _ => false,
         }
     }
@@ -331,15 +360,15 @@ pub fn is_ptr(self) -> bool {
         }
     }
 
-    pub fn to_bool(self) -> EvalResult<'tcx, bool> {
+    pub fn to_bool(self) -> InterpResult<'tcx, bool> {
         match self {
-            Scalar::Bits { bits: 0, size: 1 } => Ok(false),
-            Scalar::Bits { bits: 1, size: 1 } => Ok(true),
+            Scalar::Raw { data: 0, size: 1 } => Ok(false),
+            Scalar::Raw { data: 1, size: 1 } => Ok(true),
             _ => err!(InvalidBool),
         }
     }
 
-    pub fn to_char(self) -> EvalResult<'tcx, char> {
+    pub fn to_char(self) -> InterpResult<'tcx, char> {
         let val = self.to_u32()?;
         match ::std::char::from_u32(val) {
             Some(c) => Ok(c),
@@ -347,72 +376,67 @@ pub fn to_char(self) -> EvalResult<'tcx, char> {
         }
     }
 
-    pub fn to_u8(self) -> EvalResult<'static, u8> {
+    pub fn to_u8(self) -> InterpResult<'static, u8> {
         let sz = Size::from_bits(8);
         let b = self.to_bits(sz)?;
-        assert_eq!(b as u8 as u128, b);
         Ok(b as u8)
     }
 
-    pub fn to_u32(self) -> EvalResult<'static, u32> {
+    pub fn to_u32(self) -> InterpResult<'static, u32> {
         let sz = Size::from_bits(32);
         let b = self.to_bits(sz)?;
-        assert_eq!(b as u32 as u128, b);
         Ok(b as u32)
     }
 
-    pub fn to_u64(self) -> EvalResult<'static, u64> {
+    pub fn to_u64(self) -> InterpResult<'static, u64> {
         let sz = Size::from_bits(64);
         let b = self.to_bits(sz)?;
-        assert_eq!(b as u64 as u128, b);
         Ok(b as u64)
     }
 
-    pub fn to_usize(self, cx: &impl HasDataLayout) -> EvalResult<'static, u64> {
+    pub fn to_usize(self, cx: &impl HasDataLayout) -> InterpResult<'static, u64> {
         let b = self.to_bits(cx.data_layout().pointer_size)?;
-        assert_eq!(b as u64 as u128, b);
         Ok(b as u64)
     }
 
-    pub fn to_i8(self) -> EvalResult<'static, i8> {
+    pub fn to_i8(self) -> InterpResult<'static, i8> {
         let sz = Size::from_bits(8);
         let b = self.to_bits(sz)?;
         let b = sign_extend(b, sz) as i128;
-        assert_eq!(b as i8 as i128, b);
         Ok(b as i8)
     }
 
-    pub fn to_i32(self) -> EvalResult<'static, i32> {
+    pub fn to_i32(self) -> InterpResult<'static, i32> {
         let sz = Size::from_bits(32);
         let b = self.to_bits(sz)?;
         let b = sign_extend(b, sz) as i128;
-        assert_eq!(b as i32 as i128, b);
         Ok(b as i32)
     }
 
-    pub fn to_i64(self) -> EvalResult<'static, i64> {
+    pub fn to_i64(self) -> InterpResult<'static, i64> {
         let sz = Size::from_bits(64);
         let b = self.to_bits(sz)?;
         let b = sign_extend(b, sz) as i128;
-        assert_eq!(b as i64 as i128, b);
         Ok(b as i64)
     }
 
-    pub fn to_isize(self, cx: &impl HasDataLayout) -> EvalResult<'static, i64> {
-        let b = self.to_bits(cx.data_layout().pointer_size)?;
-        let b = sign_extend(b, cx.data_layout().pointer_size) as i128;
-        assert_eq!(b as i64 as i128, b);
+    pub fn to_isize(self, cx: &impl HasDataLayout) -> InterpResult<'static, i64> {
+        let sz = cx.data_layout().pointer_size;
+        let b = self.to_bits(sz)?;
+        let b = sign_extend(b, sz) as i128;
         Ok(b as i64)
     }
 
     #[inline]
-    pub fn to_f32(self) -> EvalResult<'static, f32> {
-        Ok(f32::from_bits(self.to_u32()?))
+    pub fn to_f32(self) -> InterpResult<'static, Single> {
+        // Going through `u32` to check size and truncation.
+        Ok(Single::from_bits(self.to_u32()? as u128))
     }
 
     #[inline]
-    pub fn to_f64(self) -> EvalResult<'static, f64> {
-        Ok(f64::from_bits(self.to_u64()?))
+    pub fn to_f64(self) -> InterpResult<'static, Double> {
+        // Going through `u64` to check size and truncation.
+        Ok(Double::from_bits(self.to_u64()? as u128))
     }
 }
 
@@ -454,24 +478,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-impl<'tcx> ScalarMaybeUndef<()> {
-    #[inline]
-    pub fn with_tag<Tag>(self, new_tag: Tag) -> ScalarMaybeUndef<Tag> {
-        match self {
-            ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.with_tag(new_tag)),
-            ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef,
-        }
-    }
-
-    #[inline(always)]
-    pub fn with_default_tag<Tag>(self) -> ScalarMaybeUndef<Tag>
-        where Tag: Default
-    {
-        self.with_tag(Tag::default())
-    }
-}
-
 impl<'tcx, Tag> ScalarMaybeUndef<Tag> {
+    /// 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) -> ScalarMaybeUndef
     {
@@ -482,7 +492,7 @@ pub fn erase_tag(self) -> ScalarMaybeUndef
     }
 
     #[inline]
-    pub fn not_undef(self) -> EvalResult<'static, Scalar<Tag>> {
+    pub fn not_undef(self) -> InterpResult<'static, Scalar<Tag>> {
         match self {
             ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
             ScalarMaybeUndef::Undef => err!(ReadUndefBytes(Size::from_bytes(0))),
@@ -490,72 +500,72 @@ pub fn not_undef(self) -> EvalResult<'static, Scalar<Tag>> {
     }
 
     #[inline(always)]
-    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
+    pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
         self.not_undef()?.to_ptr()
     }
 
     #[inline(always)]
-    pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
+    pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
         self.not_undef()?.to_bits(target_size)
     }
 
     #[inline(always)]
-    pub fn to_bool(self) -> EvalResult<'tcx, bool> {
+    pub fn to_bool(self) -> InterpResult<'tcx, bool> {
         self.not_undef()?.to_bool()
     }
 
     #[inline(always)]
-    pub fn to_char(self) -> EvalResult<'tcx, char> {
+    pub fn to_char(self) -> InterpResult<'tcx, char> {
         self.not_undef()?.to_char()
     }
 
     #[inline(always)]
-    pub fn to_f32(self) -> EvalResult<'tcx, f32> {
+    pub fn to_f32(self) -> InterpResult<'tcx, Single> {
         self.not_undef()?.to_f32()
     }
 
     #[inline(always)]
-    pub fn to_f64(self) -> EvalResult<'tcx, f64> {
+    pub fn to_f64(self) -> InterpResult<'tcx, Double> {
         self.not_undef()?.to_f64()
     }
 
     #[inline(always)]
-    pub fn to_u8(self) -> EvalResult<'tcx, u8> {
+    pub fn to_u8(self) -> InterpResult<'tcx, u8> {
         self.not_undef()?.to_u8()
     }
 
     #[inline(always)]
-    pub fn to_u32(self) -> EvalResult<'tcx, u32> {
+    pub fn to_u32(self) -> InterpResult<'tcx, u32> {
         self.not_undef()?.to_u32()
     }
 
     #[inline(always)]
-    pub fn to_u64(self) -> EvalResult<'tcx, u64> {
+    pub fn to_u64(self) -> InterpResult<'tcx, u64> {
         self.not_undef()?.to_u64()
     }
 
     #[inline(always)]
-    pub fn to_usize(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, u64> {
+    pub fn to_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
         self.not_undef()?.to_usize(cx)
     }
 
     #[inline(always)]
-    pub fn to_i8(self) -> EvalResult<'tcx, i8> {
+    pub fn to_i8(self) -> InterpResult<'tcx, i8> {
         self.not_undef()?.to_i8()
     }
 
     #[inline(always)]
-    pub fn to_i32(self) -> EvalResult<'tcx, i32> {
+    pub fn to_i32(self) -> InterpResult<'tcx, i32> {
         self.not_undef()?.to_i32()
     }
 
     #[inline(always)]
-    pub fn to_i64(self) -> EvalResult<'tcx, i64> {
+    pub fn to_i64(self) -> InterpResult<'tcx, i64> {
         self.not_undef()?.to_i64()
     }
 
     #[inline(always)]
-    pub fn to_isize(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, i64> {
+    pub fn to_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> {
         self.not_undef()?.to_isize(cx)
     }
 }