2 use rustc_macros
::HashStable
;
3 use rustc_apfloat
::{Float, ieee::{Double, Single}
};
5 use crate::ty
::{Ty, layout::{HasDataLayout, Size}
};
7 use super::{InterpResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate}
;
9 /// Represents the result of a raw const operation, pre-validation.
10 #[derive(Clone, HashStable)]
11 pub struct RawConst
<'tcx
> {
12 // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
13 // (so you can use `AllocMap::unwrap_memory`).
14 pub alloc_id
: AllocId
,
18 /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
19 /// array length computations, enum discriminants and the pattern matching logic.
20 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord,
21 RustcEncodable
, RustcDecodable
, Hash
, HashStable
)]
22 pub enum ConstValue
<'tcx
> {
23 /// Used only for types with `layout::abi::Scalar` ABI and ZSTs.
25 /// Not using the enum `Value` to encode that this must not be `Undef`.
28 /// Used only for `&[u8]` and `&str`
30 data
: &'tcx Allocation
,
35 /// A value not represented/representable by `Scalar` or `Slice`
37 /// The backing memory of the value, may contain more memory than needed for just the value
38 /// in order to share `Allocation`s between values
39 alloc
: &'tcx Allocation
,
40 /// Offset into `alloc`
45 #[cfg(target_arch = "x86_64")]
46 static_assert_size
!(ConstValue
<'_
>, 32);
48 impl<'tcx
> ConstValue
<'tcx
> {
50 pub fn try_to_scalar(&self) -> Option
<Scalar
> {
52 ConstValue
::ByRef { .. }
|
53 ConstValue
::Slice { .. }
=> None
,
54 ConstValue
::Scalar(val
) => Some(val
),
59 /// A `Scalar` represents an immediate, primitive value existing outside of a
60 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
61 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
62 /// of a simple value or a pointer into another `Allocation`
63 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd,
64 RustcEncodable
, RustcDecodable
, Hash
, HashStable
)]
65 pub enum Scalar
<Tag
= (), Id
= AllocId
> {
66 /// The raw bytes of a simple value.
68 /// The first `size` bytes of `data` are the value.
69 /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
74 /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
75 /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
76 /// relocation and its associated offset together as a `Pointer` here.
77 Ptr(Pointer
<Tag
, Id
>),
80 #[cfg(target_arch = "x86_64")]
81 static_assert_size
!(Scalar
, 24);
83 impl<Tag
: fmt
::Debug
, Id
: fmt
::Debug
> fmt
::Debug
for Scalar
<Tag
, Id
> {
84 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
87 write
!(f
, "{:?}", ptr
),
88 &Scalar
::Raw { data, size }
=> {
89 Scalar
::check_data(data
, size
);
93 // Format as hex number wide enough to fit any value of the given `size`.
94 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
95 write
!(f
, "0x{:>0width$x}", data
, width
=(size
*2) as usize)
102 impl<Tag
> fmt
::Display
for Scalar
<Tag
> {
103 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
105 Scalar
::Ptr(_
) => write
!(f
, "a pointer"),
106 Scalar
::Raw { data, .. }
=> write
!(f
, "{}", data
),
111 impl<Tag
> From
<Single
> for Scalar
<Tag
> {
113 fn from(f
: Single
) -> Self {
118 impl<Tag
> From
<Double
> for Scalar
<Tag
> {
120 fn from(f
: Double
) -> Self {
127 fn check_data(data
: u128
, size
: u8) {
128 debug_assert_eq
!(truncate(data
, Size
::from_bytes(size
as u64)), data
,
129 "Scalar value {:#x} exceeds size of {} bytes", data
, size
);
132 /// Tag this scalar with `new_tag` if it is a pointer, leave it unchanged otherwise.
134 /// Used by `MemPlace::replace_tag`.
136 pub fn with_tag
<Tag
>(self, new_tag
: Tag
) -> Scalar
<Tag
> {
138 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.with_tag(new_tag
)),
139 Scalar
::Raw { data, size }
=> Scalar
::Raw { data, size }
,
144 impl<'tcx
, Tag
> Scalar
<Tag
> {
145 /// Erase the tag from the scalar, if any.
147 /// Used by error reporting code to avoid having the error type depend on `Tag`.
149 pub fn erase_tag(self) -> Scalar
{
151 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.erase_tag()),
152 Scalar
::Raw { data, size }
=> Scalar
::Raw { data, size }
,
157 pub fn ptr_null(cx
: &impl HasDataLayout
) -> Self {
160 size
: cx
.data_layout().pointer_size
.bytes() as u8,
165 pub fn zst() -> Self {
166 Scalar
::Raw { data: 0, size: 0 }
170 pub fn ptr_offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, Self> {
171 let dl
= cx
.data_layout();
173 Scalar
::Raw { data, size }
=> {
174 assert_eq
!(size
as u64, dl
.pointer_size
.bytes());
176 data
: dl
.offset(data
as u64, i
.bytes())?
as u128
,
180 Scalar
::Ptr(ptr
) => ptr
.offset(i
, dl
).map(Scalar
::Ptr
),
185 pub fn ptr_wrapping_offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> Self {
186 let dl
= cx
.data_layout();
188 Scalar
::Raw { data, size }
=> {
189 assert_eq
!(size
as u64, dl
.pointer_size
.bytes());
191 data
: dl
.overflowing_offset(data
as u64, i
.bytes()).0 as u128
,
195 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.wrapping_offset(i
, dl
)),
200 pub fn ptr_signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, Self> {
201 let dl
= cx
.data_layout();
203 Scalar
::Raw { data, size }
=> {
204 assert_eq
!(size
as u64, dl
.pointer_size().bytes());
206 data
: dl
.signed_offset(data
as u64, i
)?
as u128
,
210 Scalar
::Ptr(ptr
) => ptr
.signed_offset(i
, dl
).map(Scalar
::Ptr
),
215 pub fn ptr_wrapping_signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> Self {
216 let dl
= cx
.data_layout();
218 Scalar
::Raw { data, size }
=> {
219 assert_eq
!(size
as u64, dl
.pointer_size
.bytes());
221 data
: dl
.overflowing_signed_offset(data
as u64, i128
::from(i
)).0 as u128
,
225 Scalar
::Ptr(ptr
) => Scalar
::Ptr(ptr
.wrapping_signed_offset(i
, dl
)),
230 pub fn from_bool(b
: bool
) -> Self {
231 Scalar
::Raw { data: b as u128, size: 1 }
235 pub fn from_char(c
: char) -> Self {
236 Scalar
::Raw { data: c as u128, size: 4 }
240 pub fn from_uint(i
: impl Into
<u128
>, size
: Size
) -> Self {
243 truncate(i
, size
), i
,
244 "Unsigned value {:#x} does not fit in {} bits", i
, size
.bits()
246 Scalar
::Raw { data: i, size: size.bytes() as u8 }
250 pub fn from_u8(i
: u8) -> Self {
251 Scalar
::Raw { data: i as u128, size: 1 }
255 pub fn from_u16(i
: u16) -> Self {
256 Scalar
::Raw { data: i as u128, size: 2 }
260 pub fn from_u32(i
: u32) -> Self {
261 Scalar
::Raw { data: i as u128, size: 4 }
265 pub fn from_u64(i
: u64) -> Self {
266 Scalar
::Raw { data: i as u128, size: 8 }
270 pub fn from_int(i
: impl Into
<i128
>, size
: Size
) -> Self {
272 // `into` performed sign extension, we have to truncate
273 let truncated
= truncate(i
as u128
, size
);
275 sign_extend(truncated
, size
) as i128
, i
,
276 "Signed value {:#x} does not fit in {} bits", i
, size
.bits()
278 Scalar
::Raw { data: truncated, size: size.bytes() as u8 }
282 pub fn from_f32(f
: Single
) -> Self {
283 // We trust apfloat to give us properly truncated data.
284 Scalar
::Raw { data: f.to_bits(), size: 4 }
288 pub fn from_f64(f
: Double
) -> Self {
289 // We trust apfloat to give us properly truncated data.
290 Scalar
::Raw { data: f.to_bits(), size: 8 }
293 /// This is very rarely the method you want! You should dispatch on the type
294 /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`.
295 /// This method only exists for the benefit of low-level memory operations
296 /// as well as the implementation of the `force_*` methods.
298 pub fn to_bits_or_ptr(
301 cx
: &impl HasDataLayout
,
302 ) -> Result
<u128
, Pointer
<Tag
>> {
304 Scalar
::Raw { data, size }
=> {
305 assert_eq
!(target_size
.bytes(), size
as u64);
306 assert_ne
!(size
, 0, "you should never look at the bits of a ZST");
307 Scalar
::check_data(data
, size
);
310 Scalar
::Ptr(ptr
) => {
311 assert_eq
!(target_size
, cx
.data_layout().pointer_size
);
318 pub fn check_raw(data
: u128
, size
: u8, target_size
: Size
) {
319 assert_eq
!(target_size
.bytes(), size
as u64);
320 assert_ne
!(size
, 0, "you should never look at the bits of a ZST");
321 Scalar
::check_data(data
, size
);
324 /// Do not call this method! Use either `assert_bits` or `force_bits`.
326 pub fn to_bits(self, target_size
: Size
) -> InterpResult
<'tcx
, u128
> {
328 Scalar
::Raw { data, size }
=> {
329 Self::check_raw(data
, size
, target_size
);
332 Scalar
::Ptr(_
) => throw_unsup
!(ReadPointerAsBytes
),
337 pub fn assert_bits(self, target_size
: Size
) -> u128
{
338 self.to_bits(target_size
).expect("expected Raw bits but got a Pointer")
341 /// Do not call this method! Use either `assert_ptr` or `force_ptr`.
343 pub fn to_ptr(self) -> InterpResult
<'tcx
, Pointer
<Tag
>> {
345 Scalar
::Raw { data: 0, .. }
=> throw_unsup
!(InvalidNullPointerUsage
),
346 Scalar
::Raw { .. }
=> throw_unsup
!(ReadBytesAsPointer
),
347 Scalar
::Ptr(p
) => Ok(p
),
352 pub fn assert_ptr(self) -> Pointer
<Tag
> {
353 self.to_ptr().expect("expected a Pointer but got Raw bits")
356 /// Do not call this method! Dispatch based on the type instead.
358 pub fn is_bits(self) -> bool
{
360 Scalar
::Raw { .. }
=> true,
365 /// Do not call this method! Dispatch based on the type instead.
367 pub fn is_ptr(self) -> bool
{
369 Scalar
::Ptr(_
) => true,
374 pub fn to_bool(self) -> InterpResult
<'tcx
, bool
> {
376 Scalar
::Raw { data: 0, size: 1 }
=> Ok(false),
377 Scalar
::Raw { data: 1, size: 1 }
=> Ok(true),
378 _
=> throw_unsup
!(InvalidBool
),
382 pub fn to_char(self) -> InterpResult
<'tcx
, char> {
383 let val
= self.to_u32()?
;
384 match ::std
::char::from_u32(val
) {
386 None
=> throw_unsup
!(InvalidChar(val
as u128
)),
390 pub fn to_u8(self) -> InterpResult
<'
static, u8> {
391 let sz
= Size
::from_bits(8);
392 let b
= self.to_bits(sz
)?
;
396 pub fn to_u32(self) -> InterpResult
<'
static, u32> {
397 let sz
= Size
::from_bits(32);
398 let b
= self.to_bits(sz
)?
;
402 pub fn to_u64(self) -> InterpResult
<'
static, u64> {
403 let sz
= Size
::from_bits(64);
404 let b
= self.to_bits(sz
)?
;
408 pub fn to_machine_usize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'
static, u64> {
409 let b
= self.to_bits(cx
.data_layout().pointer_size
)?
;
413 pub fn to_i8(self) -> InterpResult
<'
static, i8> {
414 let sz
= Size
::from_bits(8);
415 let b
= self.to_bits(sz
)?
;
416 let b
= sign_extend(b
, sz
) as i128
;
420 pub fn to_i32(self) -> InterpResult
<'
static, i32> {
421 let sz
= Size
::from_bits(32);
422 let b
= self.to_bits(sz
)?
;
423 let b
= sign_extend(b
, sz
) as i128
;
427 pub fn to_i64(self) -> InterpResult
<'
static, i64> {
428 let sz
= Size
::from_bits(64);
429 let b
= self.to_bits(sz
)?
;
430 let b
= sign_extend(b
, sz
) as i128
;
434 pub fn to_machine_isize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'
static, i64> {
435 let sz
= cx
.data_layout().pointer_size
;
436 let b
= self.to_bits(sz
)?
;
437 let b
= sign_extend(b
, sz
) as i128
;
442 pub fn to_f32(self) -> InterpResult
<'
static, Single
> {
443 // Going through `u32` to check size and truncation.
444 Ok(Single
::from_bits(self.to_u32()?
as u128
))
448 pub fn to_f64(self) -> InterpResult
<'
static, Double
> {
449 // Going through `u64` to check size and truncation.
450 Ok(Double
::from_bits(self.to_u64()?
as u128
))
454 impl<Tag
> From
<Pointer
<Tag
>> for Scalar
<Tag
> {
456 fn from(ptr
: Pointer
<Tag
>) -> Self {
461 #[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable, HashStable, Hash)]
462 pub enum ScalarMaybeUndef
<Tag
= (), Id
= AllocId
> {
463 Scalar(Scalar
<Tag
, Id
>),
467 impl<Tag
> From
<Scalar
<Tag
>> for ScalarMaybeUndef
<Tag
> {
469 fn from(s
: Scalar
<Tag
>) -> Self {
470 ScalarMaybeUndef
::Scalar(s
)
474 impl<Tag
> From
<Pointer
<Tag
>> for ScalarMaybeUndef
<Tag
> {
476 fn from(s
: Pointer
<Tag
>) -> Self {
477 ScalarMaybeUndef
::Scalar(s
.into())
481 impl<Tag
: fmt
::Debug
, Id
: fmt
::Debug
> fmt
::Debug
for ScalarMaybeUndef
<Tag
, Id
> {
482 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
484 ScalarMaybeUndef
::Undef
=> write
!(f
, "Undef"),
485 ScalarMaybeUndef
::Scalar(s
) => write
!(f
, "{:?}", s
),
490 impl<Tag
> fmt
::Display
for ScalarMaybeUndef
<Tag
> {
491 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
493 ScalarMaybeUndef
::Undef
=> write
!(f
, "uninitialized bytes"),
494 ScalarMaybeUndef
::Scalar(s
) => write
!(f
, "{}", s
),
499 impl<'tcx
, Tag
> ScalarMaybeUndef
<Tag
> {
500 /// Erase the tag from the scalar, if any.
502 /// Used by error reporting code to avoid having the error type depend on `Tag`.
504 pub fn erase_tag(self) -> ScalarMaybeUndef
507 ScalarMaybeUndef
::Scalar(s
) => ScalarMaybeUndef
::Scalar(s
.erase_tag()),
508 ScalarMaybeUndef
::Undef
=> ScalarMaybeUndef
::Undef
,
513 pub fn not_undef(self) -> InterpResult
<'
static, Scalar
<Tag
>> {
515 ScalarMaybeUndef
::Scalar(scalar
) => Ok(scalar
),
516 ScalarMaybeUndef
::Undef
=> throw_unsup
!(ReadUndefBytes(Size
::ZERO
)),
520 /// Do not call this method! Use either `assert_ptr` or `force_ptr`.
522 pub fn to_ptr(self) -> InterpResult
<'tcx
, Pointer
<Tag
>> {
523 self.not_undef()?
.to_ptr()
526 /// Do not call this method! Use either `assert_bits` or `force_bits`.
528 pub fn to_bits(self, target_size
: Size
) -> InterpResult
<'tcx
, u128
> {
529 self.not_undef()?
.to_bits(target_size
)
533 pub fn to_bool(self) -> InterpResult
<'tcx
, bool
> {
534 self.not_undef()?
.to_bool()
538 pub fn to_char(self) -> InterpResult
<'tcx
, char> {
539 self.not_undef()?
.to_char()
543 pub fn to_f32(self) -> InterpResult
<'tcx
, Single
> {
544 self.not_undef()?
.to_f32()
548 pub fn to_f64(self) -> InterpResult
<'tcx
, Double
> {
549 self.not_undef()?
.to_f64()
553 pub fn to_u8(self) -> InterpResult
<'tcx
, u8> {
554 self.not_undef()?
.to_u8()
558 pub fn to_u32(self) -> InterpResult
<'tcx
, u32> {
559 self.not_undef()?
.to_u32()
563 pub fn to_u64(self) -> InterpResult
<'tcx
, u64> {
564 self.not_undef()?
.to_u64()
568 pub fn to_machine_usize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, u64> {
569 self.not_undef()?
.to_machine_usize(cx
)
573 pub fn to_i8(self) -> InterpResult
<'tcx
, i8> {
574 self.not_undef()?
.to_i8()
578 pub fn to_i32(self) -> InterpResult
<'tcx
, i32> {
579 self.not_undef()?
.to_i32()
583 pub fn to_i64(self) -> InterpResult
<'tcx
, i64> {
584 self.not_undef()?
.to_i64()
588 pub fn to_machine_isize(self, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, i64> {
589 self.not_undef()?
.to_machine_isize(cx
)
593 /// Gets the bytes of a constant slice value.
594 pub fn get_slice_bytes
<'tcx
>(cx
: &impl HasDataLayout
, val
: ConstValue
<'tcx
>) -> &'tcx
[u8] {
595 if let ConstValue
::Slice { data, start, end }
= val
{
596 let len
= end
- start
;
599 // invent a pointer, only the offset is relevant anyway
600 Pointer
::new(AllocId(0), Size
::from_bytes(start
as u64)),
601 Size
::from_bytes(len
as u64),
602 ).unwrap_or_else(|err
| bug
!("const slice is invalid: {:?}", err
))
604 bug
!("expected const slice, but found another const value");