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