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)]
Scalar::Ptr(ptr) =>
write!(f, "{:?}", ptr),
&Scalar::Raw { data, size } => {
+ Scalar::check_data(data, size);
if size == 0 {
- assert_eq!(data, 0, "ZST value must be 0");
write!(f, "<ZST>")
} else {
- assert_eq!(truncate(data, Size::from_bytes(size as u64)), data,
- "Scalar value {:#x} exceeds size of {} bytes", data, size);
// Format as hex number wide enough to fit any value of the given `size`.
// 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)
}
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::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 {
}
#[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::Raw { data, size } => {
}
#[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::Raw { data, size } => {
}
}
- /// 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 {
#[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());
+ 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 }
}
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());
+ 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: f32) -> Self {
+ pub fn from_f32(f: Single) -> Self {
Scalar::Raw { data: f.to_bits() as u128, size: 4 }
}
#[inline]
- pub fn from_f64(f: f64) -> Self {
+ pub fn from_f64(f: Double) -> Self {
Scalar::Raw { data: f.to_bits() as u128, size: 8 }
}
#[inline]
- pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
+ 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) -> InterpResult<'tcx, u128> {
match self {
Scalar::Raw { data, size } => {
assert_eq!(target_size.bytes(), size as u64);
- assert_ne!(size, 0, "to_bits cannot be used with zsts");
+ 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::Raw { data: 0, .. } => err!(InvalidNullPointerUsage),
Scalar::Raw { .. } => err!(ReadBytesAsPointer),
}
}
- pub fn to_bool(self) -> EvalResult<'tcx, bool> {
+ pub fn to_bool(self) -> InterpResult<'tcx, bool> {
match self {
Scalar::Raw { data: 0, size: 1 } => Ok(false),
Scalar::Raw { data: 1, size: 1 } => Ok(true),
}
}
- 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),
}
}
- 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))
}
}
}
}
-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
{
}
#[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))),
}
#[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)
}
}