]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/consts/int.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / consts / int.rs
CommitLineData
29967ef6
XL
1use rustc_apfloat::ieee::{Double, Single};
2use rustc_apfloat::Float;
3use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
136023e0 4use rustc_target::abi::Size;
29967ef6
XL
5use std::convert::{TryFrom, TryInto};
6use std::fmt;
064997fb 7use std::num::NonZeroU8;
3dfed10e 8
6a06907d
XL
9use crate::ty::TyCtxt;
10
3dfed10e
XL
11#[derive(Copy, Clone)]
12/// A type for representing any integer. Only used for printing.
3dfed10e 13pub struct ConstInt {
29967ef6
XL
14 /// The "untyped" variant of `ConstInt`.
15 int: ScalarInt,
3dfed10e
XL
16 /// Whether the value is of a signed integer type.
17 signed: bool,
18 /// Whether the value is a `usize` or `isize` type.
19 is_ptr_sized_integral: bool,
3dfed10e
XL
20}
21
22impl ConstInt {
29967ef6
XL
23 pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self {
24 Self { int, signed, is_ptr_sized_integral }
3dfed10e
XL
25 }
26}
27
28impl std::fmt::Debug for ConstInt {
29 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29967ef6
XL
30 let Self { int, signed, is_ptr_sized_integral } = *self;
31 let size = int.size().bytes();
32 let raw = int.data;
3dfed10e
XL
33 if signed {
34 let bit_size = size * 8;
35 let min = 1u128 << (bit_size - 1);
36 let max = min - 1;
37 if raw == min {
38 match (size, is_ptr_sized_integral) {
39 (_, true) => write!(fmt, "isize::MIN"),
40 (1, _) => write!(fmt, "i8::MIN"),
41 (2, _) => write!(fmt, "i16::MIN"),
42 (4, _) => write!(fmt, "i32::MIN"),
43 (8, _) => write!(fmt, "i64::MIN"),
44 (16, _) => write!(fmt, "i128::MIN"),
45 _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
46 }
47 } else if raw == max {
48 match (size, is_ptr_sized_integral) {
49 (_, true) => write!(fmt, "isize::MAX"),
50 (1, _) => write!(fmt, "i8::MAX"),
51 (2, _) => write!(fmt, "i16::MAX"),
52 (4, _) => write!(fmt, "i32::MAX"),
53 (8, _) => write!(fmt, "i64::MAX"),
54 (16, _) => write!(fmt, "i128::MAX"),
55 _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
56 }
57 } else {
58 match size {
59 1 => write!(fmt, "{}", raw as i8)?,
60 2 => write!(fmt, "{}", raw as i16)?,
61 4 => write!(fmt, "{}", raw as i32)?,
62 8 => write!(fmt, "{}", raw as i64)?,
63 16 => write!(fmt, "{}", raw as i128)?,
64 _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
65 }
66 if fmt.alternate() {
67 match (size, is_ptr_sized_integral) {
68 (_, true) => write!(fmt, "_isize")?,
69 (1, _) => write!(fmt, "_i8")?,
70 (2, _) => write!(fmt, "_i16")?,
71 (4, _) => write!(fmt, "_i32")?,
72 (8, _) => write!(fmt, "_i64")?,
73 (16, _) => write!(fmt, "_i128")?,
74 _ => bug!(),
75 }
76 }
77 Ok(())
78 }
79 } else {
29967ef6 80 let max = Size::from_bytes(size).truncate(u128::MAX);
3dfed10e
XL
81 if raw == max {
82 match (size, is_ptr_sized_integral) {
83 (_, true) => write!(fmt, "usize::MAX"),
84 (1, _) => write!(fmt, "u8::MAX"),
85 (2, _) => write!(fmt, "u16::MAX"),
86 (4, _) => write!(fmt, "u32::MAX"),
87 (8, _) => write!(fmt, "u64::MAX"),
88 (16, _) => write!(fmt, "u128::MAX"),
89 _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
90 }
91 } else {
92 match size {
93 1 => write!(fmt, "{}", raw as u8)?,
94 2 => write!(fmt, "{}", raw as u16)?,
95 4 => write!(fmt, "{}", raw as u32)?,
96 8 => write!(fmt, "{}", raw as u64)?,
97 16 => write!(fmt, "{}", raw as u128)?,
98 _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
99 }
100 if fmt.alternate() {
101 match (size, is_ptr_sized_integral) {
102 (_, true) => write!(fmt, "_usize")?,
103 (1, _) => write!(fmt, "_u8")?,
104 (2, _) => write!(fmt, "_u16")?,
105 (4, _) => write!(fmt, "_u32")?,
106 (8, _) => write!(fmt, "_u64")?,
107 (16, _) => write!(fmt, "_u128")?,
108 _ => bug!(),
109 }
110 }
111 Ok(())
112 }
113 }
114 }
115}
29967ef6
XL
116
117/// The raw bytes of a simple value.
118///
119/// This is a packed struct in order to allow this type to be optimally embedded in enums
120/// (like Scalar).
121#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
122#[repr(packed)]
123pub struct ScalarInt {
124 /// The first `size` bytes of `data` are the value.
125 /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
126 data: u128,
064997fb 127 size: NonZeroU8,
29967ef6
XL
128}
129
130// Cannot derive these, as the derives take references to the fields, and we
131// can't take references to fields of packed structs.
132impl<CTX> crate::ty::HashStable<CTX> for ScalarInt {
133 fn hash_stable(&self, hcx: &mut CTX, hasher: &mut crate::ty::StableHasher) {
134 // Using a block `{self.data}` here to force a copy instead of using `self.data`
135 // directly, because `hash_stable` takes `&self` and would thus borrow `self.data`.
136 // Since `Self` is a packed struct, that would create a possibly unaligned reference,
137 // which is UB.
138 { self.data }.hash_stable(hcx, hasher);
064997fb 139 self.size.get().hash_stable(hcx, hasher);
29967ef6
XL
140 }
141}
142
143impl<S: Encoder> Encodable<S> for ScalarInt {
923072b8
FG
144 fn encode(&self, s: &mut S) {
145 s.emit_u128(self.data);
064997fb 146 s.emit_u8(self.size.get());
29967ef6
XL
147 }
148}
149
150impl<D: Decoder> Decodable<D> for ScalarInt {
5099ac24 151 fn decode(d: &mut D) -> ScalarInt {
064997fb 152 ScalarInt { data: d.read_u128(), size: NonZeroU8::new(d.read_u8()).unwrap() }
29967ef6
XL
153 }
154}
155
156impl ScalarInt {
064997fb 157 pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZeroU8::new(1).unwrap() };
29967ef6 158
064997fb 159 pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZeroU8::new(1).unwrap() };
29967ef6
XL
160
161 #[inline]
162 pub fn size(self) -> Size {
064997fb 163 Size::from_bytes(self.size.get())
29967ef6
XL
164 }
165
166 /// Make sure the `data` fits in `size`.
167 /// This is guaranteed by all constructors here, but having had this check saved us from
168 /// bugs many times in the past, so keeping it around is definitely worth it.
169 #[inline(always)]
170 fn check_data(self) {
171 // Using a block `{self.data}` here to force a copy instead of using `self.data`
172 // directly, because `debug_assert_eq` takes references to its arguments and formatting
173 // arguments and would thus borrow `self.data`. Since `Self`
174 // is a packed struct, that would create a possibly unaligned reference, which
175 // is UB.
176 debug_assert_eq!(
177 self.size().truncate(self.data),
178 { self.data },
179 "Scalar value {:#x} exceeds size of {} bytes",
180 { self.data },
181 self.size
182 );
183 }
184
185 #[inline]
186 pub fn null(size: Size) -> Self {
064997fb 187 Self { data: 0, size: NonZeroU8::new(size.bytes() as u8).unwrap() }
29967ef6
XL
188 }
189
190 #[inline]
191 pub fn is_null(self) -> bool {
192 self.data == 0
193 }
194
29967ef6
XL
195 #[inline]
196 pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
197 let data = i.into();
198 if size.truncate(data) == data {
064997fb 199 Some(Self { data, size: NonZeroU8::new(size.bytes() as u8).unwrap() })
29967ef6
XL
200 } else {
201 None
202 }
203 }
204
205 #[inline]
206 pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
207 let i = i.into();
208 // `into` performed sign extension, we have to truncate
209 let truncated = size.truncate(i as u128);
210 if size.sign_extend(truncated) as i128 == i {
064997fb 211 Some(Self { data: truncated, size: NonZeroU8::new(size.bytes() as u8).unwrap() })
29967ef6
XL
212 } else {
213 None
214 }
215 }
216
217 #[inline]
218 pub fn assert_bits(self, target_size: Size) -> u128 {
219 self.to_bits(target_size).unwrap_or_else(|size| {
220 bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
221 })
222 }
223
224 #[inline]
225 pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
226 assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
064997fb 227 if target_size.bytes() == u64::from(self.size.get()) {
29967ef6
XL
228 self.check_data();
229 Ok(self.data)
230 } else {
231 Err(self.size())
232 }
233 }
6a06907d
XL
234
235 #[inline]
a2a8927a 236 pub fn try_to_machine_usize<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
6a06907d
XL
237 Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
238 }
04454e1e
FG
239
240 /// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
241 /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
242 /// `ScalarInt`s size in that case.
243 #[inline]
244 pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
245 self.to_bits(size)
246 }
247
487cf647
FG
248 // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
249 // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
250 // value of the `ScalarInt` in that case.
251 #[inline]
252 pub fn try_to_bool(self) -> Result<bool, Size> {
253 match self.try_to_u8()? {
254 0 => Ok(false),
255 1 => Ok(true),
256 _ => Err(self.size()),
257 }
258 }
259
04454e1e
FG
260 // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
261 // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
262 // that case.
263 #[inline]
264 pub fn try_to_u8(self) -> Result<u8, Size> {
265 self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
266 }
267
268 /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
269 /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
270 /// that case.
271 #[inline]
272 pub fn try_to_u16(self) -> Result<u16, Size> {
273 self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
274 }
275
276 /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
277 /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
278 /// that case.
279 #[inline]
280 pub fn try_to_u32(self) -> Result<u32, Size> {
281 self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
282 }
283
284 /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
285 /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
286 /// that case.
287 #[inline]
288 pub fn try_to_u64(self) -> Result<u64, Size> {
289 self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
290 }
291
292 /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
293 /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
294 /// that case.
295 #[inline]
296 pub fn try_to_u128(self) -> Result<u128, Size> {
297 self.to_bits(Size::from_bits(128))
298 }
299
300 /// Tries to convert the `ScalarInt` to a signed integer of the given size.
301 /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
302 /// `ScalarInt`s size in that case.
303 #[inline]
304 pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
305 let b = self.to_bits(size)?;
306 Ok(size.sign_extend(b) as i128)
307 }
308
309 /// Tries to convert the `ScalarInt` to i8.
310 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 1 }`
311 /// and returns the `ScalarInt`s size in that case.
312 pub fn try_to_i8(self) -> Result<i8, Size> {
313 self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
314 }
315
316 /// Tries to convert the `ScalarInt` to i16.
317 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 2 }`
318 /// and returns the `ScalarInt`s size in that case.
319 pub fn try_to_i16(self) -> Result<i16, Size> {
320 self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
321 }
322
323 /// Tries to convert the `ScalarInt` to i32.
324 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 4 }`
325 /// and returns the `ScalarInt`s size in that case.
326 pub fn try_to_i32(self) -> Result<i32, Size> {
327 self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
328 }
329
330 /// Tries to convert the `ScalarInt` to i64.
331 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 8 }`
332 /// and returns the `ScalarInt`s size in that case.
333 pub fn try_to_i64(self) -> Result<i64, Size> {
334 self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
335 }
336
337 /// Tries to convert the `ScalarInt` to i128.
338 /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 16 }`
339 /// and returns the `ScalarInt`s size in that case.
340 pub fn try_to_i128(self) -> Result<i128, Size> {
341 self.try_to_int(Size::from_bits(128)).map(|v| i128::try_from(v).unwrap())
342 }
29967ef6
XL
343}
344
345macro_rules! from {
346 ($($ty:ty),*) => {
347 $(
348 impl From<$ty> for ScalarInt {
349 #[inline]
350 fn from(u: $ty) -> Self {
351 Self {
352 data: u128::from(u),
064997fb 353 size: NonZeroU8::new(std::mem::size_of::<$ty>() as u8).unwrap(),
29967ef6
XL
354 }
355 }
356 }
357 )*
358 }
359}
360
361macro_rules! try_from {
362 ($($ty:ty),*) => {
363 $(
364 impl TryFrom<ScalarInt> for $ty {
365 type Error = Size;
366 #[inline]
367 fn try_from(int: ScalarInt) -> Result<Self, Size> {
368 // The `unwrap` cannot fail because to_bits (if it succeeds)
369 // is guaranteed to return a value that fits into the size.
370 int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
371 .map(|u| u.try_into().unwrap())
372 }
373 }
374 )*
375 }
376}
377
378from!(u8, u16, u32, u64, u128, bool);
379try_from!(u8, u16, u32, u64, u128);
380
6a06907d
XL
381impl TryFrom<ScalarInt> for bool {
382 type Error = Size;
383 #[inline]
384 fn try_from(int: ScalarInt) -> Result<Self, Size> {
385 int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
386 0 => Ok(false),
387 1 => Ok(true),
388 _ => Err(Size::from_bytes(1)),
389 })
390 }
391}
392
29967ef6
XL
393impl From<char> for ScalarInt {
394 #[inline]
395 fn from(c: char) -> Self {
064997fb 396 Self { data: c as u128, size: NonZeroU8::new(std::mem::size_of::<char>() as u8).unwrap() }
29967ef6
XL
397 }
398}
399
5099ac24
FG
400/// Error returned when a conversion from ScalarInt to char fails.
401#[derive(Debug)]
402pub struct CharTryFromScalarInt;
403
29967ef6 404impl TryFrom<ScalarInt> for char {
5099ac24
FG
405 type Error = CharTryFromScalarInt;
406
29967ef6 407 #[inline]
5099ac24
FG
408 fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
409 let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
410 return Err(CharTryFromScalarInt);
411 };
412 match char::from_u32(bits.try_into().unwrap()) {
413 Some(c) => Ok(c),
414 None => Err(CharTryFromScalarInt),
415 }
29967ef6
XL
416 }
417}
418
419impl From<Single> for ScalarInt {
420 #[inline]
421 fn from(f: Single) -> Self {
422 // We trust apfloat to give us properly truncated data.
064997fb 423 Self { data: f.to_bits(), size: NonZeroU8::new((Single::BITS / 8) as u8).unwrap() }
29967ef6
XL
424 }
425}
426
427impl TryFrom<ScalarInt> for Single {
428 type Error = Size;
429 #[inline]
430 fn try_from(int: ScalarInt) -> Result<Self, Size> {
431 int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
432 }
433}
434
435impl From<Double> for ScalarInt {
436 #[inline]
437 fn from(f: Double) -> Self {
438 // We trust apfloat to give us properly truncated data.
064997fb 439 Self { data: f.to_bits(), size: NonZeroU8::new((Double::BITS / 8) as u8).unwrap() }
29967ef6
XL
440 }
441}
442
443impl TryFrom<ScalarInt> for Double {
444 type Error = Size;
445 #[inline]
446 fn try_from(int: ScalarInt) -> Result<Self, Size> {
447 int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
448 }
449}
450
451impl fmt::Debug for ScalarInt {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
064997fb
FG
453 // Dispatch to LowerHex below.
454 write!(f, "0x{:x}", self)
29967ef6
XL
455 }
456}
457
458impl fmt::LowerHex for ScalarInt {
459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 self.check_data();
064997fb
FG
461 if f.alternate() {
462 // Like regular ints, alternate flag adds leading `0x`.
463 write!(f, "0x")?;
464 }
29967ef6
XL
465 // Format as hex number wide enough to fit any value of the given `size`.
466 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
467 // Using a block `{self.data}` here to force a copy instead of using `self.data`
468 // directly, because `write!` takes references to its formatting arguments and
469 // would thus borrow `self.data`. Since `Self`
470 // is a packed struct, that would create a possibly unaligned reference, which
471 // is UB.
064997fb 472 write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
29967ef6
XL
473 }
474}
475
476impl fmt::UpperHex for ScalarInt {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 self.check_data();
479 // Format as hex number wide enough to fit any value of the given `size`.
480 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
481 // Using a block `{self.data}` here to force a copy instead of using `self.data`
482 // directly, because `write!` takes references to its formatting arguments and
483 // would thus borrow `self.data`. Since `Self`
484 // is a packed struct, that would create a possibly unaligned reference, which
485 // is UB.
064997fb 486 write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
29967ef6
XL
487 }
488}
5e7ed085
FG
489
490impl fmt::Display for ScalarInt {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 self.check_data();
493 write!(f, "{}", { self.data })
494 }
495}