]>
Commit | Line | Data |
---|---|---|
6a06907d | 1 | use std::convert::{TryFrom, TryInto}; |
ba9703b0 XL |
2 | use std::fmt; |
3 | ||
dfeec247 XL |
4 | use rustc_apfloat::{ |
5 | ieee::{Double, Single}, | |
6 | Float, | |
7 | }; | |
532ac7d7 | 8 | use rustc_macros::HashStable; |
136023e0 | 9 | use rustc_target::abi::{HasDataLayout, Size}; |
ea8adc8c | 10 | |
6a06907d | 11 | use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt}; |
ea8adc8c | 12 | |
136023e0 | 13 | use super::{ |
5e7ed085 | 14 | AllocId, AllocRange, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance, |
04454e1e | 15 | ScalarSizeMismatch, |
136023e0 | 16 | }; |
94b46f34 | 17 | |
1b1a35ee | 18 | /// Represents the result of const evaluation via the `eval_to_allocation` query. |
6a06907d | 19 | #[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)] |
1b1a35ee | 20 | pub struct ConstAlloc<'tcx> { |
94222f64 | 21 | // the value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory` |
a1dfa0c6 XL |
22 | // (so you can use `AllocMap::unwrap_memory`). |
23 | pub alloc_id: AllocId, | |
24 | pub ty: Ty<'tcx>, | |
25 | } | |
26 | ||
e1599b0c XL |
27 | /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for |
28 | /// array length computations, enum discriminants and the pattern matching logic. | |
3dfed10e | 29 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] |
ba9703b0 | 30 | #[derive(HashStable)] |
94b46f34 | 31 | pub enum ConstValue<'tcx> { |
9fa01778 | 32 | /// Used only for types with `layout::abi::Scalar` ABI and ZSTs. |
b7449926 | 33 | /// |
f9f354fc | 34 | /// Not using the enum `Value` to encode that this must not be `Uninit`. |
94b46f34 | 35 | Scalar(Scalar), |
0bf4aa26 | 36 | |
dc9dc135 | 37 | /// Used only for `&[u8]` and `&str` |
5e7ed085 | 38 | Slice { data: ConstAllocation<'tcx>, start: usize, end: usize }, |
9fa01778 | 39 | |
dc9dc135 XL |
40 | /// A value not represented/representable by `Scalar` or `Slice` |
41 | ByRef { | |
dc9dc135 | 42 | /// The backing memory of the value, may contain more memory than needed for just the value |
5e7ed085 FG |
43 | /// in order to share `ConstAllocation`s between values |
44 | alloc: ConstAllocation<'tcx>, | |
416331ca XL |
45 | /// Offset into `alloc` |
46 | offset: Size, | |
dc9dc135 | 47 | }, |
94b46f34 XL |
48 | } |
49 | ||
6a06907d | 50 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
dc9dc135 | 51 | static_assert_size!(ConstValue<'_>, 32); |
9fa01778 | 52 | |
6a06907d XL |
53 | impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> { |
54 | type Lifted = ConstValue<'tcx>; | |
55 | fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ConstValue<'tcx>> { | |
56 | Some(match self { | |
57 | ConstValue::Scalar(s) => ConstValue::Scalar(s), | |
58 | ConstValue::Slice { data, start, end } => { | |
59 | ConstValue::Slice { data: tcx.lift(data)?, start, end } | |
60 | } | |
61 | ConstValue::ByRef { alloc, offset } => { | |
62 | ConstValue::ByRef { alloc: tcx.lift(alloc)?, offset } | |
63 | } | |
64 | }) | |
65 | } | |
66 | } | |
67 | ||
94b46f34 XL |
68 | impl<'tcx> ConstValue<'tcx> { |
69 | #[inline] | |
136023e0 | 70 | pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> { |
94b46f34 | 71 | match *self { |
dfeec247 | 72 | ConstValue::ByRef { .. } | ConstValue::Slice { .. } => None, |
94b46f34 XL |
73 | ConstValue::Scalar(val) => Some(val), |
74 | } | |
75 | } | |
74b04a01 | 76 | |
6a06907d XL |
77 | pub fn try_to_scalar_int(&self) -> Option<ScalarInt> { |
78 | Some(self.try_to_scalar()?.assert_int()) | |
79 | } | |
80 | ||
74b04a01 | 81 | pub fn try_to_bits(&self, size: Size) -> Option<u128> { |
6a06907d | 82 | self.try_to_scalar_int()?.to_bits(size).ok() |
74b04a01 XL |
83 | } |
84 | ||
3dfed10e | 85 | pub fn try_to_bool(&self) -> Option<bool> { |
6a06907d | 86 | self.try_to_scalar_int()?.try_into().ok() |
3dfed10e XL |
87 | } |
88 | ||
89 | pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> { | |
6a06907d | 90 | self.try_to_scalar_int()?.try_to_machine_usize(tcx).ok() |
3dfed10e XL |
91 | } |
92 | ||
74b04a01 XL |
93 | pub fn try_to_bits_for_ty( |
94 | &self, | |
95 | tcx: TyCtxt<'tcx>, | |
96 | param_env: ParamEnv<'tcx>, | |
97 | ty: Ty<'tcx>, | |
98 | ) -> Option<u128> { | |
3dfed10e | 99 | let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; |
74b04a01 XL |
100 | self.try_to_bits(size) |
101 | } | |
102 | ||
103 | pub fn from_bool(b: bool) -> Self { | |
104 | ConstValue::Scalar(Scalar::from_bool(b)) | |
105 | } | |
106 | ||
107 | pub fn from_u64(i: u64) -> Self { | |
108 | ConstValue::Scalar(Scalar::from_u64(i)) | |
109 | } | |
110 | ||
111 | pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self { | |
112 | ConstValue::Scalar(Scalar::from_machine_usize(i, cx)) | |
113 | } | |
923072b8 FG |
114 | |
115 | pub fn zst() -> Self { | |
116 | Self::Scalar(Scalar::ZST) | |
117 | } | |
0531ce1d XL |
118 | } |
119 | ||
0bf4aa26 | 120 | /// A `Scalar` represents an immediate, primitive value existing outside of a |
94222f64 | 121 | /// `memory::Allocation`. It is in many ways like a small chunk of an `Allocation`, up to 16 bytes in |
0bf4aa26 XL |
122 | /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes |
123 | /// of a simple value or a pointer into another `Allocation` | |
136023e0 XL |
124 | /// |
125 | /// These variants would be private if there was a convenient way to achieve that in Rust. | |
126 | /// Do *not* match on a `Scalar`! Use the various `to_*` methods instead. | |
3dfed10e | 127 | #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] |
ba9703b0 | 128 | #[derive(HashStable)] |
136023e0 | 129 | pub enum Scalar<Tag = AllocId> { |
0bf4aa26 | 130 | /// The raw bytes of a simple value. |
29967ef6 | 131 | Int(ScalarInt), |
0bf4aa26 XL |
132 | |
133 | /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of | |
134 | /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the | |
135 | /// relocation and its associated offset together as a `Pointer` here. | |
136023e0 XL |
136 | /// |
137 | /// We also store the size of the pointer, such that a `Scalar` always knows how big it is. | |
138 | /// The size is always the pointer size of the current target, but this is not information | |
139 | /// that we always have readily available. | |
140 | Ptr(Pointer<Tag>, u8), | |
0bf4aa26 XL |
141 | } |
142 | ||
6a06907d | 143 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
48663c56 | 144 | static_assert_size!(Scalar, 24); |
9fa01778 | 145 | |
f9f354fc XL |
146 | // We want the `Debug` output to be readable as it is used by `derive(Debug)` for |
147 | // all the Miri types. | |
136023e0 | 148 | impl<Tag: Provenance> fmt::Debug for Scalar<Tag> { |
dc9dc135 XL |
149 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
150 | match self { | |
136023e0 | 151 | Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr), |
29967ef6 | 152 | Scalar::Int(int) => write!(f, "{:?}", int), |
dc9dc135 XL |
153 | } |
154 | } | |
155 | } | |
156 | ||
136023e0 | 157 | impl<Tag: Provenance> fmt::Display for Scalar<Tag> { |
a1dfa0c6 XL |
158 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
159 | match self { | |
136023e0 | 160 | Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), |
5e7ed085 FG |
161 | Scalar::Int(int) => write!(f, "{}", int), |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> { | |
167 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
168 | match self { | |
169 | Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), | |
170 | Scalar::Int(int) => write!(f, "0x{:x}", int), | |
a1dfa0c6 XL |
171 | } |
172 | } | |
173 | } | |
174 | ||
dc9dc135 XL |
175 | impl<Tag> From<Single> for Scalar<Tag> { |
176 | #[inline(always)] | |
177 | fn from(f: Single) -> Self { | |
178 | Scalar::from_f32(f) | |
179 | } | |
180 | } | |
181 | ||
182 | impl<Tag> From<Double> for Scalar<Tag> { | |
183 | #[inline(always)] | |
184 | fn from(f: Double) -> Self { | |
185 | Scalar::from_f64(f) | |
186 | } | |
187 | } | |
188 | ||
136023e0 XL |
189 | impl<Tag> From<ScalarInt> for Scalar<Tag> { |
190 | #[inline(always)] | |
191 | fn from(ptr: ScalarInt) -> Self { | |
192 | Scalar::Int(ptr) | |
0bf4aa26 XL |
193 | } |
194 | } | |
195 | ||
136023e0 | 196 | impl<Tag> Scalar<Tag> { |
29967ef6 XL |
197 | pub const ZST: Self = Scalar::Int(ScalarInt::ZST); |
198 | ||
ba9703b0 | 199 | #[inline(always)] |
136023e0 XL |
200 | pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { |
201 | Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap()) | |
ba9703b0 XL |
202 | } |
203 | ||
136023e0 XL |
204 | /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer). |
205 | pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { | |
206 | match ptr.into_parts() { | |
207 | (Some(tag), offset) => Scalar::from_pointer(Pointer::new(tag, offset), cx), | |
208 | (None, offset) => { | |
209 | Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap()) | |
210 | } | |
211 | } | |
ea8adc8c XL |
212 | } |
213 | ||
b7449926 | 214 | #[inline] |
136023e0 XL |
215 | pub fn null_ptr(cx: &impl HasDataLayout) -> Self { |
216 | Scalar::Int(ScalarInt::null(cx.pointer_size())) | |
ea8adc8c XL |
217 | } |
218 | ||
b7449926 XL |
219 | #[inline] |
220 | pub fn from_bool(b: bool) -> Self { | |
29967ef6 | 221 | Scalar::Int(b.into()) |
ea8adc8c XL |
222 | } |
223 | ||
b7449926 XL |
224 | #[inline] |
225 | pub fn from_char(c: char) -> Self { | |
29967ef6 | 226 | Scalar::Int(c.into()) |
ea8adc8c | 227 | } |
ea8adc8c | 228 | |
dfeec247 XL |
229 | #[inline] |
230 | pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> { | |
29967ef6 | 231 | ScalarInt::try_from_uint(i, size).map(Scalar::Int) |
dfeec247 XL |
232 | } |
233 | ||
b7449926 XL |
234 | #[inline] |
235 | pub fn from_uint(i: impl Into<u128>, size: Size) -> Self { | |
236 | let i = i.into(); | |
dfeec247 XL |
237 | Self::try_from_uint(i, size) |
238 | .unwrap_or_else(|| bug!("Unsigned value {:#x} does not fit in {} bits", i, size.bits())) | |
dc9dc135 XL |
239 | } |
240 | ||
241 | #[inline] | |
242 | pub fn from_u8(i: u8) -> Self { | |
29967ef6 | 243 | Scalar::Int(i.into()) |
dc9dc135 XL |
244 | } |
245 | ||
246 | #[inline] | |
247 | pub fn from_u16(i: u16) -> Self { | |
29967ef6 | 248 | Scalar::Int(i.into()) |
dc9dc135 XL |
249 | } |
250 | ||
251 | #[inline] | |
252 | pub fn from_u32(i: u32) -> Self { | |
29967ef6 | 253 | Scalar::Int(i.into()) |
dc9dc135 XL |
254 | } |
255 | ||
256 | #[inline] | |
257 | pub fn from_u64(i: u64) -> Self { | |
29967ef6 | 258 | Scalar::Int(i.into()) |
ea8adc8c | 259 | } |
ea8adc8c | 260 | |
74b04a01 XL |
261 | #[inline] |
262 | pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self { | |
263 | Self::from_uint(i, cx.data_layout().pointer_size) | |
264 | } | |
265 | ||
b7449926 | 266 | #[inline] |
dfeec247 | 267 | pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> { |
29967ef6 | 268 | ScalarInt::try_from_int(i, size).map(Scalar::Int) |
dfeec247 XL |
269 | } |
270 | ||
271 | #[inline] | |
272 | pub fn from_int(i: impl Into<i128>, size: Size) -> Self { | |
273 | let i = i.into(); | |
274 | Self::try_from_int(i, size) | |
275 | .unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits())) | |
ea8adc8c XL |
276 | } |
277 | ||
ba9703b0 XL |
278 | #[inline] |
279 | pub fn from_i32(i: i32) -> Self { | |
280 | Self::from_int(i, Size::from_bits(32)) | |
281 | } | |
282 | ||
283 | #[inline] | |
284 | pub fn from_i64(i: i64) -> Self { | |
285 | Self::from_int(i, Size::from_bits(64)) | |
286 | } | |
287 | ||
74b04a01 XL |
288 | #[inline] |
289 | pub fn from_machine_isize(i: i64, cx: &impl HasDataLayout) -> Self { | |
290 | Self::from_int(i, cx.data_layout().pointer_size) | |
291 | } | |
292 | ||
b7449926 | 293 | #[inline] |
dc9dc135 | 294 | pub fn from_f32(f: Single) -> Self { |
29967ef6 | 295 | Scalar::Int(f.into()) |
ea8adc8c XL |
296 | } |
297 | ||
b7449926 | 298 | #[inline] |
dc9dc135 | 299 | pub fn from_f64(f: Double) -> Self { |
29967ef6 | 300 | Scalar::Int(f.into()) |
dc9dc135 XL |
301 | } |
302 | ||
136023e0 XL |
303 | /// This is almost certainly not the method you want! You should dispatch on the type |
304 | /// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed. | |
305 | /// | |
306 | /// This method only exists for the benefit of low-level operations that truly need to treat the | |
307 | /// scalar in whatever form it is. | |
04454e1e FG |
308 | /// |
309 | /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in | |
310 | /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type. | |
dc9dc135 | 311 | #[inline] |
04454e1e FG |
312 | pub fn to_bits_or_ptr_internal( |
313 | self, | |
314 | target_size: Size, | |
315 | ) -> Result<Result<u128, Pointer<Tag>>, ScalarSizeMismatch> { | |
74b04a01 | 316 | assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); |
04454e1e FG |
317 | Ok(match self { |
318 | Scalar::Int(int) => Ok(int.to_bits(target_size).map_err(|size| { | |
319 | ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() } | |
320 | })?), | |
136023e0 | 321 | Scalar::Ptr(ptr, sz) => { |
923072b8 | 322 | if target_size.bytes() != u64::from(sz) { |
04454e1e FG |
323 | return Err(ScalarSizeMismatch { |
324 | target_size: target_size.bytes(), | |
325 | data_size: sz.into(), | |
326 | }); | |
327 | } | |
dc9dc135 XL |
328 | Err(ptr) |
329 | } | |
04454e1e | 330 | }) |
ea8adc8c | 331 | } |
136023e0 | 332 | } |
ea8adc8c | 333 | |
136023e0 XL |
334 | impl<'tcx, Tag: Provenance> Scalar<Tag> { |
335 | /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you | |
336 | /// likely want to use instead. | |
337 | /// | |
338 | /// Will perform ptr-to-int casts if needed and possible. | |
339 | /// If that fails, we know the offset is relative, so we return an "erased" Scalar | |
340 | /// (which is useful for error messages but not much else). | |
b7449926 | 341 | #[inline] |
136023e0 | 342 | pub fn try_to_int(self) -> Result<ScalarInt, Scalar<AllocId>> { |
ea8adc8c | 343 | match self { |
136023e0 XL |
344 | Scalar::Int(int) => Ok(int), |
345 | Scalar::Ptr(ptr, sz) => { | |
346 | if Tag::OFFSET_IS_ADDR { | |
347 | Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap()) | |
348 | } else { | |
349 | // We know `offset` is relative, since `OFFSET_IS_ADDR == false`. | |
350 | let (tag, offset) = ptr.into_parts(); | |
04454e1e FG |
351 | // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail. |
352 | Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz)) | |
136023e0 XL |
353 | } |
354 | } | |
ea8adc8c XL |
355 | } |
356 | } | |
357 | ||
416331ca | 358 | #[inline(always)] |
29967ef6 | 359 | pub fn assert_int(self) -> ScalarInt { |
136023e0 | 360 | self.try_to_int().unwrap() |
29967ef6 XL |
361 | } |
362 | ||
136023e0 XL |
363 | /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in |
364 | /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type. | |
b7449926 | 365 | #[inline] |
136023e0 XL |
366 | pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { |
367 | assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); | |
368 | self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsBytes))?.to_bits(target_size).map_err( | |
369 | |size| { | |
04454e1e | 370 | err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { |
136023e0 XL |
371 | target_size: target_size.bytes(), |
372 | data_size: size.bytes(), | |
04454e1e | 373 | })) |
136023e0 XL |
374 | .into() |
375 | }, | |
376 | ) | |
ea8adc8c XL |
377 | } |
378 | ||
136023e0 XL |
379 | #[inline(always)] |
380 | pub fn assert_bits(self, target_size: Size) -> u128 { | |
381 | self.to_bits(target_size).unwrap() | |
ea8adc8c XL |
382 | } |
383 | ||
dc9dc135 | 384 | pub fn to_bool(self) -> InterpResult<'tcx, bool> { |
ba9703b0 XL |
385 | let val = self.to_u8()?; |
386 | match val { | |
387 | 0 => Ok(false), | |
388 | 1 => Ok(true), | |
389 | _ => throw_ub!(InvalidBool(val)), | |
ea8adc8c XL |
390 | } |
391 | } | |
b7449926 | 392 | |
dc9dc135 | 393 | pub fn to_char(self) -> InterpResult<'tcx, char> { |
b7449926 | 394 | let val = self.to_u32()?; |
29967ef6 | 395 | match std::char::from_u32(val) { |
b7449926 | 396 | Some(c) => Ok(c), |
ba9703b0 | 397 | None => throw_ub!(InvalidChar(val)), |
b7449926 XL |
398 | } |
399 | } | |
400 | ||
5e7ed085 FG |
401 | /// Converts the scalar to produce an unsigned integer of the given size. |
402 | /// Fails if the scalar is a pointer. | |
dfeec247 | 403 | #[inline] |
923072b8 | 404 | pub fn to_uint(self, size: Size) -> InterpResult<'tcx, u128> { |
5e7ed085 | 405 | self.to_bits(size) |
dfeec247 XL |
406 | } |
407 | ||
94222f64 | 408 | /// Converts the scalar to produce a `u8`. Fails if the scalar is a pointer. |
923072b8 | 409 | pub fn to_u8(self) -> InterpResult<'tcx, u8> { |
5e7ed085 | 410 | self.to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap()) |
b7449926 XL |
411 | } |
412 | ||
94222f64 | 413 | /// Converts the scalar to produce a `u16`. Fails if the scalar is a pointer. |
923072b8 | 414 | pub fn to_u16(self) -> InterpResult<'tcx, u16> { |
5e7ed085 | 415 | self.to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap()) |
dfeec247 XL |
416 | } |
417 | ||
94222f64 | 418 | /// Converts the scalar to produce a `u32`. Fails if the scalar is a pointer. |
923072b8 | 419 | pub fn to_u32(self) -> InterpResult<'tcx, u32> { |
5e7ed085 | 420 | self.to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap()) |
b7449926 XL |
421 | } |
422 | ||
94222f64 | 423 | /// Converts the scalar to produce a `u64`. Fails if the scalar is a pointer. |
923072b8 | 424 | pub fn to_u64(self) -> InterpResult<'tcx, u64> { |
5e7ed085 | 425 | self.to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap()) |
b7449926 XL |
426 | } |
427 | ||
94222f64 | 428 | /// Converts the scalar to produce a `u128`. Fails if the scalar is a pointer. |
923072b8 | 429 | pub fn to_u128(self) -> InterpResult<'tcx, u128> { |
5e7ed085 | 430 | self.to_uint(Size::from_bits(128)) |
1b1a35ee XL |
431 | } |
432 | ||
5e7ed085 FG |
433 | /// Converts the scalar to produce a machine-pointer-sized unsigned integer. |
434 | /// Fails if the scalar is a pointer. | |
923072b8 | 435 | pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { |
5e7ed085 | 436 | let b = self.to_uint(cx.data_layout().pointer_size)?; |
ba9703b0 | 437 | Ok(u64::try_from(b).unwrap()) |
b7449926 XL |
438 | } |
439 | ||
5e7ed085 FG |
440 | /// Converts the scalar to produce a signed integer of the given size. |
441 | /// Fails if the scalar is a pointer. | |
dfeec247 | 442 | #[inline] |
923072b8 | 443 | pub fn to_int(self, size: Size) -> InterpResult<'tcx, i128> { |
5e7ed085 FG |
444 | let b = self.to_bits(size)?; |
445 | Ok(size.sign_extend(b) as i128) | |
b7449926 XL |
446 | } |
447 | ||
dfeec247 | 448 | /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer. |
923072b8 | 449 | pub fn to_i8(self) -> InterpResult<'tcx, i8> { |
5e7ed085 | 450 | self.to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap()) |
dfeec247 XL |
451 | } |
452 | ||
453 | /// Converts the scalar to produce an `i16`. Fails if the scalar is a pointer. | |
923072b8 | 454 | pub fn to_i16(self) -> InterpResult<'tcx, i16> { |
5e7ed085 | 455 | self.to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap()) |
dfeec247 XL |
456 | } |
457 | ||
458 | /// Converts the scalar to produce an `i32`. Fails if the scalar is a pointer. | |
923072b8 | 459 | pub fn to_i32(self) -> InterpResult<'tcx, i32> { |
5e7ed085 | 460 | self.to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap()) |
b7449926 XL |
461 | } |
462 | ||
dfeec247 | 463 | /// Converts the scalar to produce an `i64`. Fails if the scalar is a pointer. |
923072b8 | 464 | pub fn to_i64(self) -> InterpResult<'tcx, i64> { |
5e7ed085 | 465 | self.to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap()) |
b7449926 XL |
466 | } |
467 | ||
1b1a35ee | 468 | /// Converts the scalar to produce an `i128`. Fails if the scalar is a pointer. |
923072b8 | 469 | pub fn to_i128(self) -> InterpResult<'tcx, i128> { |
5e7ed085 | 470 | self.to_int(Size::from_bits(128)) |
1b1a35ee XL |
471 | } |
472 | ||
5e7ed085 FG |
473 | /// Converts the scalar to produce a machine-pointer-sized signed integer. |
474 | /// Fails if the scalar is a pointer. | |
923072b8 | 475 | pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> { |
5e7ed085 | 476 | let b = self.to_int(cx.data_layout().pointer_size)?; |
ba9703b0 | 477 | Ok(i64::try_from(b).unwrap()) |
b7449926 XL |
478 | } |
479 | ||
480 | #[inline] | |
923072b8 | 481 | pub fn to_f32(self) -> InterpResult<'tcx, Single> { |
dc9dc135 | 482 | // Going through `u32` to check size and truncation. |
ba9703b0 | 483 | Ok(Single::from_bits(self.to_u32()?.into())) |
b7449926 XL |
484 | } |
485 | ||
486 | #[inline] | |
923072b8 | 487 | pub fn to_f64(self) -> InterpResult<'tcx, Double> { |
dc9dc135 | 488 | // Going through `u64` to check size and truncation. |
ba9703b0 | 489 | Ok(Double::from_bits(self.to_u64()?.into())) |
b7449926 XL |
490 | } |
491 | } | |
492 | ||
3dfed10e | 493 | #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] |
136023e0 | 494 | pub enum ScalarMaybeUninit<Tag = AllocId> { |
f9f354fc XL |
495 | Scalar(Scalar<Tag>), |
496 | Uninit, | |
a1dfa0c6 XL |
497 | } |
498 | ||
6a06907d | 499 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
1b1a35ee XL |
500 | static_assert_size!(ScalarMaybeUninit, 24); |
501 | ||
f9f354fc | 502 | impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> { |
a1dfa0c6 XL |
503 | #[inline(always)] |
504 | fn from(s: Scalar<Tag>) -> Self { | |
f9f354fc | 505 | ScalarMaybeUninit::Scalar(s) |
a1dfa0c6 XL |
506 | } |
507 | } | |
508 | ||
f9f354fc XL |
509 | // We want the `Debug` output to be readable as it is used by `derive(Debug)` for |
510 | // all the Miri types. | |
136023e0 | 511 | impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> { |
a1dfa0c6 XL |
512 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
513 | match self { | |
f9f354fc XL |
514 | ScalarMaybeUninit::Uninit => write!(f, "<uninitialized>"), |
515 | ScalarMaybeUninit::Scalar(s) => write!(f, "{:?}", s), | |
a1dfa0c6 XL |
516 | } |
517 | } | |
518 | } | |
519 | ||
5e7ed085 | 520 | impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> { |
dc9dc135 | 521 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
a1dfa0c6 | 522 | match self { |
f9f354fc | 523 | ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"), |
5e7ed085 | 524 | ScalarMaybeUninit::Scalar(s) => write!(f, "{:x}", s), |
a1dfa0c6 XL |
525 | } |
526 | } | |
527 | } | |
528 | ||
136023e0 | 529 | impl<Tag> ScalarMaybeUninit<Tag> { |
a1dfa0c6 | 530 | #[inline] |
136023e0 XL |
531 | pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { |
532 | ScalarMaybeUninit::Scalar(Scalar::from_pointer(ptr, cx)) | |
533 | } | |
534 | ||
535 | #[inline] | |
536 | pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { | |
537 | ScalarMaybeUninit::Scalar(Scalar::from_maybe_pointer(ptr, cx)) | |
a1dfa0c6 XL |
538 | } |
539 | ||
540 | #[inline] | |
923072b8 | 541 | pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Tag>> { |
a1dfa0c6 | 542 | match self { |
f9f354fc XL |
543 | ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), |
544 | ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), | |
a1dfa0c6 XL |
545 | } |
546 | } | |
136023e0 | 547 | } |
a1dfa0c6 | 548 | |
136023e0 | 549 | impl<'tcx, Tag: Provenance> ScalarMaybeUninit<Tag> { |
a1dfa0c6 | 550 | #[inline(always)] |
dc9dc135 | 551 | pub fn to_bool(self) -> InterpResult<'tcx, bool> { |
3dfed10e | 552 | self.check_init()?.to_bool() |
a1dfa0c6 XL |
553 | } |
554 | ||
555 | #[inline(always)] | |
dc9dc135 | 556 | pub fn to_char(self) -> InterpResult<'tcx, char> { |
3dfed10e | 557 | self.check_init()?.to_char() |
a1dfa0c6 XL |
558 | } |
559 | ||
560 | #[inline(always)] | |
dc9dc135 | 561 | pub fn to_f32(self) -> InterpResult<'tcx, Single> { |
3dfed10e | 562 | self.check_init()?.to_f32() |
a1dfa0c6 XL |
563 | } |
564 | ||
565 | #[inline(always)] | |
dc9dc135 | 566 | pub fn to_f64(self) -> InterpResult<'tcx, Double> { |
3dfed10e | 567 | self.check_init()?.to_f64() |
a1dfa0c6 XL |
568 | } |
569 | ||
570 | #[inline(always)] | |
dc9dc135 | 571 | pub fn to_u8(self) -> InterpResult<'tcx, u8> { |
3dfed10e | 572 | self.check_init()?.to_u8() |
a1dfa0c6 XL |
573 | } |
574 | ||
74b04a01 XL |
575 | #[inline(always)] |
576 | pub fn to_u16(self) -> InterpResult<'tcx, u16> { | |
3dfed10e | 577 | self.check_init()?.to_u16() |
74b04a01 XL |
578 | } |
579 | ||
a1dfa0c6 | 580 | #[inline(always)] |
dc9dc135 | 581 | pub fn to_u32(self) -> InterpResult<'tcx, u32> { |
3dfed10e | 582 | self.check_init()?.to_u32() |
a1dfa0c6 XL |
583 | } |
584 | ||
585 | #[inline(always)] | |
dc9dc135 | 586 | pub fn to_u64(self) -> InterpResult<'tcx, u64> { |
3dfed10e | 587 | self.check_init()?.to_u64() |
a1dfa0c6 XL |
588 | } |
589 | ||
590 | #[inline(always)] | |
60c5eb7d | 591 | pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { |
3dfed10e | 592 | self.check_init()?.to_machine_usize(cx) |
a1dfa0c6 XL |
593 | } |
594 | ||
595 | #[inline(always)] | |
dc9dc135 | 596 | pub fn to_i8(self) -> InterpResult<'tcx, i8> { |
3dfed10e | 597 | self.check_init()?.to_i8() |
a1dfa0c6 | 598 | } |
74b04a01 XL |
599 | |
600 | #[inline(always)] | |
601 | pub fn to_i16(self) -> InterpResult<'tcx, i16> { | |
3dfed10e | 602 | self.check_init()?.to_i16() |
74b04a01 | 603 | } |
a1dfa0c6 XL |
604 | |
605 | #[inline(always)] | |
dc9dc135 | 606 | pub fn to_i32(self) -> InterpResult<'tcx, i32> { |
3dfed10e | 607 | self.check_init()?.to_i32() |
a1dfa0c6 XL |
608 | } |
609 | ||
610 | #[inline(always)] | |
dc9dc135 | 611 | pub fn to_i64(self) -> InterpResult<'tcx, i64> { |
3dfed10e | 612 | self.check_init()?.to_i64() |
a1dfa0c6 XL |
613 | } |
614 | ||
615 | #[inline(always)] | |
60c5eb7d | 616 | pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> { |
3dfed10e | 617 | self.check_init()?.to_machine_isize(cx) |
a1dfa0c6 XL |
618 | } |
619 | } | |
620 | ||
e74abb32 XL |
621 | /// Gets the bytes of a constant slice value. |
622 | pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] { | |
623 | if let ConstValue::Slice { data, start, end } = val { | |
624 | let len = end - start; | |
5e7ed085 FG |
625 | data.inner() |
626 | .get_bytes( | |
627 | cx, | |
628 | AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) }, | |
629 | ) | |
630 | .unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err)) | |
e74abb32 XL |
631 | } else { |
632 | bug!("expected const slice, but found another const value"); | |
633 | } | |
634 | } |