X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc%2Fmir%2Finterpret%2Fvalue.rs;h=1909f3cb998be90584d99480dd7c2e837ed6fd06;hb=3836573ae4610f75d41d467e35d855efd6b000b5;hp=72545f23f8e2bf3ac67a39ebef68f6afd60c0e76;hpb=5187be620c76a313a19b9b596e1bce3a80a345dd;p=rust.git diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 72545f23f8e..1909f3cb998 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -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 { RustcEncodable, RustcDecodable, Hash, HashStable)] pub enum Scalar { /// 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, "") } 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 fmt::Display for Scalar { 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(self, new_tag: Tag) -> Scalar { 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(self) -> Scalar - where Tag: Default - { - self.with_tag(Tag::default()) - } } impl<'tcx, Tag> Scalar { + /// 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, 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, 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> { + 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> { + pub fn to_ptr(self) -> InterpResult<'tcx, Pointer> { 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> { #[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(self, new_tag: Tag) -> ScalarMaybeUndef { - match self { - ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.with_tag(new_tag)), - ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef, - } - } - - #[inline(always)] - pub fn with_default_tag(self) -> ScalarMaybeUndef - where Tag: Default - { - self.with_tag(Tag::default()) - } -} - impl<'tcx, Tag> ScalarMaybeUndef { + /// 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> { + pub fn not_undef(self) -> InterpResult<'static, Scalar> { 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> { } #[inline(always)] - pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { + pub fn to_ptr(self) -> InterpResult<'tcx, Pointer> { 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) } }