2 ieee
::{Double, Single}
,
5 use rustc_macros
::HashStable
;
9 layout
::{HasDataLayout, Size}
,
13 use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}
;
15 /// Represents the result of a raw const operation, pre-validation.
16 #[derive(Clone, HashStable)]
17 pub struct RawConst
<'tcx
> {
18 // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
19 // (so you can use `AllocMap::unwrap_memory`).
20 pub alloc_id
: AllocId
,
24 /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
25 /// array length computations, enum discriminants and the pattern matching logic.
39 pub enum ConstValue
<'tcx
> {
40 /// Used only for types with `layout::abi::Scalar` ABI and ZSTs.
42 /// Not using the enum `Value` to encode that this must not be `Undef`.
45 /// Used only for `&[u8]` and `&str`
46 Slice { data: &'tcx Allocation, start: usize, end: usize }
,
48 /// A value not represented/representable by `Scalar` or `Slice`
50 /// The backing memory of the value, may contain more memory than needed for just the value
51 /// in order to share `Allocation`s between values
52 alloc
: &'tcx Allocation
,
53 /// Offset into `alloc`
58 #[cfg(target_arch = "x86_64")]
59 static_assert_size
!(ConstValue
<'_
>, 32);
61 impl<'tcx
> ConstValue
<'tcx
> {
63 pub fn try_to_scalar(&self) -> Option
<Scalar
> {
65 ConstValue
::ByRef { .. }
| ConstValue
::Slice { .. }
=> None
,
66 ConstValue
::Scalar(val
) => Some(val
),
70 pub fn try_to_bits(&self, size
: Size
) -> Option
<u128
> {
71 self.try_to_scalar()?
.to_bits(size
).ok()
74 pub fn try_to_bits_for_ty(
77 param_env
: ParamEnv
<'tcx
>,
80 let size
= tcx
.layout_of(param_env
.with_reveal_all().and(ty
)).ok()?
.size
;
81 self.try_to_bits(size
)
84 pub fn from_bool(b
: bool
) -> Self {
85 ConstValue
::Scalar(Scalar
::from_bool(b
))
88 pub fn from_u64(i
: u64) -> Self {
89 ConstValue
::Scalar(Scalar
::from_u64(i
))
92 pub fn from_machine_usize(i
: u64, cx
: &impl HasDataLayout
) -> Self {
93 ConstValue
::Scalar(Scalar
::from_machine_usize(i
, cx
))
97 /// A `Scalar` represents an immediate, primitive value existing outside of a
98 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
99 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
100 /// of a simple value or a pointer into another `Allocation`
113 pub enum Scalar
<Tag
= (), Id
= AllocId
> {
114 /// The raw bytes of a simple value.
116 /// The first `size` bytes of `data` are the value.
117 /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
122 /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
123 /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
124 /// relocation and its associated offset together as a `Pointer` here.
125 Ptr(Pointer
<Tag
, Id
>),
128 #[cfg(target_arch = "x86_64")]
129 static_assert_size
!(Scalar
, 24);
131 impl<Tag
: fmt
::Debug
, Id
: fmt
::Debug
> fmt
::Debug
for Scalar
<Tag
, Id
> {
132 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
134 Scalar
::Ptr(ptr
) => write
!(f
, "{:?}", ptr
),
135 &Scalar
::Raw { data, size }
=> {
136 Scalar
::check_data(data
, size
);
140 // Format as hex number wide enough to fit any value of the given `size`.
141 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
142 write
!(f
, "0x{:>0width$x}", data
, width
= (size
* 2) as usize)
149 impl<Tag
> fmt
::Display
for Scalar
<Tag
> {
150 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
152 Scalar
::Ptr(_
) => write
!(f
, "a pointer"),
153 Scalar
::Raw { data, .. }
=> write
!(f
, "{}", data
),
158 impl<Tag
> From
<Single
> for Scalar
<Tag
> {
160 fn from(f
: Single
) -> Self {
165 impl<Tag
> From
<Double
> for Scalar
<Tag
> {
167 fn from(f
: Double
) -> Self {
173 /// Make sure the `data` fits in `size`.
174 /// This is guaranteed by all constructors here, but since the enum variants are public,
175 /// it could still be violated (even though no code outside this file should
176 /// construct `Scalar`s).
178 fn check_data(data
: u128
, size
: u8) {
180 truncate(data
, Size
::from_bytes(size
as u64)),
182 "Scalar value {:#x} exceeds size of {} bytes",
188 /// Tag this scalar with `new_tag` if it is a pointer, leave it unchanged otherwise.
190 /// Used by `MemPlace::replace_tag`.
192 pub fn with_tag
<Tag
>(self, new_tag
: Tag
) -> Scalar
<Tag
> {
194 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.with_tag(new_tag
)),
195 Scalar
::Raw { data, size }
=> Scalar
::Raw { data, size }
,
200 impl<'tcx
, Tag
> Scalar
<Tag
> {
201 /// Erase the tag from the scalar, if any.
203 /// Used by error reporting code to avoid having the error type depend on `Tag`.
205 pub fn erase_tag(self) -> Scalar
{
207 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.erase_tag()),
208 Scalar
::Raw { data, size }
=> Scalar
::Raw { data, size }
,
213 pub fn ptr_null(cx
: &impl HasDataLayout
) -> Self {
214 Scalar
::Raw { data: 0, size: cx.data_layout().pointer_size.bytes() as u8 }
218 pub fn zst() -> Self {
219 Scalar
::Raw { data: 0, size: 0 }
223 pub fn ptr_offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, Self> {
224 let dl
= cx
.data_layout();
226 Scalar
::Raw { data, size }
=> {
227 assert_eq
!(size
as u64, dl
.pointer_size
.bytes());
228 Ok(Scalar
::Raw { data: dl.offset(data as u64, i.bytes())? as u128, size }
)
230 Scalar
::Ptr(ptr
) => ptr
.offset(i
, dl
).map(Scalar
::Ptr
),
235 pub fn ptr_wrapping_offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> Self {
236 let dl
= cx
.data_layout();
238 Scalar
::Raw { data, size }
=> {
239 assert_eq
!(size
as u64, dl
.pointer_size
.bytes());
240 Scalar
::Raw { data: dl.overflowing_offset(data as u64, i.bytes()).0 as u128, size }
242 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.wrapping_offset(i
, dl
)),
247 pub fn ptr_signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, Self> {
248 let dl
= cx
.data_layout();
250 Scalar
::Raw { data, size }
=> {
251 assert_eq
!(size
as u64, dl
.pointer_size().bytes());
252 Ok(Scalar
::Raw { data: dl.signed_offset(data as u64, i)? as u128, size }
)
254 Scalar
::Ptr(ptr
) => ptr
.signed_offset(i
, dl
).map(Scalar
::Ptr
),
259 pub fn ptr_wrapping_signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> Self {
260 let dl
= cx
.data_layout();
262 Scalar
::Raw { data, size }
=> {
263 assert_eq
!(size
as u64, dl
.pointer_size
.bytes());
265 data
: dl
.overflowing_signed_offset(data
as u64, i128
::from(i
)).0 as u128
,
269 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.wrapping_signed_offset(i
, dl
)),
274 pub fn from_bool(b
: bool
) -> Self {
275 Scalar
::Raw { data: b as u128, size: 1 }
279 pub fn from_char(c
: char) -> Self {
280 Scalar
::Raw { data: c as u128, size: 4 }
284 pub fn try_from_uint(i
: impl Into
<u128
>, size
: Size
) -> Option
<Self> {
286 if truncate(i
, size
) == i
{
287 Some(Scalar
::Raw { data: i, size: size.bytes() as u8 }
)
294 pub fn from_uint(i
: impl Into
<u128
>, size
: Size
) -> Self {
296 Self::try_from_uint(i
, size
)
297 .unwrap_or_else(|| bug
!("Unsigned value {:#x} does not fit in {} bits", i
, size
.bits()))
301 pub fn from_u8(i
: u8) -> Self {
302 Scalar
::Raw { data: i as u128, size: 1 }
306 pub fn from_u16(i
: u16) -> Self {
307 Scalar
::Raw { data: i as u128, size: 2 }
311 pub fn from_u32(i
: u32) -> Self {
312 Scalar
::Raw { data: i as u128, size: 4 }
316 pub fn from_u64(i
: u64) -> Self {
317 Scalar
::Raw { data: i as u128, size: 8 }
321 pub fn from_machine_usize(i
: u64, cx
: &impl HasDataLayout
) -> Self {
322 Self::from_uint(i
, cx
.data_layout().pointer_size
)
326 pub fn try_from_int(i
: impl Into
<i128
>, size
: Size
) -> Option
<Self> {
328 // `into` performed sign extension, we have to truncate
329 let truncated
= truncate(i
as u128
, size
);
330 if sign_extend(truncated
, size
) as i128
== i
{
331 Some(Scalar
::Raw { data: truncated, size: size.bytes() as u8 }
)
338 pub fn from_int(i
: impl Into
<i128
>, size
: Size
) -> Self {
340 Self::try_from_int(i
, size
)
341 .unwrap_or_else(|| bug
!("Signed value {:#x} does not fit in {} bits", i
, size
.bits()))
345 pub fn from_machine_isize(i
: i64, cx
: &impl HasDataLayout
) -> Self {
346 Self::from_int(i
, cx
.data_layout().pointer_size
)
350 pub fn from_f32(f
: Single
) -> Self {
351 // We trust apfloat to give us properly truncated data.
352 Scalar
::Raw { data: f.to_bits(), size: 4 }
356 pub fn from_f64(f
: Double
) -> Self {
357 // We trust apfloat to give us properly truncated data.
358 Scalar
::Raw { data: f.to_bits(), size: 8 }
361 /// This is very rarely the method you want! You should dispatch on the type
362 /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`.
363 /// This method only exists for the benefit of low-level memory operations
364 /// as well as the implementation of the `force_*` methods.
366 pub fn to_bits_or_ptr(
369 cx
: &impl HasDataLayout
,
370 ) -> Result
<u128
, Pointer
<Tag
>> {
371 assert_ne
!(target_size
.bytes(), 0, "you should never look at the bits of a ZST");
373 Scalar
::Raw { data, size }
=> {
374 assert_eq
!(target_size
.bytes(), size
as u64);
375 Scalar
::check_data(data
, size
);
378 Scalar
::Ptr(ptr
) => {
379 assert_eq
!(target_size
, cx
.data_layout().pointer_size
);
385 /// This method is intentionally private!
386 /// It is just a helper for other methods in this file.
388 fn to_bits(self, target_size
: Size
) -> InterpResult
<'tcx
, u128
> {
389 assert_ne
!(target_size
.bytes(), 0, "you should never look at the bits of a ZST");
391 Scalar
::Raw { data, size }
=> {
392 assert_eq
!(target_size
.bytes(), size
as u64);
393 Scalar
::check_data(data
, size
);
396 Scalar
::Ptr(_
) => throw_unsup
!(ReadPointerAsBytes
),
401 pub fn assert_bits(self, target_size
: Size
) -> u128
{
402 self.to_bits(target_size
).expect("expected Raw bits but got a Pointer")
406 pub fn assert_ptr(self) -> Pointer
<Tag
> {
409 Scalar
::Raw { .. }
=> bug
!("expected a Pointer but got Raw bits"),
413 /// Do not call this method! Dispatch based on the type instead.
415 pub fn is_bits(self) -> bool
{
417 Scalar
::Raw { .. }
=> true,
422 /// Do not call this method! Dispatch based on the type instead.
424 pub fn is_ptr(self) -> bool
{
426 Scalar
::Ptr(_
) => true,
431 pub fn to_bool(self) -> InterpResult
<'tcx
, bool
> {
433 Scalar
::Raw { data: 0, size: 1 }
=> Ok(false),
434 Scalar
::Raw { data: 1, size: 1 }
=> Ok(true),
435 _
=> throw_unsup
!(InvalidBool
),
439 pub fn to_char(self) -> InterpResult
<'tcx
, char> {
440 let val
= self.to_u32()?
;
441 match ::std
::char::from_u32(val
) {
443 None
=> throw_unsup
!(InvalidChar(val
as u128
)),
448 fn to_unsigned_with_bit_width(self, bits
: u64) -> InterpResult
<'
static, u128
> {
449 let sz
= Size
::from_bits(bits
);
453 /// Converts the scalar to produce an `u8`. Fails if the scalar is a pointer.
454 pub fn to_u8(self) -> InterpResult
<'
static, u8> {
455 self.to_unsigned_with_bit_width(8).map(|v
| v
as u8)
458 /// Converts the scalar to produce an `u16`. Fails if the scalar is a pointer.
459 pub fn to_u16(self) -> InterpResult
<'
static, u16> {
460 self.to_unsigned_with_bit_width(16).map(|v
| v
as u16)
463 /// Converts the scalar to produce an `u32`. Fails if the scalar is a pointer.
464 pub fn to_u32(self) -> InterpResult
<'
static, u32> {
465 self.to_unsigned_with_bit_width(32).map(|v
| v
as u32)
468 /// Converts the scalar to produce an `u64`. Fails if the scalar is a pointer.
469 pub fn to_u64(self) -> InterpResult
<'
static, u64> {
470 self.to_unsigned_with_bit_width(64).map(|v
| v
as u64)
473 pub fn to_machine_usize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'
static, u64> {
474 let b
= self.to_bits(cx
.data_layout().pointer_size
)?
;
479 fn to_signed_with_bit_width(self, bits
: u64) -> InterpResult
<'
static, i128
> {
480 let sz
= Size
::from_bits(bits
);
481 let b
= self.to_bits(sz
)?
;
482 Ok(sign_extend(b
, sz
) as i128
)
485 /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer.
486 pub fn to_i8(self) -> InterpResult
<'
static, i8> {
487 self.to_signed_with_bit_width(8).map(|v
| v
as i8)
490 /// Converts the scalar to produce an `i16`. Fails if the scalar is a pointer.
491 pub fn to_i16(self) -> InterpResult
<'
static, i16> {
492 self.to_signed_with_bit_width(16).map(|v
| v
as i16)
495 /// Converts the scalar to produce an `i32`. Fails if the scalar is a pointer.
496 pub fn to_i32(self) -> InterpResult
<'
static, i32> {
497 self.to_signed_with_bit_width(32).map(|v
| v
as i32)
500 /// Converts the scalar to produce an `i64`. Fails if the scalar is a pointer.
501 pub fn to_i64(self) -> InterpResult
<'
static, i64> {
502 self.to_signed_with_bit_width(64).map(|v
| v
as i64)
505 pub fn to_machine_isize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'
static, i64> {
506 let sz
= cx
.data_layout().pointer_size
;
507 let b
= self.to_bits(sz
)?
;
508 let b
= sign_extend(b
, sz
) as i128
;
513 pub fn to_f32(self) -> InterpResult
<'
static, Single
> {
514 // Going through `u32` to check size and truncation.
515 Ok(Single
::from_bits(self.to_u32()?
as u128
))
519 pub fn to_f64(self) -> InterpResult
<'
static, Double
> {
520 // Going through `u64` to check size and truncation.
521 Ok(Double
::from_bits(self.to_u64()?
as u128
))
525 impl<Tag
> From
<Pointer
<Tag
>> for Scalar
<Tag
> {
527 fn from(ptr
: Pointer
<Tag
>) -> Self {
532 #[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable, HashStable, Hash)]
533 pub enum ScalarMaybeUndef
<Tag
= (), Id
= AllocId
> {
534 Scalar(Scalar
<Tag
, Id
>),
538 impl<Tag
> From
<Scalar
<Tag
>> for ScalarMaybeUndef
<Tag
> {
540 fn from(s
: Scalar
<Tag
>) -> Self {
541 ScalarMaybeUndef
::Scalar(s
)
545 impl<Tag
> From
<Pointer
<Tag
>> for ScalarMaybeUndef
<Tag
> {
547 fn from(s
: Pointer
<Tag
>) -> Self {
548 ScalarMaybeUndef
::Scalar(s
.into())
552 impl<Tag
: fmt
::Debug
, Id
: fmt
::Debug
> fmt
::Debug
for ScalarMaybeUndef
<Tag
, Id
> {
553 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
555 ScalarMaybeUndef
::Undef
=> write
!(f
, "Undef"),
556 ScalarMaybeUndef
::Scalar(s
) => write
!(f
, "{:?}", s
),
561 impl<Tag
> fmt
::Display
for ScalarMaybeUndef
<Tag
> {
562 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
564 ScalarMaybeUndef
::Undef
=> write
!(f
, "uninitialized bytes"),
565 ScalarMaybeUndef
::Scalar(s
) => write
!(f
, "{}", s
),
570 impl<'tcx
, Tag
> ScalarMaybeUndef
<Tag
> {
571 /// Erase the tag from the scalar, if any.
573 /// Used by error reporting code to avoid having the error type depend on `Tag`.
575 pub fn erase_tag(self) -> ScalarMaybeUndef
{
577 ScalarMaybeUndef
::Scalar(s
) => ScalarMaybeUndef
::Scalar(s
.erase_tag()),
578 ScalarMaybeUndef
::Undef
=> ScalarMaybeUndef
::Undef
,
583 pub fn not_undef(self) -> InterpResult
<'
static, Scalar
<Tag
>> {
585 ScalarMaybeUndef
::Scalar(scalar
) => Ok(scalar
),
586 ScalarMaybeUndef
::Undef
=> throw_unsup
!(ReadUndefBytes(Size
::ZERO
)),
591 pub fn to_bool(self) -> InterpResult
<'tcx
, bool
> {
592 self.not_undef()?
.to_bool()
596 pub fn to_char(self) -> InterpResult
<'tcx
, char> {
597 self.not_undef()?
.to_char()
601 pub fn to_f32(self) -> InterpResult
<'tcx
, Single
> {
602 self.not_undef()?
.to_f32()
606 pub fn to_f64(self) -> InterpResult
<'tcx
, Double
> {
607 self.not_undef()?
.to_f64()
611 pub fn to_u8(self) -> InterpResult
<'tcx
, u8> {
612 self.not_undef()?
.to_u8()
616 pub fn to_u16(self) -> InterpResult
<'tcx
, u16> {
617 self.not_undef()?
.to_u16()
621 pub fn to_u32(self) -> InterpResult
<'tcx
, u32> {
622 self.not_undef()?
.to_u32()
626 pub fn to_u64(self) -> InterpResult
<'tcx
, u64> {
627 self.not_undef()?
.to_u64()
631 pub fn to_machine_usize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, u64> {
632 self.not_undef()?
.to_machine_usize(cx
)
636 pub fn to_i8(self) -> InterpResult
<'tcx
, i8> {
637 self.not_undef()?
.to_i8()
641 pub fn to_i16(self) -> InterpResult
<'tcx
, i16> {
642 self.not_undef()?
.to_i16()
646 pub fn to_i32(self) -> InterpResult
<'tcx
, i32> {
647 self.not_undef()?
.to_i32()
651 pub fn to_i64(self) -> InterpResult
<'tcx
, i64> {
652 self.not_undef()?
.to_i64()
656 pub fn to_machine_isize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, i64> {
657 self.not_undef()?
.to_machine_isize(cx
)
661 /// Gets the bytes of a constant slice value.
662 pub fn get_slice_bytes
<'tcx
>(cx
: &impl HasDataLayout
, val
: ConstValue
<'tcx
>) -> &'tcx
[u8] {
663 if let ConstValue
::Slice { data, start, end }
= val
{
664 let len
= end
- start
;
667 // invent a pointer, only the offset is relevant anyway
668 Pointer
::new(AllocId(0), Size
::from_bytes(start
as u64)),
669 Size
::from_bytes(len
as u64),
671 .unwrap_or_else(|err
| bug
!("const slice is invalid: {:?}", err
))
673 bug
!("expected const slice, but found another const value");