]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_middle/src/ty/consts/int.rs
New upstream version 1.61.0+dfsg1
[rustc.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::convert::{TryFrom, TryInto};
6 use std::fmt;
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: u8,
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.hash_stable(hcx, hasher);
139 }
140 }
141
142 impl<S: Encoder> Encodable<S> for ScalarInt {
143 fn encode(&self, s: &mut S) -> Result<(), S::Error> {
144 s.emit_u128(self.data)?;
145 s.emit_u8(self.size)
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: d.read_u8() }
152 }
153 }
154
155 impl ScalarInt {
156 pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: 1 };
157
158 pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: 1 };
159
160 pub const ZST: ScalarInt = ScalarInt { data: 0_u128, size: 0 };
161
162 #[inline]
163 pub fn size(self) -> Size {
164 Size::from_bytes(self.size)
165 }
166
167 /// Make sure the `data` fits in `size`.
168 /// This is guaranteed by all constructors here, but having had this check saved us from
169 /// bugs many times in the past, so keeping it around is definitely worth it.
170 #[inline(always)]
171 fn check_data(self) {
172 // Using a block `{self.data}` here to force a copy instead of using `self.data`
173 // directly, because `debug_assert_eq` takes references to its arguments and formatting
174 // arguments and would thus borrow `self.data`. Since `Self`
175 // is a packed struct, that would create a possibly unaligned reference, which
176 // is UB.
177 debug_assert_eq!(
178 self.size().truncate(self.data),
179 { self.data },
180 "Scalar value {:#x} exceeds size of {} bytes",
181 { self.data },
182 self.size
183 );
184 }
185
186 #[inline]
187 pub fn null(size: Size) -> Self {
188 Self { data: 0, size: size.bytes() as u8 }
189 }
190
191 #[inline]
192 pub fn is_null(self) -> bool {
193 self.data == 0
194 }
195
196 #[inline]
197 pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
198 let data = i.into();
199 if size.truncate(data) == data {
200 Some(Self { data, size: size.bytes() as u8 })
201 } else {
202 None
203 }
204 }
205
206 #[inline]
207 pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
208 let i = i.into();
209 // `into` performed sign extension, we have to truncate
210 let truncated = size.truncate(i as u128);
211 if size.sign_extend(truncated) as i128 == i {
212 Some(Self { data: truncated, size: size.bytes() as u8 })
213 } else {
214 None
215 }
216 }
217
218 #[inline]
219 pub fn assert_bits(self, target_size: Size) -> u128 {
220 self.to_bits(target_size).unwrap_or_else(|size| {
221 bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
222 })
223 }
224
225 #[inline]
226 pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
227 assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
228 if target_size.bytes() == u64::from(self.size) {
229 self.check_data();
230 Ok(self.data)
231 } else {
232 Err(self.size())
233 }
234 }
235
236 #[inline]
237 pub fn try_to_machine_usize<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
238 Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
239 }
240 }
241
242 macro_rules! from {
243 ($($ty:ty),*) => {
244 $(
245 impl From<$ty> for ScalarInt {
246 #[inline]
247 fn from(u: $ty) -> Self {
248 Self {
249 data: u128::from(u),
250 size: std::mem::size_of::<$ty>() as u8,
251 }
252 }
253 }
254 )*
255 }
256 }
257
258 macro_rules! try_from {
259 ($($ty:ty),*) => {
260 $(
261 impl TryFrom<ScalarInt> for $ty {
262 type Error = Size;
263 #[inline]
264 fn try_from(int: ScalarInt) -> Result<Self, Size> {
265 // The `unwrap` cannot fail because to_bits (if it succeeds)
266 // is guaranteed to return a value that fits into the size.
267 int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
268 .map(|u| u.try_into().unwrap())
269 }
270 }
271 )*
272 }
273 }
274
275 from!(u8, u16, u32, u64, u128, bool);
276 try_from!(u8, u16, u32, u64, u128);
277
278 impl TryFrom<ScalarInt> for bool {
279 type Error = Size;
280 #[inline]
281 fn try_from(int: ScalarInt) -> Result<Self, Size> {
282 int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
283 0 => Ok(false),
284 1 => Ok(true),
285 _ => Err(Size::from_bytes(1)),
286 })
287 }
288 }
289
290 impl From<char> for ScalarInt {
291 #[inline]
292 fn from(c: char) -> Self {
293 Self { data: c as u128, size: std::mem::size_of::<char>() as u8 }
294 }
295 }
296
297 /// Error returned when a conversion from ScalarInt to char fails.
298 #[derive(Debug)]
299 pub struct CharTryFromScalarInt;
300
301 impl TryFrom<ScalarInt> for char {
302 type Error = CharTryFromScalarInt;
303
304 #[inline]
305 fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
306 let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
307 return Err(CharTryFromScalarInt);
308 };
309 match char::from_u32(bits.try_into().unwrap()) {
310 Some(c) => Ok(c),
311 None => Err(CharTryFromScalarInt),
312 }
313 }
314 }
315
316 impl From<Single> for ScalarInt {
317 #[inline]
318 fn from(f: Single) -> Self {
319 // We trust apfloat to give us properly truncated data.
320 Self { data: f.to_bits(), size: 4 }
321 }
322 }
323
324 impl TryFrom<ScalarInt> for Single {
325 type Error = Size;
326 #[inline]
327 fn try_from(int: ScalarInt) -> Result<Self, Size> {
328 int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
329 }
330 }
331
332 impl From<Double> for ScalarInt {
333 #[inline]
334 fn from(f: Double) -> Self {
335 // We trust apfloat to give us properly truncated data.
336 Self { data: f.to_bits(), size: 8 }
337 }
338 }
339
340 impl TryFrom<ScalarInt> for Double {
341 type Error = Size;
342 #[inline]
343 fn try_from(int: ScalarInt) -> Result<Self, Size> {
344 int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
345 }
346 }
347
348 impl fmt::Debug for ScalarInt {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 if self.size == 0 {
351 self.check_data();
352 write!(f, "<ZST>")
353 } else {
354 // Dispatch to LowerHex below.
355 write!(f, "0x{:x}", self)
356 }
357 }
358 }
359
360 impl fmt::LowerHex for ScalarInt {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 self.check_data();
363 // Format as hex number wide enough to fit any value of the given `size`.
364 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
365 // Using a block `{self.data}` here to force a copy instead of using `self.data`
366 // directly, because `write!` takes references to its formatting arguments and
367 // would thus borrow `self.data`. Since `Self`
368 // is a packed struct, that would create a possibly unaligned reference, which
369 // is UB.
370 write!(f, "{:01$x}", { self.data }, self.size as usize * 2)
371 }
372 }
373
374 impl fmt::UpperHex for ScalarInt {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 self.check_data();
377 // Format as hex number wide enough to fit any value of the given `size`.
378 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
379 // Using a block `{self.data}` here to force a copy instead of using `self.data`
380 // directly, because `write!` takes references to its formatting arguments and
381 // would thus borrow `self.data`. Since `Self`
382 // is a packed struct, that would create a possibly unaligned reference, which
383 // is UB.
384 write!(f, "{:01$X}", { self.data }, self.size as usize * 2)
385 }
386 }
387
388 impl fmt::Display for ScalarInt {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 self.check_data();
391 write!(f, "{}", { self.data })
392 }
393 }