]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/consts/int.rs
Auto merge of #105613 - Nilstrieb:rename-assert_uninit_valid, r=RalfJung
[rust.git] / compiler / rustc_middle / src / ty / consts / int.rs
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;
5 use std::fmt;
6 use std::num::NonZeroU8;
7
8 use crate::ty::TyCtxt;
9
10 #[derive(Copy, Clone)]
11 /// A type for representing any integer. Only used for printing.
12 pub struct ConstInt {
13     /// The "untyped" variant of `ConstInt`.
14     int: ScalarInt,
15     /// Whether the value is of a signed integer type.
16     signed: bool,
17     /// Whether the value is a `usize` or `isize` type.
18     is_ptr_sized_integral: bool,
19 }
20
21 impl ConstInt {
22     pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self {
23         Self { int, signed, is_ptr_sized_integral }
24     }
25 }
26
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();
31         let raw = int.data;
32         if signed {
33             let bit_size = size * 8;
34             let min = 1u128 << (bit_size - 1);
35             let max = min - 1;
36             if raw == min {
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),
45                 }
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),
55                 }
56             } else {
57                 match size {
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),
64                 }
65                 if fmt.alternate() {
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")?,
73                         _ => bug!(),
74                     }
75                 }
76                 Ok(())
77             }
78         } else {
79             let max = Size::from_bytes(size).truncate(u128::MAX);
80             if raw == 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),
89                 }
90             } else {
91                 match size {
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),
98                 }
99                 if fmt.alternate() {
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")?,
107                         _ => bug!(),
108                     }
109                 }
110                 Ok(())
111             }
112         }
113     }
114 }
115
116 /// The raw bytes of a simple value.
117 ///
118 /// This is a packed struct in order to allow this type to be optimally embedded in enums
119 /// (like Scalar).
120 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
121 #[repr(packed)]
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.
125     data: u128,
126     size: NonZeroU8,
127 }
128
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,
136         // which is UB.
137         { self.data }.hash_stable(hcx, hasher);
138         self.size.get().hash_stable(hcx, hasher);
139     }
140 }
141
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());
146     }
147 }
148
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() }
152     }
153 }
154
155 impl ScalarInt {
156     pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZeroU8::new(1).unwrap() };
157
158     pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZeroU8::new(1).unwrap() };
159
160     #[inline]
161     pub fn size(self) -> Size {
162         Size::from_bytes(self.size.get())
163     }
164
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.
168     #[inline(always)]
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
174         // is UB.
175         debug_assert_eq!(
176             self.size().truncate(self.data),
177             { self.data },
178             "Scalar value {:#x} exceeds size of {} bytes",
179             { self.data },
180             self.size
181         );
182     }
183
184     #[inline]
185     pub fn null(size: Size) -> Self {
186         Self { data: 0, size: NonZeroU8::new(size.bytes() as u8).unwrap() }
187     }
188
189     #[inline]
190     pub fn is_null(self) -> bool {
191         self.data == 0
192     }
193
194     #[inline]
195     pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
196         let data = i.into();
197         if size.truncate(data) == data {
198             Some(Self { data, size: NonZeroU8::new(size.bytes() as u8).unwrap() })
199         } else {
200             None
201         }
202     }
203
204     #[inline]
205     pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
206         let i = i.into();
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() })
211         } else {
212             None
213         }
214     }
215
216     #[inline]
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())
220         })
221     }
222
223     #[inline]
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()) {
227             self.check_data();
228             Ok(self.data)
229         } else {
230             Err(self.size())
231         }
232     }
233
234     #[inline]
235     pub fn try_to_machine_usize<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
236         Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
237     }
238
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.
242     #[inline]
243     pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
244         self.to_bits(size)
245     }
246
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.
250     #[inline]
251     pub fn try_to_bool(self) -> Result<bool, Size> {
252         match self.try_to_u8()? {
253             0 => Ok(false),
254             1 => Ok(true),
255             _ => Err(self.size()),
256         }
257     }
258
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
261     // that case.
262     #[inline]
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())
265     }
266
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
269     /// that case.
270     #[inline]
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())
273     }
274
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
277     /// that case.
278     #[inline]
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())
281     }
282
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
285     /// that case.
286     #[inline]
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())
289     }
290
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
293     /// that case.
294     #[inline]
295     pub fn try_to_u128(self) -> Result<u128, Size> {
296         self.to_bits(Size::from_bits(128))
297     }
298
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.
302     #[inline]
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)
306     }
307
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())
313     }
314
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())
320     }
321
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())
327     }
328
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())
334     }
335
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())
341     }
342 }
343
344 macro_rules! from {
345     ($($ty:ty),*) => {
346         $(
347             impl From<$ty> for ScalarInt {
348                 #[inline]
349                 fn from(u: $ty) -> Self {
350                     Self {
351                         data: u128::from(u),
352                         size: NonZeroU8::new(std::mem::size_of::<$ty>() as u8).unwrap(),
353                     }
354                 }
355             }
356         )*
357     }
358 }
359
360 macro_rules! try_from {
361     ($($ty:ty),*) => {
362         $(
363             impl TryFrom<ScalarInt> for $ty {
364                 type Error = Size;
365                 #[inline]
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())
371                 }
372             }
373         )*
374     }
375 }
376
377 from!(u8, u16, u32, u64, u128, bool);
378 try_from!(u8, u16, u32, u64, u128);
379
380 impl TryFrom<ScalarInt> for bool {
381     type Error = Size;
382     #[inline]
383     fn try_from(int: ScalarInt) -> Result<Self, Size> {
384         int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
385             0 => Ok(false),
386             1 => Ok(true),
387             _ => Err(Size::from_bytes(1)),
388         })
389     }
390 }
391
392 impl From<char> for ScalarInt {
393     #[inline]
394     fn from(c: char) -> Self {
395         Self { data: c as u128, size: NonZeroU8::new(std::mem::size_of::<char>() as u8).unwrap() }
396     }
397 }
398
399 /// Error returned when a conversion from ScalarInt to char fails.
400 #[derive(Debug)]
401 pub struct CharTryFromScalarInt;
402
403 impl TryFrom<ScalarInt> for char {
404     type Error = CharTryFromScalarInt;
405
406     #[inline]
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);
410         };
411         match char::from_u32(bits.try_into().unwrap()) {
412             Some(c) => Ok(c),
413             None => Err(CharTryFromScalarInt),
414         }
415     }
416 }
417
418 impl From<Single> for ScalarInt {
419     #[inline]
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() }
423     }
424 }
425
426 impl TryFrom<ScalarInt> for Single {
427     type Error = Size;
428     #[inline]
429     fn try_from(int: ScalarInt) -> Result<Self, Size> {
430         int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
431     }
432 }
433
434 impl From<Double> for ScalarInt {
435     #[inline]
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() }
439     }
440 }
441
442 impl TryFrom<ScalarInt> for Double {
443     type Error = Size;
444     #[inline]
445     fn try_from(int: ScalarInt) -> Result<Self, Size> {
446         int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
447     }
448 }
449
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)
454     }
455 }
456
457 impl fmt::LowerHex for ScalarInt {
458     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459         self.check_data();
460         if f.alternate() {
461             // Like regular ints, alternate flag adds leading `0x`.
462             write!(f, "0x")?;
463         }
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
470         // is UB.
471         write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
472     }
473 }
474
475 impl fmt::UpperHex for ScalarInt {
476     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477         self.check_data();
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
484         // is UB.
485         write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
486     }
487 }
488
489 impl fmt::Display for ScalarInt {
490     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491         self.check_data();
492         write!(f, "{}", { self.data })
493     }
494 }