Skip to main content

rustc_middle/mir/interpret/
value.rs

1use std::fmt;
2
3use either::{Either, Left, Right};
4use rustc_abi::{HasDataLayout, Size};
5use rustc_apfloat::Float;
6use rustc_apfloat::ieee::{Double, Half, Quad, Single};
7use rustc_macros::{StableHash, TyDecodable, TyEncodable};
8
9use super::{
10    AllocId, CtfeProvenance, InterpResult, Pointer, PointerArithmetic, Provenance, interp_ok,
11};
12use crate::ty::ScalarInt;
13
14/// A `Scalar` represents an immediate, primitive value existing outside of a
15/// `memory::Allocation`. It is in many ways like a small chunk of an `Allocation`, up to 16 bytes in
16/// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
17/// of a simple value or a pointer into another `Allocation`
18///
19/// These variants would be private if there was a convenient way to achieve that in Rust.
20/// Do *not* match on a `Scalar`! Use the various `to_*` methods instead.
21#[derive(#[automatically_derived]
impl<Prov: ::core::clone::Clone> ::core::clone::Clone for Scalar<Prov> {
    #[inline]
    fn clone(&self) -> Scalar<Prov> {
        match self {
            Scalar::Int(__self_0) =>
                Scalar::Int(::core::clone::Clone::clone(__self_0)),
            Scalar::Ptr(__self_0, __self_1) =>
                Scalar::Ptr(::core::clone::Clone::clone(__self_0),
                    ::core::clone::Clone::clone(__self_1)),
        }
    }
}Clone, #[automatically_derived]
impl<Prov: ::core::marker::Copy> ::core::marker::Copy for Scalar<Prov> { }Copy, #[automatically_derived]
impl<Prov: ::core::cmp::Eq> ::core::cmp::Eq for Scalar<Prov> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<ScalarInt>;
        let _: ::core::cmp::AssertParamIsEq<Pointer<Prov>>;
        let _: ::core::cmp::AssertParamIsEq<u8>;
    }
}Eq, #[automatically_derived]
impl<Prov: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Scalar<Prov> {
    #[inline]
    fn eq(&self, other: &Scalar<Prov>) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (Scalar::Int(__self_0), Scalar::Int(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Scalar::Ptr(__self_0, __self_1),
                    Scalar::Ptr(__arg1_0, __arg1_1)) =>
                    __self_1 == __arg1_1 && __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, const _: () =
    {
        impl<'tcx, Prov, __E: ::rustc_middle::ty::codec::TyEncoder<'tcx>>
            ::rustc_serialize::Encodable<__E> for Scalar<Prov> where
            Pointer<Prov>: ::rustc_serialize::Encodable<__E> {
            fn encode(&self, __encoder: &mut __E) {
                let disc =
                    match *self {
                        Scalar::Int(ref __binding_0) => { 0usize }
                        Scalar::Ptr(ref __binding_0, ref __binding_1) => { 1usize }
                    };
                ::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
                match *self {
                    Scalar::Int(ref __binding_0) => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                    }
                    Scalar::Ptr(ref __binding_0, ref __binding_1) => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_1,
                            __encoder);
                    }
                }
            }
        }
    };TyEncodable, const _: () =
    {
        impl<'tcx, Prov, __D: ::rustc_middle::ty::codec::TyDecoder<'tcx>>
            ::rustc_serialize::Decodable<__D> for Scalar<Prov> where
            Pointer<Prov>: ::rustc_serialize::Decodable<__D> {
            fn decode(__decoder: &mut __D) -> Self {
                match ::rustc_serialize::Decoder::read_u8(__decoder) as usize
                    {
                    0usize => {
                        Scalar::Int(::rustc_serialize::Decodable::decode(__decoder))
                    }
                    1usize => {
                        Scalar::Ptr(::rustc_serialize::Decodable::decode(__decoder),
                            ::rustc_serialize::Decodable::decode(__decoder))
                    }
                    n => {
                        ::core::panicking::panic_fmt(format_args!("invalid enum variant tag while decoding `Scalar`, expected 0..2, actual {0}",
                                n));
                    }
                }
            }
        }
    };TyDecodable, #[automatically_derived]
impl<Prov: ::core::hash::Hash> ::core::hash::Hash for Scalar<Prov> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state);
        match self {
            Scalar::Int(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            Scalar::Ptr(__self_0, __self_1) => {
                ::core::hash::Hash::hash(__self_0, state);
                ::core::hash::Hash::hash(__self_1, state)
            }
        }
    }
}Hash)]
22#[derive(const _: () =
    {
        impl<Prov> ::rustc_data_structures::stable_hash::StableHash for
            Scalar<Prov> where
            Prov: ::rustc_data_structures::stable_hash::StableHash {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __hasher:
                    &mut ::rustc_data_structures::stable_hash::StableHasher) {
                ::std::mem::discriminant(self).stable_hash(__hcx, __hasher);
                match *self {
                    Scalar::Int(ref __binding_0) => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                    }
                    Scalar::Ptr(ref __binding_0, ref __binding_1) => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash)]
23pub enum Scalar<Prov = CtfeProvenance> {
24    /// The raw bytes of a simple value.
25    Int(ScalarInt),
26
27    /// A pointer.
28    ///
29    /// We also store the size of the pointer, such that a `Scalar` always knows how big it is.
30    /// The size is always the pointer size of the current target, but this is not information
31    /// that we always have readily available.
32    Ptr(Pointer<Prov>, u8),
33}
34
35#[cfg(target_pointer_width = "64")]
36const _: [(); 24] = [(); ::std::mem::size_of::<Scalar>()];rustc_data_structures::static_assert_size!(Scalar, 24);
37
38// We want the `Debug` output to be readable as it is used by `derive(Debug)` for
39// all the Miri types.
40impl<Prov: Provenance> fmt::Debug for Scalar<Prov> {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Scalar::Ptr(ptr, _size) => f.write_fmt(format_args!("{0:?}", ptr))write!(f, "{ptr:?}"),
44            Scalar::Int(int) => f.write_fmt(format_args!("{0:?}", int))write!(f, "{int:?}"),
45        }
46    }
47}
48
49impl<Prov: Provenance> fmt::Display for Scalar<Prov> {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match self {
52            Scalar::Ptr(ptr, _size) => f.write_fmt(format_args!("pointer to {0:?}", ptr))write!(f, "pointer to {ptr:?}"),
53            Scalar::Int(int) => f.write_fmt(format_args!("{0}", int))write!(f, "{int}"),
54        }
55    }
56}
57
58impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            Scalar::Ptr(ptr, _size) => f.write_fmt(format_args!("pointer to {0:?}", ptr))write!(f, "pointer to {ptr:?}"),
62            Scalar::Int(int) => f.write_fmt(format_args!("{0:#x}", int))write!(f, "{int:#x}"),
63        }
64    }
65}
66
67impl<Prov> From<Half> for Scalar<Prov> {
68    #[inline(always)]
69    fn from(f: Half) -> Self {
70        Scalar::from_f16(f)
71    }
72}
73
74impl<Prov> From<Single> for Scalar<Prov> {
75    #[inline(always)]
76    fn from(f: Single) -> Self {
77        Scalar::from_f32(f)
78    }
79}
80
81impl<Prov> From<Double> for Scalar<Prov> {
82    #[inline(always)]
83    fn from(f: Double) -> Self {
84        Scalar::from_f64(f)
85    }
86}
87
88impl<Prov> From<Quad> for Scalar<Prov> {
89    #[inline(always)]
90    fn from(f: Quad) -> Self {
91        Scalar::from_f128(f)
92    }
93}
94
95impl<Prov> From<ScalarInt> for Scalar<Prov> {
96    #[inline(always)]
97    fn from(ptr: ScalarInt) -> Self {
98        Scalar::Int(ptr)
99    }
100}
101
102impl<Prov> Scalar<Prov> {
103    #[inline(always)]
104    pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
105        Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap())
106    }
107
108    /// Create a Scalar from a pointer with an `Option<_>` provenance (where `None` represents a
109    /// plain integer / "invalid" pointer).
110    pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
111        match ptr.into_raw_parts() {
112            (Some(prov), offset) => Scalar::from_pointer(Pointer::new(prov, offset), cx),
113            (None, offset) => {
114                Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap())
115            }
116        }
117    }
118
119    #[inline]
120    pub fn null_ptr(cx: &impl HasDataLayout) -> Self {
121        Scalar::Int(ScalarInt::null(cx.pointer_size()))
122    }
123
124    #[inline]
125    pub fn from_bool(b: bool) -> Self {
126        Scalar::Int(b.into())
127    }
128
129    #[inline]
130    pub fn from_char(c: char) -> Self {
131        Scalar::Int(c.into())
132    }
133
134    #[inline]
135    pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
136        let i = i.into();
137        ScalarInt::try_from_uint(i, size)
138            .unwrap_or_else(|| crate::util::bug::bug_fmt(format_args!("Unsigned value {0:#x} does not fit in {1} bits",
        i, size.bits()))bug!("Unsigned value {:#x} does not fit in {} bits", i, size.bits()))
139            .into()
140    }
141
142    #[inline]
143    pub fn from_u8(i: u8) -> Self {
144        Scalar::Int(i.into())
145    }
146
147    #[inline]
148    pub fn from_u16(i: u16) -> Self {
149        Scalar::Int(i.into())
150    }
151
152    #[inline]
153    pub fn from_u32(i: u32) -> Self {
154        Scalar::Int(i.into())
155    }
156
157    #[inline]
158    pub fn from_u64(i: u64) -> Self {
159        Scalar::Int(i.into())
160    }
161
162    #[inline]
163    pub fn from_u128(i: u128) -> Self {
164        Scalar::Int(i.into())
165    }
166
167    #[inline]
168    pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
169        Self::from_uint(i, cx.data_layout().pointer_offset())
170    }
171
172    #[inline]
173    pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
174        let i = i.into();
175        ScalarInt::try_from_int(i, size)
176            .unwrap_or_else(|| crate::util::bug::bug_fmt(format_args!("Signed value {0:#x} does not fit in {1} bits",
        i, size.bits()))bug!("Signed value {:#x} does not fit in {} bits", i, size.bits()))
177            .into()
178    }
179
180    #[inline]
181    pub fn from_i8(i: i8) -> Self {
182        Self::Int(i.into())
183    }
184
185    #[inline]
186    pub fn from_i16(i: i16) -> Self {
187        Self::Int(i.into())
188    }
189
190    #[inline]
191    pub fn from_i32(i: i32) -> Self {
192        Self::Int(i.into())
193    }
194
195    #[inline]
196    pub fn from_i64(i: i64) -> Self {
197        Self::Int(i.into())
198    }
199
200    #[inline]
201    pub fn from_i128(i: i128) -> Self {
202        Self::Int(i.into())
203    }
204
205    #[inline]
206    pub fn from_target_isize(i: i64, cx: &impl HasDataLayout) -> Self {
207        Self::from_int(i, cx.data_layout().pointer_offset())
208    }
209
210    #[inline]
211    pub fn from_f16(f: Half) -> Self {
212        Scalar::Int(f.into())
213    }
214
215    #[inline]
216    pub fn from_f32(f: Single) -> Self {
217        Scalar::Int(f.into())
218    }
219
220    #[inline]
221    pub fn from_f64(f: Double) -> Self {
222        Scalar::Int(f.into())
223    }
224
225    #[inline]
226    pub fn from_f128(f: Quad) -> Self {
227        Scalar::Int(f.into())
228    }
229
230    /// This is almost certainly not the method you want!  You should dispatch on the type
231    /// and use `to_{u8,u16,...}`/`to_pointer` to perform ptr-to-int / int-to-ptr casts as needed.
232    ///
233    /// This method only exists for the benefit of low-level operations that truly need to treat the
234    /// scalar in whatever form it is.
235    ///
236    /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in
237    /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type.
238    #[inline]
239    pub fn to_bits_or_ptr_internal(self, target_size: Size) -> Either<u128, Pointer<Prov>> {
240        {
    match (&(target_size.bytes()), &(0)) {
        (left_val, right_val) => {
            if *left_val == *right_val {
                let kind = ::core::panicking::AssertKind::Ne;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("you should never look at the bits of a ZST")));
            }
        }
    }
};assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
241        match self {
242            Scalar::Int(int) => Left(int.to_bits(target_size)),
243            Scalar::Ptr(ptr, sz) => {
244                {
    match (&target_size.bytes(), &u64::from(sz)) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("Scalar is a pointer but expected size {0}",
                            target_size.bytes())));
            }
        }
    }
};assert_eq!(
245                    target_size.bytes(),
246                    u64::from(sz),
247                    "Scalar is a pointer but expected size {}",
248                    target_size.bytes()
249                );
250                Right(ptr)
251            }
252        }
253    }
254
255    #[inline]
256    pub fn size(self) -> Size {
257        match self {
258            Scalar::Int(int) => int.size(),
259            Scalar::Ptr(_ptr, sz) => Size::from_bytes(sz),
260        }
261    }
262}
263
264impl<'tcx, Prov: Provenance> Scalar<Prov> {
265    pub fn to_pointer(self, cx: &impl HasDataLayout) -> Pointer<Option<Prov>> {
266        match self.to_bits_or_ptr_internal(cx.pointer_size()) {
267            Right(ptr) => ptr.into(),
268            Left(bits) => {
269                let addr = u64::try_from(bits).unwrap();
270                Pointer::without_provenance(addr)
271            }
272        }
273    }
274
275    /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you
276    /// likely want to use instead.
277    ///
278    /// Will perform ptr-to-int casts if needed and possible.
279    /// If that fails, we know the offset is relative, so we return an "erased" Scalar
280    /// (which is useful for error messages but not much else).
281    ///
282    /// The error type is `AllocId`, not `CtfeProvenance`, since `AllocId` is the "minimal"
283    /// component all provenance types must have.
284    #[inline]
285    pub fn try_to_scalar_int(self) -> Result<ScalarInt, Scalar<AllocId>> {
286        match self {
287            Scalar::Int(int) => Ok(int),
288            Scalar::Ptr(ptr, sz) => {
289                if Prov::OFFSET_IS_ADDR {
290                    Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap())
291                } else {
292                    // We know `offset` is relative, since `OFFSET_IS_ADDR == false`.
293                    let (prov, offset) = ptr.into_raw_parts();
294                    // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail.
295                    Err(Scalar::Ptr(Pointer::new(prov.get_alloc_id().unwrap(), offset), sz))
296                }
297            }
298        }
299    }
300
301    pub fn clear_provenance(&mut self) -> InterpResult<'tcx> {
302        if #[allow(non_exhaustive_omitted_patterns)] match self {
    Scalar::Ptr(..) => true,
    _ => false,
}matches!(self, Scalar::Ptr(..)) {
303            *self = self.to_scalar_int()?.into();
304        }
305        interp_ok(())
306    }
307
308    #[inline(always)]
309    pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {
310        self.try_to_scalar_int().map_err(|_| crate::mir::interpret::InterpErrorKind::Unsupported(crate::mir::interpret::UnsupportedOpInfo::ReadPointerAsInt(None))err_unsup!(ReadPointerAsInt(None))).into()
311    }
312
313    #[inline(always)]
314    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
315    pub fn assert_scalar_int(self) -> ScalarInt {
316        self.try_to_scalar_int().expect("got a pointer where a ScalarInt was expected")
317    }
318
319    /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in
320    /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type.
321    #[inline]
322    pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
323        {
    match (&(target_size.bytes()), &(0)) {
        (left_val, right_val) => {
            if *left_val == *right_val {
                let kind = ::core::panicking::AssertKind::Ne;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("you should never look at the bits of a ZST")));
            }
        }
    }
};assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
324        interp_ok(self.to_scalar_int()?.to_bits(target_size))
325    }
326
327    pub fn to_bool(self) -> InterpResult<'tcx, bool> {
328        let val = self.to_u8()?;
329        match val {
330            0 => interp_ok(false),
331            1 => interp_ok(true),
332            _ => do yeet crate::mir::interpret::InterpErrorKind::UndefinedBehavior(crate::mir::interpret::UndefinedBehaviorInfo::InvalidBool(val))throw_ub!(InvalidBool(val)),
333        }
334    }
335
336    pub fn to_char(self) -> InterpResult<'tcx, char> {
337        let val = self.to_u32()?;
338        match std::char::from_u32(val) {
339            Some(c) => interp_ok(c),
340            None => do yeet crate::mir::interpret::InterpErrorKind::UndefinedBehavior(crate::mir::interpret::UndefinedBehaviorInfo::InvalidChar(val))throw_ub!(InvalidChar(val)),
341        }
342    }
343
344    /// Converts the scalar to produce an unsigned integer of the given size.
345    /// Fails if the scalar is a pointer.
346    #[inline]
347    pub fn to_uint(self, size: Size) -> InterpResult<'tcx, u128> {
348        self.to_bits(size)
349    }
350
351    /// Converts the scalar to produce a `u8`. Fails if the scalar is a pointer.
352    pub fn to_u8(self) -> InterpResult<'tcx, u8> {
353        self.to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
354    }
355
356    /// Converts the scalar to produce a `u16`. Fails if the scalar is a pointer.
357    pub fn to_u16(self) -> InterpResult<'tcx, u16> {
358        self.to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
359    }
360
361    /// Converts the scalar to produce a `u32`. Fails if the scalar is a pointer.
362    pub fn to_u32(self) -> InterpResult<'tcx, u32> {
363        self.to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
364    }
365
366    /// Converts the scalar to produce a `u64`. Fails if the scalar is a pointer.
367    pub fn to_u64(self) -> InterpResult<'tcx, u64> {
368        self.to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
369    }
370
371    /// Converts the scalar to produce a `u128`. Fails if the scalar is a pointer.
372    pub fn to_u128(self) -> InterpResult<'tcx, u128> {
373        self.to_uint(Size::from_bits(128))
374    }
375
376    /// Converts the scalar to produce a machine-pointer-sized unsigned integer.
377    /// Fails if the scalar is a pointer.
378    pub fn to_target_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
379        let b = self.to_uint(cx.data_layout().pointer_size())?;
380        interp_ok(u64::try_from(b).unwrap())
381    }
382
383    /// Converts the scalar to produce a signed integer of the given size.
384    /// Fails if the scalar is a pointer.
385    #[inline]
386    pub fn to_int(self, size: Size) -> InterpResult<'tcx, i128> {
387        let b = self.to_bits(size)?;
388        interp_ok(size.sign_extend(b))
389    }
390
391    /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer.
392    pub fn to_i8(self) -> InterpResult<'tcx, i8> {
393        self.to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
394    }
395
396    /// Converts the scalar to produce an `i16`. Fails if the scalar is a pointer.
397    pub fn to_i16(self) -> InterpResult<'tcx, i16> {
398        self.to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
399    }
400
401    /// Converts the scalar to produce an `i32`. Fails if the scalar is a pointer.
402    pub fn to_i32(self) -> InterpResult<'tcx, i32> {
403        self.to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
404    }
405
406    /// Converts the scalar to produce an `i64`. Fails if the scalar is a pointer.
407    pub fn to_i64(self) -> InterpResult<'tcx, i64> {
408        self.to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
409    }
410
411    /// Converts the scalar to produce an `i128`. Fails if the scalar is a pointer.
412    pub fn to_i128(self) -> InterpResult<'tcx, i128> {
413        self.to_int(Size::from_bits(128))
414    }
415
416    /// Converts the scalar to produce a machine-pointer-sized signed integer.
417    /// Fails if the scalar is a pointer.
418    pub fn to_target_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> {
419        let b = self.to_int(cx.data_layout().pointer_size())?;
420        interp_ok(i64::try_from(b).unwrap())
421    }
422
423    #[inline]
424    pub fn to_float<F: Float>(self) -> InterpResult<'tcx, F> {
425        // Going through `to_bits` to check size and truncation.
426        interp_ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
427    }
428
429    #[inline]
430    pub fn to_f16(self) -> InterpResult<'tcx, Half> {
431        self.to_float()
432    }
433
434    #[inline]
435    pub fn to_f32(self) -> InterpResult<'tcx, Single> {
436        self.to_float()
437    }
438
439    #[inline]
440    pub fn to_f64(self) -> InterpResult<'tcx, Double> {
441        self.to_float()
442    }
443
444    #[inline]
445    pub fn to_f128(self) -> InterpResult<'tcx, Quad> {
446        self.to_float()
447    }
448}