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