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)]
/// Not using the enum `Value` to encode that this must not be `Undef`.
Scalar(Scalar),
- /// Used only for slices and strings (`&[T]`, `&str`, `*const [T]`, `*mut str`, `Box<str>`,
- /// etc.).
- ///
- /// Empty slices don't necessarily have an address backed by an `AllocId`, thus we also need to
- /// enable integer pointers. The `Scalar` type covers exactly those two cases. While we could
- /// create dummy-`AllocId`s, the additional code effort for the conversions doesn't seem worth
- /// it.
- Slice(Scalar, u64),
+ /// Used only for `&[u8]` and `&str`
+ Slice {
+ data: &'tcx Allocation,
+ start: usize,
+ end: usize,
+ },
/// An allocation together with a pointer into the allocation.
/// Invariant: the pointer's `AllocId` resolves to the allocation.
}
#[cfg(target_arch = "x86_64")]
-static_assert_size!(ConstValue<'_>, 40);
+static_assert_size!(ConstValue<'_>, 32);
impl<'tcx> ConstValue<'tcx> {
#[inline]
ConstValue::Placeholder(_) |
ConstValue::ByRef(..) |
ConstValue::Unevaluated(..) |
- ConstValue::Slice(..) => None,
+ ConstValue::Slice { .. } => None,
ConstValue::Scalar(val) => Some(val),
}
}
pub fn try_to_ptr(&self) -> Option<Pointer> {
self.try_to_scalar()?.to_ptr().ok()
}
-
- #[inline]
- pub fn new_slice(
- val: Scalar,
- len: u64,
- ) -> Self {
- ConstValue::Slice(val, len)
- }
}
/// A `Scalar` represents an immediate, primitive value existing outside of a
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
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)
}
}
}
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,
})
}
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,
}
}
}
#[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,
})
}
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,
}
}
}
}
- /// 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,
}
#[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,
}
#[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]
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: f32) -> Self {
- Scalar::Bits { bits: f.to_bits() as u128, size: 4 }
+ 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 {
- Scalar::Bits { bits: f.to_bits() as u128, size: 8 }
+ 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::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(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, "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),
}
}
#[inline]
pub fn is_bits(self) -> bool {
match self {
- Scalar::Bits { .. } => true,
+ Scalar::Raw { .. } => true,
_ => false,
}
}
}
}
- 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),
}
}
- 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)
}
}