rustc_middle/ty/consts/
int.rs

1use std::fmt;
2use std::num::NonZero;
3
4use rustc_abi::Size;
5use rustc_apfloat::Float;
6use rustc_apfloat::ieee::{Double, Half, Quad, Single};
7use rustc_errors::{DiagArgValue, IntoDiagArg};
8use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
9
10use crate::ty::TyCtxt;
11
12#[derive(Copy, Clone)]
13/// A type for representing any integer. Only used for printing.
14pub struct ConstInt {
15    /// The "untyped" variant of `ConstInt`.
16    int: ScalarInt,
17    /// Whether the value is of a signed integer type.
18    signed: bool,
19    /// Whether the value is a `usize` or `isize` type.
20    is_ptr_sized_integral: bool,
21}
22
23impl ConstInt {
24    pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self {
25        Self { int, signed, is_ptr_sized_integral }
26    }
27}
28
29impl std::fmt::Debug for ConstInt {
30    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        let Self { int, signed, is_ptr_sized_integral } = *self;
32        let size = int.size().bytes();
33        let raw = int.data;
34        if signed {
35            let bit_size = size * 8;
36            let min = 1u128 << (bit_size - 1);
37            let max = min - 1;
38            if raw == min {
39                match (size, is_ptr_sized_integral) {
40                    (_, true) => write!(fmt, "isize::MIN"),
41                    (1, _) => write!(fmt, "i8::MIN"),
42                    (2, _) => write!(fmt, "i16::MIN"),
43                    (4, _) => write!(fmt, "i32::MIN"),
44                    (8, _) => write!(fmt, "i64::MIN"),
45                    (16, _) => write!(fmt, "i128::MIN"),
46                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
47                }
48            } else if raw == max {
49                match (size, is_ptr_sized_integral) {
50                    (_, true) => write!(fmt, "isize::MAX"),
51                    (1, _) => write!(fmt, "i8::MAX"),
52                    (2, _) => write!(fmt, "i16::MAX"),
53                    (4, _) => write!(fmt, "i32::MAX"),
54                    (8, _) => write!(fmt, "i64::MAX"),
55                    (16, _) => write!(fmt, "i128::MAX"),
56                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
57                }
58            } else {
59                match size {
60                    1 => write!(fmt, "{}", raw as i8)?,
61                    2 => write!(fmt, "{}", raw as i16)?,
62                    4 => write!(fmt, "{}", raw as i32)?,
63                    8 => write!(fmt, "{}", raw as i64)?,
64                    16 => write!(fmt, "{}", raw as i128)?,
65                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
66                }
67                if fmt.alternate() {
68                    match (size, is_ptr_sized_integral) {
69                        (_, true) => write!(fmt, "_isize")?,
70                        (1, _) => write!(fmt, "_i8")?,
71                        (2, _) => write!(fmt, "_i16")?,
72                        (4, _) => write!(fmt, "_i32")?,
73                        (8, _) => write!(fmt, "_i64")?,
74                        (16, _) => write!(fmt, "_i128")?,
75                        (sz, _) => bug!("unexpected int size i{sz}"),
76                    }
77                }
78                Ok(())
79            }
80        } else {
81            let max = Size::from_bytes(size).truncate(u128::MAX);
82            if raw == max {
83                match (size, is_ptr_sized_integral) {
84                    (_, true) => write!(fmt, "usize::MAX"),
85                    (1, _) => write!(fmt, "u8::MAX"),
86                    (2, _) => write!(fmt, "u16::MAX"),
87                    (4, _) => write!(fmt, "u32::MAX"),
88                    (8, _) => write!(fmt, "u64::MAX"),
89                    (16, _) => write!(fmt, "u128::MAX"),
90                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
91                }
92            } else {
93                match size {
94                    1 => write!(fmt, "{}", raw as u8)?,
95                    2 => write!(fmt, "{}", raw as u16)?,
96                    4 => write!(fmt, "{}", raw as u32)?,
97                    8 => write!(fmt, "{}", raw as u64)?,
98                    16 => write!(fmt, "{}", raw as u128)?,
99                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
100                }
101                if fmt.alternate() {
102                    match (size, is_ptr_sized_integral) {
103                        (_, true) => write!(fmt, "_usize")?,
104                        (1, _) => write!(fmt, "_u8")?,
105                        (2, _) => write!(fmt, "_u16")?,
106                        (4, _) => write!(fmt, "_u32")?,
107                        (8, _) => write!(fmt, "_u64")?,
108                        (16, _) => write!(fmt, "_u128")?,
109                        (sz, _) => bug!("unexpected unsigned int size u{sz}"),
110                    }
111                }
112                Ok(())
113            }
114        }
115    }
116}
117
118impl IntoDiagArg for ConstInt {
119    // FIXME this simply uses the Debug impl, but we could probably do better by converting both
120    // to an inherent method that returns `Cow`.
121    fn into_diag_arg(self) -> DiagArgValue {
122        DiagArgValue::Str(format!("{self:?}").into())
123    }
124}
125
126/// The raw bytes of a simple value.
127///
128/// This is a packed struct in order to allow this type to be optimally embedded in enums
129/// (like Scalar).
130#[derive(Clone, Copy, Eq, PartialEq, Hash)]
131#[repr(packed)]
132pub struct ScalarInt {
133    /// The first `size` bytes of `data` are the value.
134    /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
135    data: u128,
136    size: NonZero<u8>,
137}
138
139// Cannot derive these, as the derives take references to the fields, and we
140// can't take references to fields of packed structs.
141impl<CTX> crate::ty::HashStable<CTX> for ScalarInt {
142    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut crate::ty::StableHasher) {
143        // Using a block `{self.data}` here to force a copy instead of using `self.data`
144        // directly, because `hash_stable` takes `&self` and would thus borrow `self.data`.
145        // Since `Self` is a packed struct, that would create a possibly unaligned reference,
146        // which is UB.
147        { self.data }.hash_stable(hcx, hasher);
148        self.size.get().hash_stable(hcx, hasher);
149    }
150}
151
152impl<S: Encoder> Encodable<S> for ScalarInt {
153    fn encode(&self, s: &mut S) {
154        let size = self.size.get();
155        s.emit_u8(size);
156        s.emit_raw_bytes(&self.data.to_le_bytes()[..size as usize]);
157    }
158}
159
160impl<D: Decoder> Decodable<D> for ScalarInt {
161    fn decode(d: &mut D) -> ScalarInt {
162        let mut data = [0u8; 16];
163        let size = d.read_u8();
164        data[..size as usize].copy_from_slice(d.read_raw_bytes(size as usize));
165        ScalarInt { data: u128::from_le_bytes(data), size: NonZero::new(size).unwrap() }
166    }
167}
168
169impl ScalarInt {
170    pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
171    pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
172
173    fn raw(data: u128, size: Size) -> Self {
174        Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
175    }
176
177    #[inline]
178    pub fn size(self) -> Size {
179        Size::from_bytes(self.size.get())
180    }
181
182    /// Make sure the `data` fits in `size`.
183    /// This is guaranteed by all constructors here, but having had this check saved us from
184    /// bugs many times in the past, so keeping it around is definitely worth it.
185    #[inline(always)]
186    fn check_data(self) {
187        // Using a block `{self.data}` here to force a copy instead of using `self.data`
188        // directly, because `debug_assert_eq` takes references to its arguments and formatting
189        // arguments and would thus borrow `self.data`. Since `Self`
190        // is a packed struct, that would create a possibly unaligned reference, which
191        // is UB.
192        debug_assert_eq!(
193            self.size().truncate(self.data),
194            { self.data },
195            "Scalar value {:#x} exceeds size of {} bytes",
196            { self.data },
197            self.size
198        );
199    }
200
201    #[inline]
202    pub fn null(size: Size) -> Self {
203        Self::raw(0, size)
204    }
205
206    #[inline]
207    pub fn is_null(self) -> bool {
208        self.data == 0
209    }
210
211    #[inline]
212    pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
213        let (r, overflow) = Self::truncate_from_uint(i, size);
214        if overflow { None } else { Some(r) }
215    }
216
217    /// Returns the truncated result, and whether truncation changed the value.
218    #[inline]
219    pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
220        let data = i.into();
221        let r = Self::raw(size.truncate(data), size);
222        (r, r.data != data)
223    }
224
225    #[inline]
226    pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
227        let (r, overflow) = Self::truncate_from_int(i, size);
228        if overflow { None } else { Some(r) }
229    }
230
231    /// Returns the truncated result, and whether truncation changed the value.
232    #[inline]
233    pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
234        let data = i.into();
235        // `into` performed sign extension, we have to truncate
236        let r = Self::raw(size.truncate(data as u128), size);
237        (r, size.sign_extend(r.data) != data)
238    }
239
240    #[inline]
241    pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
242        Self::try_from_uint(i, tcx.data_layout.pointer_size)
243    }
244
245    /// Try to convert this ScalarInt to the raw underlying bits.
246    /// Fails if the size is wrong. Generally a wrong size should lead to a panic,
247    /// but Miri sometimes wants to be resilient to size mismatches,
248    /// so the interpreter will generally use this `try` method.
249    #[inline]
250    pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
251        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
252        if target_size.bytes() == u64::from(self.size.get()) {
253            self.check_data();
254            Ok(self.data)
255        } else {
256            Err(self.size())
257        }
258    }
259
260    #[inline]
261    pub fn to_bits(self, target_size: Size) -> u128 {
262        self.try_to_bits(target_size).unwrap_or_else(|size| {
263            bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
264        })
265    }
266
267    /// Extracts the bits from the scalar without checking the size.
268    #[inline]
269    pub fn to_bits_unchecked(self) -> u128 {
270        self.check_data();
271        self.data
272    }
273
274    /// Converts the `ScalarInt` to an unsigned integer of the given size.
275    /// Panics if the size of the `ScalarInt` is not equal to `size`.
276    #[inline]
277    pub fn to_uint(self, size: Size) -> u128 {
278        self.to_bits(size)
279    }
280
281    /// Converts the `ScalarInt` to `u8`.
282    /// Panics if the `size` of the `ScalarInt`in not equal to 1 byte.
283    #[inline]
284    pub fn to_u8(self) -> u8 {
285        self.to_uint(Size::from_bits(8)).try_into().unwrap()
286    }
287
288    /// Converts the `ScalarInt` to `u16`.
289    /// Panics if the size of the `ScalarInt` in not equal to 2 bytes.
290    #[inline]
291    pub fn to_u16(self) -> u16 {
292        self.to_uint(Size::from_bits(16)).try_into().unwrap()
293    }
294
295    /// Converts the `ScalarInt` to `u32`.
296    /// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes.
297    #[inline]
298    pub fn to_u32(self) -> u32 {
299        self.to_uint(Size::from_bits(32)).try_into().unwrap()
300    }
301
302    /// Converts the `ScalarInt` to `u64`.
303    /// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes.
304    #[inline]
305    pub fn to_u64(self) -> u64 {
306        self.to_uint(Size::from_bits(64)).try_into().unwrap()
307    }
308
309    /// Converts the `ScalarInt` to `u128`.
310    /// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes.
311    #[inline]
312    pub fn to_u128(self) -> u128 {
313        self.to_uint(Size::from_bits(128))
314    }
315
316    #[inline]
317    pub fn to_target_usize(&self, tcx: TyCtxt<'_>) -> u64 {
318        self.to_uint(tcx.data_layout.pointer_size).try_into().unwrap()
319    }
320
321    /// Converts the `ScalarInt` to `bool`.
322    /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte.
323    /// Errors if it is not a valid `bool`.
324    #[inline]
325    pub fn try_to_bool(self) -> Result<bool, ()> {
326        match self.to_u8() {
327            0 => Ok(false),
328            1 => Ok(true),
329            _ => Err(()),
330        }
331    }
332
333    /// Converts the `ScalarInt` to a signed integer of the given size.
334    /// Panics if the size of the `ScalarInt` is not equal to `size`.
335    #[inline]
336    pub fn to_int(self, size: Size) -> i128 {
337        let b = self.to_bits(size);
338        size.sign_extend(b)
339    }
340
341    /// Converts the `ScalarInt` to i8.
342    /// Panics if the size of the `ScalarInt` is not equal to 1 byte.
343    pub fn to_i8(self) -> i8 {
344        self.to_int(Size::from_bits(8)).try_into().unwrap()
345    }
346
347    /// Converts the `ScalarInt` to i16.
348    /// Panics if the size of the `ScalarInt` is not equal to 2 bytes.
349    pub fn to_i16(self) -> i16 {
350        self.to_int(Size::from_bits(16)).try_into().unwrap()
351    }
352
353    /// Converts the `ScalarInt` to i32.
354    /// Panics if the size of the `ScalarInt` is not equal to 4 bytes.
355    pub fn to_i32(self) -> i32 {
356        self.to_int(Size::from_bits(32)).try_into().unwrap()
357    }
358
359    /// Converts the `ScalarInt` to i64.
360    /// Panics if the size of the `ScalarInt` is not equal to 8 bytes.
361    pub fn to_i64(self) -> i64 {
362        self.to_int(Size::from_bits(64)).try_into().unwrap()
363    }
364
365    /// Converts the `ScalarInt` to i128.
366    /// Panics if the size of the `ScalarInt` is not equal to 16 bytes.
367    pub fn to_i128(self) -> i128 {
368        self.to_int(Size::from_bits(128))
369    }
370
371    #[inline]
372    pub fn to_target_isize(&self, tcx: TyCtxt<'_>) -> i64 {
373        self.to_int(tcx.data_layout.pointer_size).try_into().unwrap()
374    }
375
376    #[inline]
377    pub fn to_float<F: Float>(self) -> F {
378        // Going through `to_uint` to check size and truncation.
379        F::from_bits(self.to_bits(Size::from_bits(F::BITS)))
380    }
381
382    #[inline]
383    pub fn to_f16(self) -> Half {
384        self.to_float()
385    }
386
387    #[inline]
388    pub fn to_f32(self) -> Single {
389        self.to_float()
390    }
391
392    #[inline]
393    pub fn to_f64(self) -> Double {
394        self.to_float()
395    }
396
397    #[inline]
398    pub fn to_f128(self) -> Quad {
399        self.to_float()
400    }
401}
402
403macro_rules! from_x_for_scalar_int {
404    ($($ty:ty),*) => {
405        $(
406            impl From<$ty> for ScalarInt {
407                #[inline]
408                fn from(u: $ty) -> Self {
409                    Self {
410                        data: u128::from(u),
411                        size: NonZero::new(std::mem::size_of::<$ty>() as u8).unwrap(),
412                    }
413                }
414            }
415        )*
416    }
417}
418
419macro_rules! from_scalar_int_for_x {
420    ($($ty:ty),*) => {
421        $(
422            impl From<ScalarInt> for $ty {
423                #[inline]
424                fn from(int: ScalarInt) -> Self {
425                    // The `unwrap` cannot fail because to_bits (if it succeeds)
426                    // is guaranteed to return a value that fits into the size.
427                    int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
428                       .try_into().unwrap()
429                }
430            }
431        )*
432    }
433}
434
435from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool);
436from_scalar_int_for_x!(u8, u16, u32, u64, u128);
437
438impl TryFrom<ScalarInt> for bool {
439    type Error = ();
440    #[inline]
441    fn try_from(int: ScalarInt) -> Result<Self, ()> {
442        int.try_to_bool()
443    }
444}
445
446impl From<char> for ScalarInt {
447    #[inline]
448    fn from(c: char) -> Self {
449        (c as u32).into()
450    }
451}
452
453/// Error returned when a conversion from ScalarInt to char fails.
454#[derive(Debug)]
455pub struct CharTryFromScalarInt;
456
457impl TryFrom<ScalarInt> for char {
458    type Error = CharTryFromScalarInt;
459
460    #[inline]
461    fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
462        match char::from_u32(int.to_u32()) {
463            Some(c) => Ok(c),
464            None => Err(CharTryFromScalarInt),
465        }
466    }
467}
468
469impl From<Half> for ScalarInt {
470    #[inline]
471    fn from(f: Half) -> Self {
472        // We trust apfloat to give us properly truncated data.
473        Self { data: f.to_bits(), size: NonZero::new((Half::BITS / 8) as u8).unwrap() }
474    }
475}
476
477impl From<ScalarInt> for Half {
478    #[inline]
479    fn from(int: ScalarInt) -> Self {
480        Self::from_bits(int.to_bits(Size::from_bytes(2)))
481    }
482}
483
484impl From<Single> for ScalarInt {
485    #[inline]
486    fn from(f: Single) -> Self {
487        // We trust apfloat to give us properly truncated data.
488        Self { data: f.to_bits(), size: NonZero::new((Single::BITS / 8) as u8).unwrap() }
489    }
490}
491
492impl From<ScalarInt> for Single {
493    #[inline]
494    fn from(int: ScalarInt) -> Self {
495        Self::from_bits(int.to_bits(Size::from_bytes(4)))
496    }
497}
498
499impl From<Double> for ScalarInt {
500    #[inline]
501    fn from(f: Double) -> Self {
502        // We trust apfloat to give us properly truncated data.
503        Self { data: f.to_bits(), size: NonZero::new((Double::BITS / 8) as u8).unwrap() }
504    }
505}
506
507impl From<ScalarInt> for Double {
508    #[inline]
509    fn from(int: ScalarInt) -> Self {
510        Self::from_bits(int.to_bits(Size::from_bytes(8)))
511    }
512}
513
514impl From<Quad> for ScalarInt {
515    #[inline]
516    fn from(f: Quad) -> Self {
517        // We trust apfloat to give us properly truncated data.
518        Self { data: f.to_bits(), size: NonZero::new((Quad::BITS / 8) as u8).unwrap() }
519    }
520}
521
522impl From<ScalarInt> for Quad {
523    #[inline]
524    fn from(int: ScalarInt) -> Self {
525        Self::from_bits(int.to_bits(Size::from_bytes(16)))
526    }
527}
528
529impl fmt::Debug for ScalarInt {
530    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531        // Dispatch to LowerHex below.
532        write!(f, "0x{self:x}")
533    }
534}
535
536impl fmt::LowerHex for ScalarInt {
537    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538        self.check_data();
539        if f.alternate() {
540            // Like regular ints, alternate flag adds leading `0x`.
541            write!(f, "0x")?;
542        }
543        // Format as hex number wide enough to fit any value of the given `size`.
544        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
545        // Using a block `{self.data}` here to force a copy instead of using `self.data`
546        // directly, because `write!` takes references to its formatting arguments and
547        // would thus borrow `self.data`. Since `Self`
548        // is a packed struct, that would create a possibly unaligned reference, which
549        // is UB.
550        write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
551    }
552}
553
554impl fmt::UpperHex for ScalarInt {
555    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
556        self.check_data();
557        // Format as hex number wide enough to fit any value of the given `size`.
558        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
559        // Using a block `{self.data}` here to force a copy instead of using `self.data`
560        // directly, because `write!` takes references to its formatting arguments and
561        // would thus borrow `self.data`. Since `Self`
562        // is a packed struct, that would create a possibly unaligned reference, which
563        // is UB.
564        write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
565    }
566}
567
568impl fmt::Display for ScalarInt {
569    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
570        self.check_data();
571        write!(f, "{}", { self.data })
572    }
573}