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;
6 use std::num::NonZeroU8;
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.get().hash_stable(hcx, hasher);
142 impl<S: Encoder> Encodable<S> for ScalarInt {
143 fn encode(&self, s: &mut S) {
144 s.emit_u128(self.data);
145 s.emit_u8(self.size.get());
149 impl<D: Decoder> Decodable<D> for ScalarInt {
150 fn decode(d: &mut D) -> ScalarInt {
151 ScalarInt { data: d.read_u128(), size: NonZeroU8::new(d.read_u8()).unwrap() }
156 pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZeroU8::new(1).unwrap() };
158 pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZeroU8::new(1).unwrap() };
161 pub fn size(self) -> Size {
162 Size::from_bytes(self.size.get())
165 /// Make sure the `data` fits in `size`.
166 /// This is guaranteed by all constructors here, but having had this check saved us from
167 /// bugs many times in the past, so keeping it around is definitely worth it.
169 fn check_data(self) {
170 // Using a block `{self.data}` here to force a copy instead of using `self.data`
171 // directly, because `debug_assert_eq` takes references to its arguments and formatting
172 // arguments and would thus borrow `self.data`. Since `Self`
173 // is a packed struct, that would create a possibly unaligned reference, which
176 self.size().truncate(self.data),
178 "Scalar value {:#x} exceeds size of {} bytes",
185 pub fn null(size: Size) -> Self {
186 Self { data: 0, size: NonZeroU8::new(size.bytes() as u8).unwrap() }
190 pub fn is_null(self) -> bool {
195 pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
197 if size.truncate(data) == data {
198 Some(Self { data, size: NonZeroU8::new(size.bytes() as u8).unwrap() })
205 pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
207 // `into` performed sign extension, we have to truncate
208 let truncated = size.truncate(i as u128);
209 if size.sign_extend(truncated) as i128 == i {
210 Some(Self { data: truncated, size: NonZeroU8::new(size.bytes() as u8).unwrap() })
217 pub fn assert_bits(self, target_size: Size) -> u128 {
218 self.to_bits(target_size).unwrap_or_else(|size| {
219 bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
224 pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
225 assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
226 if target_size.bytes() == u64::from(self.size.get()) {
235 pub fn try_to_machine_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
236 Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
239 /// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
240 /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
241 /// `ScalarInt`s size in that case.
243 pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
247 // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
248 // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
249 // value of the `ScalarInt` in that case.
251 pub fn try_to_bool(self) -> Result<bool, Size> {
252 match self.try_to_u8()? {
255 _ => Err(self.size()),
259 // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
260 // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
263 pub fn try_to_u8(self) -> Result<u8, Size> {
264 self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
267 /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
268 /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
271 pub fn try_to_u16(self) -> Result<u16, Size> {
272 self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
275 /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
276 /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
279 pub fn try_to_u32(self) -> Result<u32, Size> {
280 self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
283 /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
284 /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
287 pub fn try_to_u64(self) -> Result<u64, Size> {
288 self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
291 /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
292 /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
295 pub fn try_to_u128(self) -> Result<u128, Size> {
296 self.to_bits(Size::from_bits(128))
299 /// Tries to convert the `ScalarInt` to a signed integer of the given size.
300 /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
301 /// `ScalarInt`s size in that case.
303 pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
304 let b = self.to_bits(size)?;
305 Ok(size.sign_extend(b) as i128)
308 /// Tries to convert the `ScalarInt` to i8.
309 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 1 }`
310 /// and returns the `ScalarInt`s size in that case.
311 pub fn try_to_i8(self) -> Result<i8, Size> {
312 self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
315 /// Tries to convert the `ScalarInt` to i16.
316 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 2 }`
317 /// and returns the `ScalarInt`s size in that case.
318 pub fn try_to_i16(self) -> Result<i16, Size> {
319 self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
322 /// Tries to convert the `ScalarInt` to i32.
323 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 4 }`
324 /// and returns the `ScalarInt`s size in that case.
325 pub fn try_to_i32(self) -> Result<i32, Size> {
326 self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
329 /// Tries to convert the `ScalarInt` to i64.
330 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 8 }`
331 /// and returns the `ScalarInt`s size in that case.
332 pub fn try_to_i64(self) -> Result<i64, Size> {
333 self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
336 /// Tries to convert the `ScalarInt` to i128.
337 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 16 }`
338 /// and returns the `ScalarInt`s size in that case.
339 pub fn try_to_i128(self) -> Result<i128, Size> {
340 self.try_to_int(Size::from_bits(128)).map(|v| i128::try_from(v).unwrap())
347 impl From<$ty> for ScalarInt {
349 fn from(u: $ty) -> Self {
352 size: NonZeroU8::new(std::mem::size_of::<$ty>() as u8).unwrap(),
360 macro_rules! try_from {
363 impl TryFrom<ScalarInt> for $ty {
366 fn try_from(int: ScalarInt) -> Result<Self, Size> {
367 // The `unwrap` cannot fail because to_bits (if it succeeds)
368 // is guaranteed to return a value that fits into the size.
369 int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
370 .map(|u| u.try_into().unwrap())
377 from!(u8, u16, u32, u64, u128, bool);
378 try_from!(u8, u16, u32, u64, u128);
380 impl TryFrom<ScalarInt> for bool {
383 fn try_from(int: ScalarInt) -> Result<Self, Size> {
384 int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
387 _ => Err(Size::from_bytes(1)),
392 impl From<char> for ScalarInt {
394 fn from(c: char) -> Self {
395 Self { data: c as u128, size: NonZeroU8::new(std::mem::size_of::<char>() as u8).unwrap() }
399 /// Error returned when a conversion from ScalarInt to char fails.
401 pub struct CharTryFromScalarInt;
403 impl TryFrom<ScalarInt> for char {
404 type Error = CharTryFromScalarInt;
407 fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
408 let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
409 return Err(CharTryFromScalarInt);
411 match char::from_u32(bits.try_into().unwrap()) {
413 None => Err(CharTryFromScalarInt),
418 impl From<Single> for ScalarInt {
420 fn from(f: Single) -> Self {
421 // We trust apfloat to give us properly truncated data.
422 Self { data: f.to_bits(), size: NonZeroU8::new((Single::BITS / 8) as u8).unwrap() }
426 impl TryFrom<ScalarInt> for Single {
429 fn try_from(int: ScalarInt) -> Result<Self, Size> {
430 int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
434 impl From<Double> for ScalarInt {
436 fn from(f: Double) -> Self {
437 // We trust apfloat to give us properly truncated data.
438 Self { data: f.to_bits(), size: NonZeroU8::new((Double::BITS / 8) as u8).unwrap() }
442 impl TryFrom<ScalarInt> for Double {
445 fn try_from(int: ScalarInt) -> Result<Self, Size> {
446 int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
450 impl fmt::Debug for ScalarInt {
451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452 // Dispatch to LowerHex below.
453 write!(f, "0x{:x}", self)
457 impl fmt::LowerHex for ScalarInt {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 // Like regular ints, alternate flag adds leading `0x`.
464 // Format as hex number wide enough to fit any value of the given `size`.
465 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
466 // Using a block `{self.data}` here to force a copy instead of using `self.data`
467 // directly, because `write!` takes references to its formatting arguments and
468 // would thus borrow `self.data`. Since `Self`
469 // is a packed struct, that would create a possibly unaligned reference, which
471 write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
475 impl fmt::UpperHex for ScalarInt {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 // Format as hex number wide enough to fit any value of the given `size`.
479 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
480 // Using a block `{self.data}` here to force a copy instead of using `self.data`
481 // directly, because `write!` takes references to its formatting arguments and
482 // would thus borrow `self.data`. Since `Self`
483 // is a packed struct, that would create a possibly unaligned reference, which
485 write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
489 impl fmt::Display for ScalarInt {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 write!(f, "{}", { self.data })