Skip to main content

rustc_const_eval/interpret/
cast.rs

1use std::assert_matches;
2
3use rustc_abi::{FieldIdx, Integer};
4use rustc_apfloat::ieee::{Double, Half, Quad, Single};
5use rustc_apfloat::{Float, FloatConvert};
6use rustc_middle::mir::CastKind;
7use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
8use rustc_middle::ty::adjustment::PointerCoercion;
9use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
10use rustc_middle::ty::{self, FloatTy, Ty};
11use rustc_middle::{bug, span_bug};
12use tracing::trace;
13
14use super::util::ensure_monomorphic_enough;
15use super::{
16    FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub,
17    throw_ub_format,
18};
19use crate::enter_trace_span;
20use crate::interpret::{Projectable, Writeable};
21
22impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
23    pub fn cast(
24        &mut self,
25        src: &OpTy<'tcx, M::Provenance>,
26        cast_kind: CastKind,
27        cast_ty: Ty<'tcx>,
28        dest: &PlaceTy<'tcx, M::Provenance>,
29    ) -> InterpResult<'tcx> {
30        // `cast_ty` will often be the same as `dest.ty`, but not always, since subtyping is still
31        // possible.
32        let cast_layout =
33            if cast_ty == dest.layout.ty { dest.layout } else { self.layout_of(cast_ty)? };
34
35        // Check that the input is valid.
36        // Can be skipped for transmuts and unsizing as those do validation below.
37        if !#[allow(non_exhaustive_omitted_patterns)] match cast_kind {
    CastKind::Transmute | CastKind::Subtype | CastKind::BoxDerefTransmute |
        CastKind::PointerCoercion(PointerCoercion::Unsize, _) => true,
    _ => false,
}matches!(
38            cast_kind,
39            CastKind::Transmute
40                | CastKind::Subtype
41                | CastKind::BoxDerefTransmute
42                | CastKind::PointerCoercion(PointerCoercion::Unsize, _)
43        ) && M::enforce_validity(self, src.layout)
44        {
45            match src.layout.ty.kind() {
46                ty::RawPtr { .. } => {
47                    // We only need to check anything for wide pointers.
48                    if #[allow(non_exhaustive_omitted_patterns)] match src.layout.backend_repr {
    rustc_abi::BackendRepr::ScalarPair { .. } => true,
    _ => false,
}matches!(src.layout.backend_repr, rustc_abi::BackendRepr::ScalarPair { .. })
49                    {
50                        self.deref_pointer(src)?;
51                    }
52                }
53                ty::FnPtr { .. } => {
54                    let ptr = self.read_pointer(src)?;
55                    self.get_ptr_fn(ptr)?;
56                }
57                ty::Closure(_closure, args) => {
58                    // Can only happen for non-capturing closures, which have nothing to validate.
59                    let args = args.as_closure();
60                    if !args.upvar_tys().is_empty() {
    ::core::panicking::panic("assertion failed: args.upvar_tys().is_empty()")
};assert!(args.upvar_tys().is_empty());
61                }
62                // Types that have no requirements or whose requirements are checked by the actual
63                // cast operation.
64                ty::Int(..)
65                | ty::Uint(..)
66                | ty::Float(..)
67                | ty::Bool
68                | ty::Char
69                | ty::FnDef(..) => {}
70
71                _ => {
72                    ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("unexpected input type in non-transmute/unsize cast: {0}",
        src.layout.ty))span_bug!(
73                        self.cur_span(),
74                        "unexpected input type in non-transmute/unsize cast: {}",
75                        src.layout.ty
76                    )
77                }
78            }
79        }
80
81        match cast_kind {
82            CastKind::PointerCoercion(PointerCoercion::Unsize, _) => {
83                self.unsize_into(src, cast_layout, dest)?;
84                // Validate the entire thing and reset any padding in the output.
85                // It is enough to validate the output because we are only adding metadata,
86                // not discarding anything from the input that may have been invalid.
87                if M::enforce_validity(self, dest.layout()) {
88                    self.validate_place(
89                        dest,
90                        M::enforce_validity_recursively(self, dest.layout()),
91                        /*reset_provenance_and_padding*/ true,
92                    )?;
93                }
94            }
95
96            CastKind::PointerExposeProvenance => {
97                let src = self.read_immediate(src)?;
98                let res = self.pointer_expose_provenance_cast(&src, cast_layout)?;
99                self.write_immediate(*res, dest)?;
100            }
101
102            CastKind::PointerWithExposedProvenance => {
103                let src = self.read_immediate(src)?;
104                let res = self.pointer_with_exposed_provenance_cast(&src, cast_layout)?;
105                self.write_immediate(*res, dest)?;
106            }
107
108            CastKind::IntToInt | CastKind::IntToFloat => {
109                let src = self.read_immediate(src)?;
110                let res = self.int_to_int_or_float(&src, cast_layout)?;
111                self.write_immediate(*res, dest)?;
112            }
113
114            CastKind::FloatToFloat | CastKind::FloatToInt => {
115                let src = self.read_immediate(src)?;
116                let res = self.float_to_float_or_int(&src, cast_layout)?;
117                self.write_immediate(*res, dest)?;
118            }
119
120            CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
121                let src = self.read_immediate(src)?;
122                let res = self.ptr_to_ptr(&src, cast_layout)?;
123                self.write_immediate(*res, dest)?;
124            }
125
126            CastKind::PointerCoercion(
127                PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer,
128                _,
129            ) => {
130                ::rustc_middle::util::bug::bug_fmt(format_args!("{0:?} casts are for borrowck only, not runtime MIR",
        cast_kind));bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR");
131            }
132
133            CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => {
134                // All reifications must be monomorphic, bail out otherwise.
135                ensure_monomorphic_enough(src.layout.ty)?;
136
137                // The src operand does not matter, just its type
138                match *src.layout.ty.kind() {
139                    ty::FnDef(def_id, args) => {
140                        let instance = {
141                            let _trace = <M as
        crate::interpret::Machine>::enter_trace_span(||
        {
            use ::tracing::__macro_support::Callsite as _;
            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                {
                    static META: ::tracing::Metadata<'static> =
                        {
                            ::tracing_core::metadata::Metadata::new("resolve",
                                "rustc_const_eval::interpret::cast", ::tracing::Level::INFO,
                                ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/cast.rs"),
                                ::tracing_core::__macro_support::Option::Some(141u32),
                                ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::cast"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("resolve")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("resolve");
                                                    NAME.as_str()
                                                },
                                                {
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("def_id")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("def_id");
                                                    NAME.as_str()
                                                }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                ::tracing::metadata::Kind::SPAN)
                        };
                    ::tracing::callsite::DefaultCallsite::new(&META)
                };
            let mut interest = ::tracing::subscriber::Interest::never();
            if ::tracing::Level::INFO <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::INFO <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        { interest = __CALLSITE.interest(); !interest.is_never() }
                    &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest) {
                let meta = __CALLSITE.metadata();
                ::tracing::Span::new(meta,
                    &{
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&"resolve_for_fn_ptr")
                                                        as &dyn ::tracing::field::Value)),
                                            (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&def_id)
                                                        as &dyn ::tracing::field::Value))])
                        })
            } else {
                let span =
                    ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                {};
                span
            }
        })enter_trace_span!(M, resolve::resolve_for_fn_ptr, ?def_id);
142                            ty::Instance::resolve_for_fn_ptr(
143                                *self.tcx,
144                                self.typing_env,
145                                def_id,
146                                args.no_bound_vars().unwrap(),
147                            )
148                            .ok_or_else(|| ::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::TooGeneric)err_inval!(TooGeneric))?
149                        };
150
151                        let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
152                        self.write_pointer(fn_ptr, dest)?;
153                    }
154                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("reify fn pointer on {0}", src.layout.ty))span_bug!(self.cur_span(), "reify fn pointer on {}", src.layout.ty),
155                }
156            }
157
158            CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _) => {
159                let src = self.read_immediate(src)?;
160                match cast_ty.kind() {
161                    ty::FnPtr(..) => {
162                        // No change to value
163                        self.write_immediate(*src, dest)?;
164                    }
165                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("fn to unsafe fn cast on {0}", cast_ty))span_bug!(self.cur_span(), "fn to unsafe fn cast on {}", cast_ty),
166                }
167            }
168
169            CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _) => {
170                // All reifications must be monomorphic, bail out otherwise.
171                ensure_monomorphic_enough(src.layout.ty)?;
172
173                // The src operand does not matter, just its type
174                match *src.layout.ty.kind() {
175                    ty::Closure(def_id, args) => {
176                        let instance = {
177                            let _trace = <M as
        crate::interpret::Machine>::enter_trace_span(||
        {
            use ::tracing::__macro_support::Callsite as _;
            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                {
                    static META: ::tracing::Metadata<'static> =
                        {
                            ::tracing_core::metadata::Metadata::new("resolve",
                                "rustc_const_eval::interpret::cast", ::tracing::Level::INFO,
                                ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/cast.rs"),
                                ::tracing_core::__macro_support::Option::Some(177u32),
                                ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::cast"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("resolve")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("resolve");
                                                    NAME.as_str()
                                                },
                                                {
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("def_id")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("def_id");
                                                    NAME.as_str()
                                                }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                ::tracing::metadata::Kind::SPAN)
                        };
                    ::tracing::callsite::DefaultCallsite::new(&META)
                };
            let mut interest = ::tracing::subscriber::Interest::never();
            if ::tracing::Level::INFO <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::INFO <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        { interest = __CALLSITE.interest(); !interest.is_never() }
                    &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest) {
                let meta = __CALLSITE.metadata();
                ::tracing::Span::new(meta,
                    &{
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&"resolve_closure")
                                                        as &dyn ::tracing::field::Value)),
                                            (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&def_id)
                                                        as &dyn ::tracing::field::Value))])
                        })
            } else {
                let span =
                    ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                {};
                span
            }
        })enter_trace_span!(M, resolve::resolve_closure, ?def_id);
178                            ty::Instance::resolve_closure(
179                                *self.tcx,
180                                def_id,
181                                args,
182                                ty::ClosureKind::FnOnce,
183                            )
184                        };
185                        let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
186                        self.write_pointer(fn_ptr, dest)?;
187                    }
188                    _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("closure fn pointer on {0}", src.layout.ty))span_bug!(self.cur_span(), "closure fn pointer on {}", src.layout.ty),
189                }
190            }
191
192            CastKind::Transmute | CastKind::Subtype | CastKind::BoxDerefTransmute => {
193                if !src.layout.is_sized() {
    ::core::panicking::panic("assertion failed: src.layout.is_sized()")
};assert!(src.layout.is_sized());
194                if !dest.layout.is_sized() {
    ::core::panicking::panic("assertion failed: dest.layout.is_sized()")
};assert!(dest.layout.is_sized());
195                {
    match (&cast_ty, &dest.layout.ty) {
        (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::None);
            }
        }
    }
};assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely...
196                if src.layout.size != dest.layout.size {
197                    do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::Ub(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("transmuting from {0}-byte type to {1}-byte type: `{2}` -> `{3}`",
                            src.layout.size.bytes(), dest.layout.size.bytes(),
                            src.layout.ty, dest.layout.ty))
                })));throw_ub_format!(
198                        "transmuting from {src_bytes}-byte type to {dest_bytes}-byte type: `{src}` -> `{dest}`",
199                        src_bytes = src.layout.size.bytes(),
200                        dest_bytes = dest.layout.size.bytes(),
201                        src = src.layout.ty,
202                        dest = dest.layout.ty,
203                    );
204                }
205
206                if #[allow(non_exhaustive_omitted_patterns)] match cast_kind {
    CastKind::BoxDerefTransmute => true,
    _ => false,
}matches!(cast_kind, CastKind::BoxDerefTransmute) {
207                    // Do the extra UB checking by making the input an actual `Box<T>` pointer
208                    // and dereferencing it.
209                    let ptr = self.read_immediate(src)?;
210                    let pointee_ty = cast_ty.builtin_deref(true).unwrap();
211                    let box_ty = Ty::new_box(*self.tcx, pointee_ty);
212                    let ptr = ptr.transmute(self.layout_of(box_ty)?, self)?;
213                    self.deref_pointer(&ptr)?;
214                }
215
216                // This does validation at `src` and `dest` type.
217                self.copy_op_allow_transmute(src, dest)?;
218            }
219        }
220        interp_ok(())
221    }
222
223    /// Handles 'IntToInt' and 'IntToFloat' casts.
224    pub fn int_to_int_or_float(
225        &self,
226        src: &ImmTy<'tcx, M::Provenance>,
227        cast_to: TyAndLayout<'tcx>,
228    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
229        if !(src.layout.ty.is_integral() || src.layout.ty.is_char() ||
            src.layout.ty.is_bool()) {
    ::core::panicking::panic("assertion failed: src.layout.ty.is_integral() || src.layout.ty.is_char() ||\n    src.layout.ty.is_bool()")
};assert!(src.layout.ty.is_integral() || src.layout.ty.is_char() || src.layout.ty.is_bool());
230        if !(cast_to.ty.is_floating_point() || cast_to.ty.is_integral() ||
            cast_to.ty.is_char()) {
    ::core::panicking::panic("assertion failed: cast_to.ty.is_floating_point() || cast_to.ty.is_integral() ||\n    cast_to.ty.is_char()")
};assert!(cast_to.ty.is_floating_point() || cast_to.ty.is_integral() || cast_to.ty.is_char());
231
232        interp_ok(ImmTy::from_scalar(
233            self.cast_from_int_like(src.to_scalar(), src.layout, cast_to.ty)?,
234            cast_to,
235        ))
236    }
237
238    /// Handles 'FloatToFloat' and 'FloatToInt' casts.
239    pub fn float_to_float_or_int(
240        &self,
241        src: &ImmTy<'tcx, M::Provenance>,
242        cast_to: TyAndLayout<'tcx>,
243    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
244        let ty::Float(fty) = src.layout.ty.kind() else {
245            ::rustc_middle::util::bug::bug_fmt(format_args!("FloatToFloat/FloatToInt cast: source type {0} is not a float type",
        src.layout.ty))bug!("FloatToFloat/FloatToInt cast: source type {} is not a float type", src.layout.ty)
246        };
247        let val = match fty {
248            FloatTy::F16 => self.cast_from_float(src.to_scalar().to_f16()?, cast_to.ty),
249            FloatTy::F32 => self.cast_from_float(src.to_scalar().to_f32()?, cast_to.ty),
250            FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty),
251            FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty),
252        };
253        interp_ok(ImmTy::from_scalar(val, cast_to))
254    }
255
256    /// Handles 'FnPtrToPtr' and 'PtrToPtr' casts.
257    pub fn ptr_to_ptr(
258        &self,
259        src: &ImmTy<'tcx, M::Provenance>,
260        cast_to: TyAndLayout<'tcx>,
261    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
262        if !src.layout.ty.is_any_ptr() {
    ::core::panicking::panic("assertion failed: src.layout.ty.is_any_ptr()")
};assert!(src.layout.ty.is_any_ptr());
263        if !cast_to.ty.is_raw_ptr() {
    ::core::panicking::panic("assertion failed: cast_to.ty.is_raw_ptr()")
};assert!(cast_to.ty.is_raw_ptr());
264        // Handle casting any ptr to raw ptr (might be a wide ptr).
265        if cast_to.size == src.layout.size {
266            // Thin or wide pointer that just has the ptr kind of target type changed.
267            return interp_ok(ImmTy::from_immediate(**src, cast_to));
268        } else {
269            // Casting the metadata away from a wide ptr.
270            {
    match (&src.layout.size, &(2 * self.pointer_size())) {
        (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::None);
            }
        }
    }
};assert_eq!(src.layout.size, 2 * self.pointer_size());
271            {
    match (&cast_to.size, &self.pointer_size()) {
        (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::None);
            }
        }
    }
};assert_eq!(cast_to.size, self.pointer_size());
272            if !src.layout.ty.is_raw_ptr() {
    ::core::panicking::panic("assertion failed: src.layout.ty.is_raw_ptr()")
};assert!(src.layout.ty.is_raw_ptr());
273            return match **src {
274                Immediate::ScalarPair(data, _) => interp_ok(ImmTy::from_scalar(data, cast_to)),
275                Immediate::Scalar(..) => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("{0:?} input to a fat-to-thin cast ({1} -> {2})", *src,
        src.layout.ty, cast_to.ty))span_bug!(
276                    self.cur_span(),
277                    "{:?} input to a fat-to-thin cast ({} -> {})",
278                    *src,
279                    src.layout.ty,
280                    cast_to.ty
281                ),
282                Immediate::Uninit => do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::InvalidUninitBytes(None))throw_ub!(InvalidUninitBytes(None)),
283            };
284        }
285    }
286
287    pub fn pointer_expose_provenance_cast(
288        &mut self,
289        src: &ImmTy<'tcx, M::Provenance>,
290        cast_to: TyAndLayout<'tcx>,
291    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
292        {
    match src.layout.ty.kind() {
        ty::RawPtr(_, _) | ty::FnPtr(..) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "ty::RawPtr(_, _) | ty::FnPtr(..)",
                ::core::option::Option::None);
        }
    }
};assert_matches!(src.layout.ty.kind(), ty::RawPtr(_, _) | ty::FnPtr(..));
293        if !cast_to.ty.is_integral() {
    ::core::panicking::panic("assertion failed: cast_to.ty.is_integral()")
};assert!(cast_to.ty.is_integral());
294
295        let scalar = src.to_scalar();
296        let ptr = scalar.to_pointer(self)?;
297        match ptr.into_pointer_or_addr() {
298            Ok(ptr) => M::expose_provenance(self, ptr.provenance)?,
299            Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
300        };
301        interp_ok(ImmTy::from_scalar(
302            self.cast_from_int_like(scalar, src.layout, cast_to.ty)?,
303            cast_to,
304        ))
305    }
306
307    pub fn pointer_with_exposed_provenance_cast(
308        &self,
309        src: &ImmTy<'tcx, M::Provenance>,
310        cast_to: TyAndLayout<'tcx>,
311    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
312        if !src.layout.ty.is_integral() {
    ::core::panicking::panic("assertion failed: src.layout.ty.is_integral()")
};assert!(src.layout.ty.is_integral());
313        {
    match cast_to.ty.kind() {
        ty::RawPtr(_, _) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "ty::RawPtr(_, _)", ::core::option::Option::None);
        }
    }
};assert_matches!(cast_to.ty.kind(), ty::RawPtr(_, _));
314
315        // First cast to usize.
316        let scalar = src.to_scalar();
317        let addr = self.cast_from_int_like(scalar, src.layout, self.tcx.types.usize)?;
318        let addr = addr.to_target_usize(self)?;
319
320        // Then turn address into pointer.
321        let ptr = M::ptr_from_addr_cast(self, addr)?;
322        interp_ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to))
323    }
324
325    /// Low-level cast helper function. This works directly on scalars and can take 'int-like' input
326    /// type (basically everything with a scalar layout) to int/float/char types.
327    fn cast_from_int_like(
328        &self,
329        scalar: Scalar<M::Provenance>, // input value (there is no ScalarTy so we separate data+layout)
330        src_layout: TyAndLayout<'tcx>,
331        cast_ty: Ty<'tcx>,
332    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
333        // Let's make sure v is sign-extended *if* it has a signed type.
334        let signed = src_layout.backend_repr.is_signed(); // Also asserts that abi is `Scalar`.
335
336        // We go through the actual type of `src` to ensure the value is valid.
337        let v = match src_layout.ty.kind() {
338            ty::Uint(_) | ty::RawPtr(..) | ty::FnPtr(..) => scalar.to_uint(src_layout.size)?,
339            ty::Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters
340            ty::Bool => scalar.to_bool()?.into(),
341            ty::Char => scalar.to_char()?.into(),
342            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid int-like cast from {0}", src_layout.ty))span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty),
343        };
344
345        interp_ok(match *cast_ty.kind() {
346            // int -> int
347            ty::Int(_) | ty::Uint(_) => {
348                let size = match *cast_ty.kind() {
349                    ty::Int(t) => Integer::from_int_ty(self, t).size(),
350                    ty::Uint(t) => Integer::from_uint_ty(self, t).size(),
351                    _ => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
352                };
353                let v = size.truncate(v);
354                Scalar::from_uint(v, size)
355            }
356
357            // signed int -> float
358            ty::Float(fty) if signed => {
359                let v = v as i128;
360                match fty {
361                    FloatTy::F16 => Scalar::from_f16(Half::from_i128(v).value),
362                    FloatTy::F32 => Scalar::from_f32(Single::from_i128(v).value),
363                    FloatTy::F64 => Scalar::from_f64(Double::from_i128(v).value),
364                    FloatTy::F128 => Scalar::from_f128(Quad::from_i128(v).value),
365                }
366            }
367            // unsigned int -> float
368            ty::Float(fty) => match fty {
369                FloatTy::F16 => Scalar::from_f16(Half::from_u128(v).value),
370                FloatTy::F32 => Scalar::from_f32(Single::from_u128(v).value),
371                FloatTy::F64 => Scalar::from_f64(Double::from_u128(v).value),
372                FloatTy::F128 => Scalar::from_f128(Quad::from_u128(v).value),
373            },
374
375            // u8 -> char
376            ty::Char => Scalar::from_u32(u8::try_from(v).unwrap().into()),
377
378            // Casts to bool are not permitted by rustc, no need to handle them here.
379            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid int to {0} cast", cast_ty))span_bug!(self.cur_span(), "invalid int to {} cast", cast_ty),
380        })
381    }
382
383    /// Low-level cast helper function. Converts an apfloat `f` into int or float types.
384    fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance>
385    where
386        F: Float
387            + Into<Scalar<M::Provenance>>
388            + FloatConvert<Half>
389            + FloatConvert<Single>
390            + FloatConvert<Double>
391            + FloatConvert<Quad>,
392    {
393        match *dest_ty.kind() {
394            // float -> uint
395            ty::Uint(t) => {
396                let size = Integer::from_uint_ty(self, t).size();
397                // `to_u128` is a saturating cast, which is what we need
398                // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
399                let v = f.to_u128(size.bits_usize()).value;
400                // This should already fit the bit width
401                Scalar::from_uint(v, size)
402            }
403            // float -> int
404            ty::Int(t) => {
405                let size = Integer::from_int_ty(self, t).size();
406                // `to_i128` is a saturating cast, which is what we need
407                // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
408                let v = f.to_i128(size.bits_usize()).value;
409                Scalar::from_int(v, size)
410            }
411            // float -> float
412            ty::Float(fty) => match fty {
413                FloatTy::F16 => {
414                    Scalar::from_f16(self.adjust_nan(f.convert(&mut false).value, &[f]))
415                }
416                FloatTy::F32 => {
417                    Scalar::from_f32(self.adjust_nan(f.convert(&mut false).value, &[f]))
418                }
419                FloatTy::F64 => {
420                    Scalar::from_f64(self.adjust_nan(f.convert(&mut false).value, &[f]))
421                }
422                FloatTy::F128 => {
423                    Scalar::from_f128(self.adjust_nan(f.convert(&mut false).value, &[f]))
424                }
425            },
426            // That's it.
427            _ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid float to {0} cast", dest_ty))span_bug!(self.cur_span(), "invalid float to {} cast", dest_ty),
428        }
429    }
430
431    /// `src` is a *pointer to* a `source_ty`, and in `dest` we should store a pointer to th same
432    /// data at type `cast_ty`.
433    fn unsize_into_ptr(
434        &mut self,
435        src: &OpTy<'tcx, M::Provenance>,
436        dest: &impl Writeable<'tcx, M::Provenance>,
437        // The pointee types
438        source_ty: Ty<'tcx>,
439        cast_ty: Ty<'tcx>,
440    ) -> InterpResult<'tcx> {
441        // A<Struct> -> A<Trait> conversion
442        let (src_pointee_ty, dest_pointee_ty) =
443            self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.typing_env);
444
445        match (src_pointee_ty.kind(), dest_pointee_ty.kind()) {
446            (&ty::Array(_, length), &ty::Slice(_)) => {
447                let ptr = self.read_pointer(src)?;
448                let val = Immediate::new_slice(
449                    ptr,
450                    length
451                        .try_to_target_usize(*self.tcx)
452                        .expect("expected monomorphic const in const eval"),
453                    self,
454                );
455                self.write_immediate(val, dest)
456            }
457            (ty::Dynamic(data_a, _), ty::Dynamic(data_b, _)) => {
458                let val = self.read_immediate(src)?;
459                // MIR building generates odd NOP casts, prevent them from causing unexpected trouble.
460                // See <https://github.com/rust-lang/rust/issues/128880>.
461                // FIXME: ideally we wouldn't have to do this.
462                if data_a == data_b {
463                    return self.write_immediate(*val, dest);
464                }
465                // Take apart the old pointer, and find the dynamic type.
466                let (old_data, old_vptr) = val.to_scalar_pair();
467                let old_data = old_data.to_pointer(self)?;
468                let old_vptr = old_vptr.to_pointer(self)?;
469                let ty = self.get_ptr_vtable_ty(old_vptr, Some(data_a))?;
470
471                // Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces
472                // our destination trait.
473                let vptr_entry_idx =
474                    self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
475                let vtable_entries = self.vtable_entries(data_a.principal(), ty);
476                if let Some(entry_idx) = vptr_entry_idx {
477                    let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
478                        vtable_entries.get(entry_idx)
479                    else {
480                        ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid vtable entry index in {0} -> {1} upcast",
        src_pointee_ty, dest_pointee_ty));span_bug!(
481                            self.cur_span(),
482                            "invalid vtable entry index in {} -> {} upcast",
483                            src_pointee_ty,
484                            dest_pointee_ty
485                        );
486                    };
487                    let erased_trait_ref =
488                        ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
489                    {
    match (&data_b.principal().map(|b|
                        {
                            self.tcx.normalize_erasing_late_bound_regions(self.typing_env,
                                b)
                        }), &Some(erased_trait_ref)) {
        (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::None);
            }
        }
    }
};assert_eq!(
490                        data_b.principal().map(|b| {
491                            self.tcx.normalize_erasing_late_bound_regions(self.typing_env, b)
492                        }),
493                        Some(erased_trait_ref),
494                    );
495                } else {
496                    // In this case codegen would keep using the old vtable. We don't want to do
497                    // that as it has the wrong trait. The reason codegen can do this is that
498                    // one vtable is a prefix of the other, so we double-check that.
499                    let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
500                    if !(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b) {
    ::core::panicking::panic("assertion failed: &vtable_entries[..vtable_entries_b.len()] == vtable_entries_b")
};assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
501                };
502
503                // Get the destination trait vtable and return that.
504                let new_vptr = self.get_vtable_ptr(ty, data_b)?;
505                self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
506            }
507            (_, &ty::Dynamic(data, _)) => {
508                // Initial cast from sized to dyn trait
509                let vtable = self.get_vtable_ptr(src_pointee_ty, data)?;
510                let ptr = self.read_pointer(src)?;
511                let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
512                self.write_immediate(val, dest)
513            }
514            _ => {
515                // Do not ICE if we are not monomorphic enough.
516                ensure_monomorphic_enough(src.layout.ty)?;
517                ensure_monomorphic_enough(cast_ty)?;
518
519                ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("invalid pointer unsizing {0} -> {1}", src.layout.ty,
        cast_ty))span_bug!(
520                    self.cur_span(),
521                    "invalid pointer unsizing {} -> {}",
522                    src.layout.ty,
523                    cast_ty
524                )
525            }
526        }
527    }
528
529    /// Perform an unsizing coercion. The caller is responsible for checking validity afterwards!
530    pub fn unsize_into(
531        &mut self,
532        src: &OpTy<'tcx, M::Provenance>,
533        cast_ty: TyAndLayout<'tcx>,
534        dest: &impl Writeable<'tcx, M::Provenance>,
535    ) -> InterpResult<'tcx> {
536        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/cast.rs:536",
                        "rustc_const_eval::interpret::cast",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/cast.rs"),
                        ::tracing_core::__macro_support::Option::Some(536u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::cast"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Unsizing {0:?} of type {1} into {2}",
                                                    *src, src.layout.ty, cast_ty.ty) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty);
537        match (src.layout.ty.kind(), cast_ty.ty.kind()) {
538            (&ty::Pat(_, s_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => {
539                let src = self.project_field(src, FieldIdx::ZERO)?;
540                let dest = self.project_field(dest, FieldIdx::ZERO)?;
541                let cast_ty = self.layout_of(cast_ty)?;
542                self.unsize_into(&src, cast_ty, &dest)
543            }
544            (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
545            | (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
546            (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
547                {
    match (&def_a, &def_b) {
        (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::None);
            }
        }
    }
};assert_eq!(def_a, def_b); // implies same number of fields
548
549                // Unsizing of generic struct with pointer fields, like `Arc<T>` -> `Arc<Trait>`.
550                // There can be extra fields as long as they don't change their type or are 1-ZST.
551                // There might also be no field that actually needs unsizing.
552                let mut found_cast_field = false;
553                for i in 0..src.layout.fields.count() {
554                    let cast_ty_field = cast_ty.field(self, i);
555                    let i = FieldIdx::from_usize(i);
556                    let src_field = self.project_field(src, i)?;
557                    let dst_field = self.project_field(dest, i)?;
558                    if src_field.layout.is_1zst() && cast_ty_field.is_1zst() {
559                        // Skip 1-ZST fields.
560                    } else if src_field.layout.ty == cast_ty_field.ty {
561                        // The caller performs validation.
562                        self.copy_op_no_validate(
563                            &src_field, &dst_field, /* allow_transmute */ false,
564                        )?;
565                    } else {
566                        if found_cast_field {
567                            ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("unsize_into: more than one field to cast"));span_bug!(self.cur_span(), "unsize_into: more than one field to cast");
568                        }
569                        found_cast_field = true;
570                        self.unsize_into(&src_field, cast_ty_field, &dst_field)?;
571                    }
572                }
573                interp_ok(())
574            }
575            _ => {
576                // Do not ICE if we are not monomorphic enough.
577                ensure_monomorphic_enough(src.layout.ty)?;
578                ensure_monomorphic_enough(cast_ty.ty)?;
579
580                ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
    format_args!("unsize_into: invalid conversion: {0:?} -> {1:?}",
        src.layout, dest.layout()))span_bug!(
581                    self.cur_span(),
582                    "unsize_into: invalid conversion: {:?} -> {:?}",
583                    src.layout,
584                    dest.layout()
585                )
586            }
587        }
588    }
589}