Skip to main content

rustc_middle/mir/interpret/
value.rs

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