]> git.lizzy.rs Git - rust.git/blob - src/librustc_middle/mir/interpret/value.rs
c11e50015087bf3010536f8ce4d1fb36db88ab24
[rust.git] / src / librustc_middle / mir / interpret / value.rs
1 use std::convert::TryFrom;
2 use std::fmt;
3
4 use rustc_apfloat::{
5     ieee::{Double, Single},
6     Float,
7 };
8 use rustc_macros::HashStable;
9 use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
10
11 use crate::ty::{ParamEnv, Ty, TyCtxt};
12
13 use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
14
15 /// Represents the result of a raw const operation, pre-validation.
16 #[derive(Clone, HashStable)]
17 pub struct RawConst<'tcx> {
18     // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
19     // (so you can use `AllocMap::unwrap_memory`).
20     pub alloc_id: AllocId,
21     pub ty: Ty<'tcx>,
22 }
23
24 /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
25 /// array length computations, enum discriminants and the pattern matching logic.
26 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)]
27 #[derive(HashStable)]
28 pub enum ConstValue<'tcx> {
29     /// Used only for types with `layout::abi::Scalar` ABI and ZSTs.
30     ///
31     /// Not using the enum `Value` to encode that this must not be `Undef`.
32     Scalar(Scalar),
33
34     /// Used only for `&[u8]` and `&str`
35     Slice { data: &'tcx Allocation, start: usize, end: usize },
36
37     /// A value not represented/representable by `Scalar` or `Slice`
38     ByRef {
39         /// The backing memory of the value, may contain more memory than needed for just the value
40         /// in order to share `Allocation`s between values
41         alloc: &'tcx Allocation,
42         /// Offset into `alloc`
43         offset: Size,
44     },
45 }
46
47 #[cfg(target_arch = "x86_64")]
48 static_assert_size!(ConstValue<'_>, 32);
49
50 impl<'tcx> ConstValue<'tcx> {
51     #[inline]
52     pub fn try_to_scalar(&self) -> Option<Scalar> {
53         match *self {
54             ConstValue::ByRef { .. } | ConstValue::Slice { .. } => None,
55             ConstValue::Scalar(val) => Some(val),
56         }
57     }
58
59     pub fn try_to_bits(&self, size: Size) -> Option<u128> {
60         self.try_to_scalar()?.to_bits(size).ok()
61     }
62
63     pub fn try_to_bits_for_ty(
64         &self,
65         tcx: TyCtxt<'tcx>,
66         param_env: ParamEnv<'tcx>,
67         ty: Ty<'tcx>,
68     ) -> Option<u128> {
69         let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size;
70         self.try_to_bits(size)
71     }
72
73     pub fn from_bool(b: bool) -> Self {
74         ConstValue::Scalar(Scalar::from_bool(b))
75     }
76
77     pub fn from_u64(i: u64) -> Self {
78         ConstValue::Scalar(Scalar::from_u64(i))
79     }
80
81     pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self {
82         ConstValue::Scalar(Scalar::from_machine_usize(i, cx))
83     }
84 }
85
86 /// A `Scalar` represents an immediate, primitive value existing outside of a
87 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
88 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
89 /// of a simple value or a pointer into another `Allocation`
90 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
91 #[derive(HashStable)]
92 pub enum Scalar<Tag = (), Id = AllocId> {
93     /// The raw bytes of a simple value.
94     Raw {
95         /// The first `size` bytes of `data` are the value.
96         /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
97         data: u128,
98         size: u8,
99     },
100
101     /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
102     /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
103     /// relocation and its associated offset together as a `Pointer` here.
104     Ptr(Pointer<Tag, Id>),
105 }
106
107 #[cfg(target_arch = "x86_64")]
108 static_assert_size!(Scalar, 24);
109
110 impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for Scalar<Tag, Id> {
111     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112         match self {
113             Scalar::Ptr(ptr) => write!(f, "{:?}", ptr),
114             &Scalar::Raw { data, size } => {
115                 Scalar::check_data(data, size);
116                 if size == 0 {
117                     write!(f, "<ZST>")
118                 } else {
119                     // Format as hex number wide enough to fit any value of the given `size`.
120                     // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
121                     write!(f, "0x{:>0width$x}", data, width = (size * 2) as usize)
122                 }
123             }
124         }
125     }
126 }
127
128 impl<Tag> fmt::Display for Scalar<Tag> {
129     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130         match self {
131             Scalar::Ptr(_) => write!(f, "a pointer"),
132             Scalar::Raw { data, .. } => write!(f, "{}", data),
133         }
134     }
135 }
136
137 impl<Tag> From<Single> for Scalar<Tag> {
138     #[inline(always)]
139     fn from(f: Single) -> Self {
140         Scalar::from_f32(f)
141     }
142 }
143
144 impl<Tag> From<Double> for Scalar<Tag> {
145     #[inline(always)]
146     fn from(f: Double) -> Self {
147         Scalar::from_f64(f)
148     }
149 }
150
151 impl Scalar<()> {
152     /// Make sure the `data` fits in `size`.
153     /// This is guaranteed by all constructors here, but since the enum variants are public,
154     /// it could still be violated (even though no code outside this file should
155     /// construct `Scalar`s).
156     #[inline(always)]
157     fn check_data(data: u128, size: u8) {
158         debug_assert_eq!(
159             truncate(data, Size::from_bytes(u64::from(size))),
160             data,
161             "Scalar value {:#x} exceeds size of {} bytes",
162             data,
163             size
164         );
165     }
166
167     /// Tag this scalar with `new_tag` if it is a pointer, leave it unchanged otherwise.
168     ///
169     /// Used by `MemPlace::replace_tag`.
170     #[inline]
171     pub fn with_tag<Tag>(self, new_tag: Tag) -> Scalar<Tag> {
172         match self {
173             Scalar::Ptr(ptr) => Scalar::Ptr(ptr.with_tag(new_tag)),
174             Scalar::Raw { data, size } => Scalar::Raw { data, size },
175         }
176     }
177 }
178
179 impl<'tcx, Tag> Scalar<Tag> {
180     /// Erase the tag from the scalar, if any.
181     ///
182     /// Used by error reporting code to avoid having the error type depend on `Tag`.
183     #[inline]
184     pub fn erase_tag(self) -> Scalar {
185         match self {
186             Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_tag()),
187             Scalar::Raw { data, size } => Scalar::Raw { data, size },
188         }
189     }
190
191     #[inline]
192     pub fn null_ptr(cx: &impl HasDataLayout) -> Self {
193         Scalar::Raw { data: 0, size: cx.data_layout().pointer_size.bytes() as u8 }
194     }
195
196     #[inline]
197     pub fn zst() -> Self {
198         Scalar::Raw { data: 0, size: 0 }
199     }
200
201     #[inline(always)]
202     fn ptr_op(
203         self,
204         dl: &TargetDataLayout,
205         f_int: impl FnOnce(u64) -> InterpResult<'tcx, u64>,
206         f_ptr: impl FnOnce(Pointer<Tag>) -> InterpResult<'tcx, Pointer<Tag>>,
207     ) -> InterpResult<'tcx, Self> {
208         match self {
209             Scalar::Raw { data, size } => {
210                 assert_eq!(u64::from(size), dl.pointer_size.bytes());
211                 Ok(Scalar::Raw { data: u128::from(f_int(u64::try_from(data).unwrap())?), size })
212             }
213             Scalar::Ptr(ptr) => Ok(Scalar::Ptr(f_ptr(ptr)?)),
214         }
215     }
216
217     #[inline]
218     pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
219         let dl = cx.data_layout();
220         self.ptr_op(dl, |int| dl.offset(int, i.bytes()), |ptr| ptr.offset(i, dl))
221     }
222
223     #[inline]
224     pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
225         let dl = cx.data_layout();
226         self.ptr_op(
227             dl,
228             |int| Ok(dl.overflowing_offset(int, i.bytes()).0),
229             |ptr| Ok(ptr.wrapping_offset(i, dl)),
230         )
231         .unwrap()
232     }
233
234     #[inline]
235     pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
236         let dl = cx.data_layout();
237         self.ptr_op(dl, |int| dl.signed_offset(int, i), |ptr| ptr.signed_offset(i, dl))
238     }
239
240     #[inline]
241     pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
242         let dl = cx.data_layout();
243         self.ptr_op(
244             dl,
245             |int| Ok(dl.overflowing_signed_offset(int, i).0),
246             |ptr| Ok(ptr.wrapping_signed_offset(i, dl)),
247         )
248         .unwrap()
249     }
250
251     #[inline]
252     pub fn from_bool(b: bool) -> Self {
253         // Guaranteed to be truncated and does not need sign extension.
254         Scalar::Raw { data: b as u128, size: 1 }
255     }
256
257     #[inline]
258     pub fn from_char(c: char) -> Self {
259         // Guaranteed to be truncated and does not need sign extension.
260         Scalar::Raw { data: c as u128, size: 4 }
261     }
262
263     #[inline]
264     pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
265         let i = i.into();
266         if truncate(i, size) == i {
267             Some(Scalar::Raw { data: i, size: size.bytes() as u8 })
268         } else {
269             None
270         }
271     }
272
273     #[inline]
274     pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
275         let i = i.into();
276         Self::try_from_uint(i, size)
277             .unwrap_or_else(|| bug!("Unsigned value {:#x} does not fit in {} bits", i, size.bits()))
278     }
279
280     #[inline]
281     pub fn from_u8(i: u8) -> Self {
282         // Guaranteed to be truncated and does not need sign extension.
283         Scalar::Raw { data: i.into(), size: 1 }
284     }
285
286     #[inline]
287     pub fn from_u16(i: u16) -> Self {
288         // Guaranteed to be truncated and does not need sign extension.
289         Scalar::Raw { data: i.into(), size: 2 }
290     }
291
292     #[inline]
293     pub fn from_u32(i: u32) -> Self {
294         // Guaranteed to be truncated and does not need sign extension.
295         Scalar::Raw { data: i.into(), size: 4 }
296     }
297
298     #[inline]
299     pub fn from_u64(i: u64) -> Self {
300         // Guaranteed to be truncated and does not need sign extension.
301         Scalar::Raw { data: i.into(), size: 8 }
302     }
303
304     #[inline]
305     pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self {
306         Self::from_uint(i, cx.data_layout().pointer_size)
307     }
308
309     #[inline]
310     pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
311         let i = i.into();
312         // `into` performed sign extension, we have to truncate
313         let truncated = truncate(i as u128, size);
314         if sign_extend(truncated, size) as i128 == i {
315             Some(Scalar::Raw { data: truncated, size: size.bytes() as u8 })
316         } else {
317             None
318         }
319     }
320
321     #[inline]
322     pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
323         let i = i.into();
324         Self::try_from_int(i, size)
325             .unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits()))
326     }
327
328     #[inline]
329     pub fn from_i8(i: i8) -> Self {
330         Self::from_int(i, Size::from_bits(8))
331     }
332
333     #[inline]
334     pub fn from_i16(i: i16) -> Self {
335         Self::from_int(i, Size::from_bits(16))
336     }
337
338     #[inline]
339     pub fn from_i32(i: i32) -> Self {
340         Self::from_int(i, Size::from_bits(32))
341     }
342
343     #[inline]
344     pub fn from_i64(i: i64) -> Self {
345         Self::from_int(i, Size::from_bits(64))
346     }
347
348     #[inline]
349     pub fn from_machine_isize(i: i64, cx: &impl HasDataLayout) -> Self {
350         Self::from_int(i, cx.data_layout().pointer_size)
351     }
352
353     #[inline]
354     pub fn from_f32(f: Single) -> Self {
355         // We trust apfloat to give us properly truncated data.
356         Scalar::Raw { data: f.to_bits(), size: 4 }
357     }
358
359     #[inline]
360     pub fn from_f64(f: Double) -> Self {
361         // We trust apfloat to give us properly truncated data.
362         Scalar::Raw { data: f.to_bits(), size: 8 }
363     }
364
365     /// This is very rarely the method you want!  You should dispatch on the type
366     /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`.
367     /// This method only exists for the benefit of low-level memory operations
368     /// as well as the implementation of the `force_*` methods.
369     #[inline]
370     pub fn to_bits_or_ptr(
371         self,
372         target_size: Size,
373         cx: &impl HasDataLayout,
374     ) -> Result<u128, Pointer<Tag>> {
375         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
376         match self {
377             Scalar::Raw { data, size } => {
378                 assert_eq!(target_size.bytes(), u64::from(size));
379                 Scalar::check_data(data, size);
380                 Ok(data)
381             }
382             Scalar::Ptr(ptr) => {
383                 assert_eq!(target_size, cx.data_layout().pointer_size);
384                 Err(ptr)
385             }
386         }
387     }
388
389     /// This method is intentionally private!
390     /// It is just a helper for other methods in this file.
391     #[inline]
392     fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
393         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
394         match self {
395             Scalar::Raw { data, size } => {
396                 if target_size.bytes() != u64::from(size) {
397                     throw_ub!(ArgumentSizeMismatch {
398                         target_size: target_size.bytes(),
399                         data_size: u64::from(size)
400                     });
401                 }
402                 Scalar::check_data(data, size);
403                 Ok(data)
404             }
405             Scalar::Ptr(_) => throw_unsup!(ReadPointerAsBytes),
406         }
407     }
408
409     #[inline(always)]
410     pub fn assert_bits(self, target_size: Size) -> u128 {
411         self.to_bits(target_size).expect("expected Raw bits but got a Pointer")
412     }
413
414     #[inline]
415     pub fn assert_ptr(self) -> Pointer<Tag> {
416         match self {
417             Scalar::Ptr(p) => p,
418             Scalar::Raw { .. } => bug!("expected a Pointer but got Raw bits"),
419         }
420     }
421
422     /// Do not call this method!  Dispatch based on the type instead.
423     #[inline]
424     pub fn is_bits(self) -> bool {
425         match self {
426             Scalar::Raw { .. } => true,
427             _ => false,
428         }
429     }
430
431     /// Do not call this method!  Dispatch based on the type instead.
432     #[inline]
433     pub fn is_ptr(self) -> bool {
434         match self {
435             Scalar::Ptr(_) => true,
436             _ => false,
437         }
438     }
439
440     pub fn to_bool(self) -> InterpResult<'tcx, bool> {
441         let val = self.to_u8()?;
442         match val {
443             0 => Ok(false),
444             1 => Ok(true),
445             _ => throw_ub!(InvalidBool(val)),
446         }
447     }
448
449     pub fn to_char(self) -> InterpResult<'tcx, char> {
450         let val = self.to_u32()?;
451         match ::std::char::from_u32(val) {
452             Some(c) => Ok(c),
453             None => throw_ub!(InvalidChar(val)),
454         }
455     }
456
457     #[inline]
458     fn to_unsigned_with_bit_width(self, bits: u64) -> InterpResult<'static, u128> {
459         let sz = Size::from_bits(bits);
460         self.to_bits(sz)
461     }
462
463     /// Converts the scalar to produce an `u8`. Fails if the scalar is a pointer.
464     pub fn to_u8(self) -> InterpResult<'static, u8> {
465         self.to_unsigned_with_bit_width(8).map(|v| u8::try_from(v).unwrap())
466     }
467
468     /// Converts the scalar to produce an `u16`. Fails if the scalar is a pointer.
469     pub fn to_u16(self) -> InterpResult<'static, u16> {
470         self.to_unsigned_with_bit_width(16).map(|v| u16::try_from(v).unwrap())
471     }
472
473     /// Converts the scalar to produce an `u32`. Fails if the scalar is a pointer.
474     pub fn to_u32(self) -> InterpResult<'static, u32> {
475         self.to_unsigned_with_bit_width(32).map(|v| u32::try_from(v).unwrap())
476     }
477
478     /// Converts the scalar to produce an `u64`. Fails if the scalar is a pointer.
479     pub fn to_u64(self) -> InterpResult<'static, u64> {
480         self.to_unsigned_with_bit_width(64).map(|v| u64::try_from(v).unwrap())
481     }
482
483     pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'static, u64> {
484         let b = self.to_bits(cx.data_layout().pointer_size)?;
485         Ok(u64::try_from(b).unwrap())
486     }
487
488     #[inline]
489     fn to_signed_with_bit_width(self, bits: u64) -> InterpResult<'static, i128> {
490         let sz = Size::from_bits(bits);
491         let b = self.to_bits(sz)?;
492         Ok(sign_extend(b, sz) as i128)
493     }
494
495     /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer.
496     pub fn to_i8(self) -> InterpResult<'static, i8> {
497         self.to_signed_with_bit_width(8).map(|v| i8::try_from(v).unwrap())
498     }
499
500     /// Converts the scalar to produce an `i16`. Fails if the scalar is a pointer.
501     pub fn to_i16(self) -> InterpResult<'static, i16> {
502         self.to_signed_with_bit_width(16).map(|v| i16::try_from(v).unwrap())
503     }
504
505     /// Converts the scalar to produce an `i32`. Fails if the scalar is a pointer.
506     pub fn to_i32(self) -> InterpResult<'static, i32> {
507         self.to_signed_with_bit_width(32).map(|v| i32::try_from(v).unwrap())
508     }
509
510     /// Converts the scalar to produce an `i64`. Fails if the scalar is a pointer.
511     pub fn to_i64(self) -> InterpResult<'static, i64> {
512         self.to_signed_with_bit_width(64).map(|v| i64::try_from(v).unwrap())
513     }
514
515     pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'static, i64> {
516         let sz = cx.data_layout().pointer_size;
517         let b = self.to_bits(sz)?;
518         let b = sign_extend(b, sz) as i128;
519         Ok(i64::try_from(b).unwrap())
520     }
521
522     #[inline]
523     pub fn to_f32(self) -> InterpResult<'static, Single> {
524         // Going through `u32` to check size and truncation.
525         Ok(Single::from_bits(self.to_u32()?.into()))
526     }
527
528     #[inline]
529     pub fn to_f64(self) -> InterpResult<'static, Double> {
530         // Going through `u64` to check size and truncation.
531         Ok(Double::from_bits(self.to_u64()?.into()))
532     }
533 }
534
535 impl<Tag> From<Pointer<Tag>> for Scalar<Tag> {
536     #[inline(always)]
537     fn from(ptr: Pointer<Tag>) -> Self {
538         Scalar::Ptr(ptr)
539     }
540 }
541
542 #[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable, HashStable, Hash)]
543 pub enum ScalarMaybeUndef<Tag = (), Id = AllocId> {
544     Scalar(Scalar<Tag, Id>),
545     Undef,
546 }
547
548 impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
549     #[inline(always)]
550     fn from(s: Scalar<Tag>) -> Self {
551         ScalarMaybeUndef::Scalar(s)
552     }
553 }
554
555 impl<Tag> From<Pointer<Tag>> for ScalarMaybeUndef<Tag> {
556     #[inline(always)]
557     fn from(s: Pointer<Tag>) -> Self {
558         ScalarMaybeUndef::Scalar(s.into())
559     }
560 }
561
562 impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
563     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
564         match self {
565             ScalarMaybeUndef::Undef => write!(f, "Undef"),
566             ScalarMaybeUndef::Scalar(s) => write!(f, "{:?}", s),
567         }
568     }
569 }
570
571 impl<Tag> fmt::Display for ScalarMaybeUndef<Tag> {
572     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573         match self {
574             ScalarMaybeUndef::Undef => write!(f, "uninitialized bytes"),
575             ScalarMaybeUndef::Scalar(s) => write!(f, "{}", s),
576         }
577     }
578 }
579
580 impl<'tcx, Tag> ScalarMaybeUndef<Tag> {
581     /// Erase the tag from the scalar, if any.
582     ///
583     /// Used by error reporting code to avoid having the error type depend on `Tag`.
584     #[inline]
585     pub fn erase_tag(self) -> ScalarMaybeUndef {
586         match self {
587             ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.erase_tag()),
588             ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef,
589         }
590     }
591
592     #[inline]
593     pub fn not_undef(self) -> InterpResult<'static, Scalar<Tag>> {
594         match self {
595             ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
596             ScalarMaybeUndef::Undef => throw_ub!(InvalidUndefBytes(None)),
597         }
598     }
599
600     #[inline(always)]
601     pub fn to_bool(self) -> InterpResult<'tcx, bool> {
602         self.not_undef()?.to_bool()
603     }
604
605     #[inline(always)]
606     pub fn to_char(self) -> InterpResult<'tcx, char> {
607         self.not_undef()?.to_char()
608     }
609
610     #[inline(always)]
611     pub fn to_f32(self) -> InterpResult<'tcx, Single> {
612         self.not_undef()?.to_f32()
613     }
614
615     #[inline(always)]
616     pub fn to_f64(self) -> InterpResult<'tcx, Double> {
617         self.not_undef()?.to_f64()
618     }
619
620     #[inline(always)]
621     pub fn to_u8(self) -> InterpResult<'tcx, u8> {
622         self.not_undef()?.to_u8()
623     }
624
625     #[inline(always)]
626     pub fn to_u16(self) -> InterpResult<'tcx, u16> {
627         self.not_undef()?.to_u16()
628     }
629
630     #[inline(always)]
631     pub fn to_u32(self) -> InterpResult<'tcx, u32> {
632         self.not_undef()?.to_u32()
633     }
634
635     #[inline(always)]
636     pub fn to_u64(self) -> InterpResult<'tcx, u64> {
637         self.not_undef()?.to_u64()
638     }
639
640     #[inline(always)]
641     pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
642         self.not_undef()?.to_machine_usize(cx)
643     }
644
645     #[inline(always)]
646     pub fn to_i8(self) -> InterpResult<'tcx, i8> {
647         self.not_undef()?.to_i8()
648     }
649
650     #[inline(always)]
651     pub fn to_i16(self) -> InterpResult<'tcx, i16> {
652         self.not_undef()?.to_i16()
653     }
654
655     #[inline(always)]
656     pub fn to_i32(self) -> InterpResult<'tcx, i32> {
657         self.not_undef()?.to_i32()
658     }
659
660     #[inline(always)]
661     pub fn to_i64(self) -> InterpResult<'tcx, i64> {
662         self.not_undef()?.to_i64()
663     }
664
665     #[inline(always)]
666     pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> {
667         self.not_undef()?.to_machine_isize(cx)
668     }
669 }
670
671 /// Gets the bytes of a constant slice value.
672 pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
673     if let ConstValue::Slice { data, start, end } = val {
674         let len = end - start;
675         data.get_bytes(
676             cx,
677             // invent a pointer, only the offset is relevant anyway
678             Pointer::new(AllocId(0), Size::from_bytes(start)),
679             Size::from_bytes(len),
680         )
681         .unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
682     } else {
683         bug!("expected const slice, but found another const value");
684     }
685 }