1 use rustc_apfloat
::ieee
::{Double, Single}
;
2 use rustc_apfloat
::Float
;
3 use rustc_serialize
::{Decodable, Decoder, Encodable, Encoder}
;
4 use rustc_target
::abi
::{Size, TargetDataLayout}
;
5 use std
::convert
::{TryFrom, TryInto}
;
10 #[derive(Copy, Clone)]
11 /// A type for representing any integer. Only used for printing.
13 /// The "untyped" variant of `ConstInt`.
15 /// Whether the value is of a signed integer type.
17 /// Whether the value is a `usize` or `isize` type.
18 is_ptr_sized_integral
: bool
,
22 pub fn new(int
: ScalarInt
, signed
: bool
, is_ptr_sized_integral
: bool
) -> Self {
23 Self { int, signed, is_ptr_sized_integral }
27 impl std
::fmt
::Debug
for ConstInt
{
28 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
29 let Self { int, signed, is_ptr_sized_integral }
= *self;
30 let size
= int
.size().bytes();
33 let bit_size
= size
* 8;
34 let min
= 1u128 << (bit_size
- 1);
37 match (size
, is_ptr_sized_integral
) {
38 (_
, true) => write
!(fmt
, "isize::MIN"),
39 (1, _
) => write
!(fmt
, "i8::MIN"),
40 (2, _
) => write
!(fmt
, "i16::MIN"),
41 (4, _
) => write
!(fmt
, "i32::MIN"),
42 (8, _
) => write
!(fmt
, "i64::MIN"),
43 (16, _
) => write
!(fmt
, "i128::MIN"),
44 _
=> bug
!("ConstInt 0x{:x} with size = {} and signed = {}", raw
, size
, signed
),
46 } else if raw
== max
{
47 match (size
, is_ptr_sized_integral
) {
48 (_
, true) => write
!(fmt
, "isize::MAX"),
49 (1, _
) => write
!(fmt
, "i8::MAX"),
50 (2, _
) => write
!(fmt
, "i16::MAX"),
51 (4, _
) => write
!(fmt
, "i32::MAX"),
52 (8, _
) => write
!(fmt
, "i64::MAX"),
53 (16, _
) => write
!(fmt
, "i128::MAX"),
54 _
=> bug
!("ConstInt 0x{:x} with size = {} and signed = {}", raw
, size
, signed
),
58 1 => write
!(fmt
, "{}", raw
as i8)?
,
59 2 => write
!(fmt
, "{}", raw
as i16)?
,
60 4 => write
!(fmt
, "{}", raw
as i32)?
,
61 8 => write
!(fmt
, "{}", raw
as i64)?
,
62 16 => write
!(fmt
, "{}", raw
as i128
)?
,
63 _
=> bug
!("ConstInt 0x{:x} with size = {} and signed = {}", raw
, size
, signed
),
66 match (size
, is_ptr_sized_integral
) {
67 (_
, true) => write
!(fmt
, "_isize")?
,
68 (1, _
) => write
!(fmt
, "_i8")?
,
69 (2, _
) => write
!(fmt
, "_i16")?
,
70 (4, _
) => write
!(fmt
, "_i32")?
,
71 (8, _
) => write
!(fmt
, "_i64")?
,
72 (16, _
) => write
!(fmt
, "_i128")?
,
79 let max
= Size
::from_bytes(size
).truncate(u128
::MAX
);
81 match (size
, is_ptr_sized_integral
) {
82 (_
, true) => write
!(fmt
, "usize::MAX"),
83 (1, _
) => write
!(fmt
, "u8::MAX"),
84 (2, _
) => write
!(fmt
, "u16::MAX"),
85 (4, _
) => write
!(fmt
, "u32::MAX"),
86 (8, _
) => write
!(fmt
, "u64::MAX"),
87 (16, _
) => write
!(fmt
, "u128::MAX"),
88 _
=> bug
!("ConstInt 0x{:x} with size = {} and signed = {}", raw
, size
, signed
),
92 1 => write
!(fmt
, "{}", raw
as u8)?
,
93 2 => write
!(fmt
, "{}", raw
as u16)?
,
94 4 => write
!(fmt
, "{}", raw
as u32)?
,
95 8 => write
!(fmt
, "{}", raw
as u64)?
,
96 16 => write
!(fmt
, "{}", raw
as u128
)?
,
97 _
=> bug
!("ConstInt 0x{:x} with size = {} and signed = {}", raw
, size
, signed
),
100 match (size
, is_ptr_sized_integral
) {
101 (_
, true) => write
!(fmt
, "_usize")?
,
102 (1, _
) => write
!(fmt
, "_u8")?
,
103 (2, _
) => write
!(fmt
, "_u16")?
,
104 (4, _
) => write
!(fmt
, "_u32")?
,
105 (8, _
) => write
!(fmt
, "_u64")?
,
106 (16, _
) => write
!(fmt
, "_u128")?
,
116 /// The raw bytes of a simple value.
118 /// This is a packed struct in order to allow this type to be optimally embedded in enums
120 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
122 pub struct ScalarInt
{
123 /// The first `size` bytes of `data` are the value.
124 /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
129 // Cannot derive these, as the derives take references to the fields, and we
130 // can't take references to fields of packed structs.
131 impl<CTX
> crate::ty
::HashStable
<CTX
> for ScalarInt
{
132 fn hash_stable(&self, hcx
: &mut CTX
, hasher
: &mut crate::ty
::StableHasher
) {
133 // Using a block `{self.data}` here to force a copy instead of using `self.data`
134 // directly, because `hash_stable` takes `&self` and would thus borrow `self.data`.
135 // Since `Self` is a packed struct, that would create a possibly unaligned reference,
137 { self.data }
.hash_stable(hcx
, hasher
);
138 self.size
.hash_stable(hcx
, hasher
);
142 impl<S
: Encoder
> Encodable
<S
> for ScalarInt
{
143 fn encode(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
144 s
.emit_u128(self.data
)?
;
149 impl<D
: Decoder
> Decodable
<D
> for ScalarInt
{
150 fn decode(d
: &mut D
) -> Result
<ScalarInt
, D
::Error
> {
151 Ok(ScalarInt { data: d.read_u128()?, size: d.read_u8()? }
)
156 pub const TRUE
: ScalarInt
= ScalarInt { data: 1_u128, size: 1 }
;
158 pub const FALSE
: ScalarInt
= ScalarInt { data: 0_u128, size: 1 }
;
160 pub const ZST
: ScalarInt
= ScalarInt { data: 0_u128, size: 0 }
;
163 pub fn size(self) -> Size
{
164 Size
::from_bytes(self.size
)
167 /// Make sure the `data` fits in `size`.
168 /// This is guaranteed by all constructors here, but having had this check saved us from
169 /// bugs many times in the past, so keeping it around is definitely worth it.
171 fn check_data(self) {
172 // Using a block `{self.data}` here to force a copy instead of using `self.data`
173 // directly, because `debug_assert_eq` takes references to its arguments and formatting
174 // arguments and would thus borrow `self.data`. Since `Self`
175 // is a packed struct, that would create a possibly unaligned reference, which
178 self.size().truncate(self.data
),
180 "Scalar value {:#x} exceeds size of {} bytes",
187 pub fn null(size
: Size
) -> Self {
188 Self { data: 0, size: size.bytes() as u8 }
192 pub fn is_null(self) -> bool
{
196 pub(crate) fn ptr_sized_op
<E
>(
198 dl
: &TargetDataLayout
,
199 f_int
: impl FnOnce(u64) -> Result
<u64, E
>,
200 ) -> Result
<Self, E
> {
201 assert_eq
!(u64::from(self.size
), dl
.pointer_size
.bytes());
202 Ok(Self::try_from_uint(f_int(u64::try_from(self.data
).unwrap())?
, self.size()).unwrap())
206 pub fn try_from_uint(i
: impl Into
<u128
>, size
: Size
) -> Option
<Self> {
208 if size
.truncate(data
) == data
{
209 Some(Self { data, size: size.bytes() as u8 }
)
216 pub fn try_from_int(i
: impl Into
<i128
>, size
: Size
) -> Option
<Self> {
218 // `into` performed sign extension, we have to truncate
219 let truncated
= size
.truncate(i
as u128
);
220 if size
.sign_extend(truncated
) as i128
== i
{
221 Some(Self { data: truncated, size: size.bytes() as u8 }
)
228 pub fn assert_bits(self, target_size
: Size
) -> u128
{
229 self.to_bits(target_size
).unwrap_or_else(|size
| {
230 bug
!("expected int of size {}, but got size {}", target_size
.bytes(), size
.bytes())
235 pub fn to_bits(self, target_size
: Size
) -> Result
<u128
, Size
> {
236 assert_ne
!(target_size
.bytes(), 0, "you should never look at the bits of a ZST");
237 if target_size
.bytes() == u64::from(self.size
) {
246 pub fn try_to_machine_usize(&self, tcx
: TyCtxt
<'tcx
>) -> Result
<u64, Size
> {
247 Ok(self.to_bits(tcx
.data_layout
.pointer_size
)?
as u64)
254 impl From
<$ty
> for ScalarInt
{
256 fn from(u
: $ty
) -> Self {
259 size
: std
::mem
::size_of
::<$ty
>() as u8,
267 macro_rules
! try_from
{
270 impl TryFrom
<ScalarInt
> for $ty
{
273 fn try_from(int
: ScalarInt
) -> Result
<Self, Size
> {
274 // The `unwrap` cannot fail because to_bits (if it succeeds)
275 // is guaranteed to return a value that fits into the size.
276 int
.to_bits(Size
::from_bytes(std
::mem
::size_of
::<$ty
>()))
277 .map(|u
| u
.try_into().unwrap())
284 from
!(u8, u16, u32, u64, u128
, bool
);
285 try_from
!(u8, u16, u32, u64, u128
);
287 impl TryFrom
<ScalarInt
> for bool
{
290 fn try_from(int
: ScalarInt
) -> Result
<Self, Size
> {
291 int
.to_bits(Size
::from_bytes(1)).and_then(|u
| match u
{
294 _
=> Err(Size
::from_bytes(1)),
299 impl From
<char> for ScalarInt
{
301 fn from(c
: char) -> Self {
302 Self { data: c as u128, size: std::mem::size_of::<char>() as u8 }
306 impl TryFrom
<ScalarInt
> for char {
309 fn try_from(int
: ScalarInt
) -> Result
<Self, Size
> {
310 int
.to_bits(Size
::from_bytes(std
::mem
::size_of
::<char>()))
311 .map(|u
| char::from_u32(u
.try_into().unwrap()).unwrap())
315 impl From
<Single
> for ScalarInt
{
317 fn from(f
: Single
) -> Self {
318 // We trust apfloat to give us properly truncated data.
319 Self { data: f.to_bits(), size: 4 }
323 impl TryFrom
<ScalarInt
> for Single
{
326 fn try_from(int
: ScalarInt
) -> Result
<Self, Size
> {
327 int
.to_bits(Size
::from_bytes(4)).map(Self::from_bits
)
331 impl From
<Double
> for ScalarInt
{
333 fn from(f
: Double
) -> Self {
334 // We trust apfloat to give us properly truncated data.
335 Self { data: f.to_bits(), size: 8 }
339 impl TryFrom
<ScalarInt
> for Double
{
342 fn try_from(int
: ScalarInt
) -> Result
<Self, Size
> {
343 int
.to_bits(Size
::from_bytes(8)).map(Self::from_bits
)
347 impl fmt
::Debug
for ScalarInt
{
348 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
353 // Dispatch to LowerHex below.
354 write
!(f
, "0x{:x}", self)
359 impl fmt
::LowerHex
for ScalarInt
{
360 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
362 // Format as hex number wide enough to fit any value of the given `size`.
363 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
364 // Using a block `{self.data}` here to force a copy instead of using `self.data`
365 // directly, because `write!` takes references to its formatting arguments and
366 // would thus borrow `self.data`. Since `Self`
367 // is a packed struct, that would create a possibly unaligned reference, which
369 write
!(f
, "{:01$x}", { self.data }
, self.size
as usize * 2)
373 impl fmt
::UpperHex
for ScalarInt
{
374 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
376 // Format as hex number wide enough to fit any value of the given `size`.
377 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
378 // Using a block `{self.data}` here to force a copy instead of using `self.data`
379 // directly, because `write!` takes references to its formatting arguments and
380 // would thus borrow `self.data`. Since `Self`
381 // is a packed struct, that would create a possibly unaligned reference, which
383 write
!(f
, "{:01$X}", { self.data }
, self.size
as usize * 2)