]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/consts/int.rs
New upstream version 1.52.0~beta.3+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};
4use rustc_target::abi::{Size, TargetDataLayout};
5use std::convert::{TryFrom, TryInto};
6use std::fmt;
3dfed10e 7
6a06907d
XL
8use crate::ty::TyCtxt;
9
3dfed10e
XL
10#[derive(Copy, Clone)]
11/// A type for representing any integer. Only used for printing.
3dfed10e 12pub struct ConstInt {
29967ef6
XL
13 /// The "untyped" variant of `ConstInt`.
14 int: ScalarInt,
3dfed10e
XL
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,
3dfed10e
XL
19}
20
21impl ConstInt {
29967ef6
XL
22 pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self {
23 Self { int, signed, is_ptr_sized_integral }
3dfed10e
XL
24 }
25}
26
27impl std::fmt::Debug for ConstInt {
28 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29967ef6
XL
29 let Self { int, signed, is_ptr_sized_integral } = *self;
30 let size = int.size().bytes();
31 let raw = int.data;
3dfed10e
XL
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 {
29967ef6 79 let max = Size::from_bytes(size).truncate(u128::MAX);
3dfed10e
XL
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}
29967ef6
XL
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)]
122pub 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.
131impl<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
142impl<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
149impl<D: Decoder> Decodable<D> for ScalarInt {
150 fn decode(d: &mut D) -> Result<ScalarInt, D::Error> {
151 Ok(ScalarInt { data: d.read_u128()?, size: d.read_u8()? })
152 }
153}
154
155impl 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 pub(crate) fn ptr_sized_op<E>(
197 self,
198 dl: &TargetDataLayout,
199 f_int: impl FnOnce(u64) -> Result<u64, E>,
200 ) -> Result<Self, E> {
201 assert_eq!(u64::from(self.size), dl.pointer_size.bytes());
202 Ok(Self::try_from_uint(f_int(u64::try_from(self.data).unwrap())?, self.size()).unwrap())
203 }
204
205 #[inline]
206 pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
207 let data = i.into();
208 if size.truncate(data) == data {
209 Some(Self { data, size: size.bytes() as u8 })
210 } else {
211 None
212 }
213 }
214
215 #[inline]
216 pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
217 let i = i.into();
218 // `into` performed sign extension, we have to truncate
219 let truncated = size.truncate(i as u128);
220 if size.sign_extend(truncated) as i128 == i {
221 Some(Self { data: truncated, size: size.bytes() as u8 })
222 } else {
223 None
224 }
225 }
226
227 #[inline]
228 pub fn assert_bits(self, target_size: Size) -> u128 {
229 self.to_bits(target_size).unwrap_or_else(|size| {
230 bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
231 })
232 }
233
234 #[inline]
235 pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
236 assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
237 if target_size.bytes() == u64::from(self.size) {
238 self.check_data();
239 Ok(self.data)
240 } else {
241 Err(self.size())
242 }
243 }
6a06907d
XL
244
245 #[inline]
246 pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
247 Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
248 }
29967ef6
XL
249}
250
251macro_rules! from {
252 ($($ty:ty),*) => {
253 $(
254 impl From<$ty> for ScalarInt {
255 #[inline]
256 fn from(u: $ty) -> Self {
257 Self {
258 data: u128::from(u),
259 size: std::mem::size_of::<$ty>() as u8,
260 }
261 }
262 }
263 )*
264 }
265}
266
267macro_rules! try_from {
268 ($($ty:ty),*) => {
269 $(
270 impl TryFrom<ScalarInt> for $ty {
271 type Error = Size;
272 #[inline]
273 fn try_from(int: ScalarInt) -> Result<Self, Size> {
274 // The `unwrap` cannot fail because to_bits (if it succeeds)
275 // is guaranteed to return a value that fits into the size.
276 int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
277 .map(|u| u.try_into().unwrap())
278 }
279 }
280 )*
281 }
282}
283
284from!(u8, u16, u32, u64, u128, bool);
285try_from!(u8, u16, u32, u64, u128);
286
6a06907d
XL
287impl TryFrom<ScalarInt> for bool {
288 type Error = Size;
289 #[inline]
290 fn try_from(int: ScalarInt) -> Result<Self, Size> {
291 int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
292 0 => Ok(false),
293 1 => Ok(true),
294 _ => Err(Size::from_bytes(1)),
295 })
296 }
297}
298
29967ef6
XL
299impl From<char> for ScalarInt {
300 #[inline]
301 fn from(c: char) -> Self {
302 Self { data: c as u128, size: std::mem::size_of::<char>() as u8 }
303 }
304}
305
306impl TryFrom<ScalarInt> for char {
307 type Error = Size;
308 #[inline]
309 fn try_from(int: ScalarInt) -> Result<Self, Size> {
310 int.to_bits(Size::from_bytes(std::mem::size_of::<char>()))
311 .map(|u| char::from_u32(u.try_into().unwrap()).unwrap())
312 }
313}
314
315impl From<Single> for ScalarInt {
316 #[inline]
317 fn from(f: Single) -> Self {
318 // We trust apfloat to give us properly truncated data.
319 Self { data: f.to_bits(), size: 4 }
320 }
321}
322
323impl TryFrom<ScalarInt> for Single {
324 type Error = Size;
325 #[inline]
326 fn try_from(int: ScalarInt) -> Result<Self, Size> {
327 int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
328 }
329}
330
331impl From<Double> for ScalarInt {
332 #[inline]
333 fn from(f: Double) -> Self {
334 // We trust apfloat to give us properly truncated data.
335 Self { data: f.to_bits(), size: 8 }
336 }
337}
338
339impl TryFrom<ScalarInt> for Double {
340 type Error = Size;
341 #[inline]
342 fn try_from(int: ScalarInt) -> Result<Self, Size> {
343 int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
344 }
345}
346
347impl fmt::Debug for ScalarInt {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 if self.size == 0 {
350 self.check_data();
351 write!(f, "<ZST>")
352 } else {
353 // Dispatch to LowerHex below.
354 write!(f, "0x{:x}", self)
355 }
356 }
357}
358
359impl fmt::LowerHex for ScalarInt {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 self.check_data();
362 // Format as hex number wide enough to fit any value of the given `size`.
363 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
364 // Using a block `{self.data}` here to force a copy instead of using `self.data`
365 // directly, because `write!` takes references to its formatting arguments and
366 // would thus borrow `self.data`. Since `Self`
367 // is a packed struct, that would create a possibly unaligned reference, which
368 // is UB.
369 write!(f, "{:01$x}", { self.data }, self.size as usize * 2)
370 }
371}
372
373impl fmt::UpperHex for ScalarInt {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 self.check_data();
376 // Format as hex number wide enough to fit any value of the given `size`.
377 // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
378 // Using a block `{self.data}` here to force a copy instead of using `self.data`
379 // directly, because `write!` takes references to its formatting arguments and
380 // would thus borrow `self.data`. Since `Self`
381 // is a packed struct, that would create a possibly unaligned reference, which
382 // is UB.
383 write!(f, "{:01$X}", { self.data }, self.size as usize * 2)
384 }
385}