1 use std
::assert_matches
::assert_matches
;
2 use std
::convert
::TryFrom
;
4 use rustc_apfloat
::ieee
::{Double, Single}
;
5 use rustc_apfloat
::{Float, FloatConvert}
;
6 use rustc_middle
::mir
::interpret
::{InterpResult, PointerArithmetic, Scalar}
;
7 use rustc_middle
::mir
::CastKind
;
8 use rustc_middle
::ty
::adjustment
::PointerCast
;
9 use rustc_middle
::ty
::layout
::{IntegerExt, LayoutOf, TyAndLayout}
;
10 use rustc_middle
::ty
::{self, FloatTy, Ty, TypeAndMut}
;
11 use rustc_target
::abi
::{Integer, Variants}
;
12 use rustc_type_ir
::sty
::TyKind
::*;
15 util
::ensure_monomorphic_enough
, FnVal
, ImmTy
, Immediate
, InterpCx
, Machine
, OpTy
, PlaceTy
,
18 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
21 src
: &OpTy
<'tcx
, M
::PointerTag
>,
24 dest
: &PlaceTy
<'tcx
, M
::PointerTag
>,
25 ) -> InterpResult
<'tcx
> {
26 use rustc_middle
::mir
::CastKind
::*;
27 // FIXME: In which cases should we trigger UB when the source is uninit?
29 Pointer(PointerCast
::Unsize
) => {
30 let cast_ty
= self.layout_of(cast_ty
)?
;
31 self.unsize_into(src
, cast_ty
, dest
)?
;
34 PointerExposeAddress
=> {
35 let src
= self.read_immediate(src
)?
;
36 let res
= self.pointer_expose_address_cast(&src
, cast_ty
)?
;
37 self.write_immediate(res
, dest
)?
;
40 PointerFromExposedAddress
=> {
41 let src
= self.read_immediate(src
)?
;
42 let res
= self.pointer_from_exposed_address_cast(&src
, cast_ty
)?
;
43 self.write_immediate(res
, dest
)?
;
47 let src
= self.read_immediate(src
)?
;
48 let res
= self.misc_cast(&src
, cast_ty
)?
;
49 self.write_immediate(res
, dest
)?
;
52 Pointer(PointerCast
::MutToConstPointer
| PointerCast
::ArrayToPointer
) => {
53 // These are NOPs, but can be wide pointers.
54 let v
= self.read_immediate(src
)?
;
55 self.write_immediate(*v
, dest
)?
;
58 Pointer(PointerCast
::ReifyFnPointer
) => {
59 // The src operand does not matter, just its type
60 match *src
.layout
.ty
.kind() {
61 ty
::FnDef(def_id
, substs
) => {
62 // All reifications must be monomorphic, bail out otherwise.
63 ensure_monomorphic_enough(*self.tcx
, src
.layout
.ty
)?
;
65 let instance
= ty
::Instance
::resolve_for_fn_ptr(
71 .ok_or_else(|| err_inval
!(TooGeneric
))?
;
73 let fn_ptr
= self.create_fn_alloc_ptr(FnVal
::Instance(instance
));
74 self.write_pointer(fn_ptr
, dest
)?
;
76 _
=> span_bug
!(self.cur_span(), "reify fn pointer on {:?}", src
.layout
.ty
),
80 Pointer(PointerCast
::UnsafeFnPointer
) => {
81 let src
= self.read_immediate(src
)?
;
82 match cast_ty
.kind() {
85 self.write_immediate(*src
, dest
)?
;
87 _
=> span_bug
!(self.cur_span(), "fn to unsafe fn cast on {:?}", cast_ty
),
91 Pointer(PointerCast
::ClosureFnPointer(_
)) => {
92 // The src operand does not matter, just its type
93 match *src
.layout
.ty
.kind() {
94 ty
::Closure(def_id
, substs
) => {
95 // All reifications must be monomorphic, bail out otherwise.
96 ensure_monomorphic_enough(*self.tcx
, src
.layout
.ty
)?
;
98 let instance
= ty
::Instance
::resolve_closure(
102 ty
::ClosureKind
::FnOnce
,
104 let fn_ptr
= self.create_fn_alloc_ptr(FnVal
::Instance(instance
));
105 self.write_pointer(fn_ptr
, dest
)?
;
107 _
=> span_bug
!(self.cur_span(), "closure fn pointer on {:?}", src
.layout
.ty
),
116 src
: &ImmTy
<'tcx
, M
::PointerTag
>,
118 ) -> InterpResult
<'tcx
, Immediate
<M
::PointerTag
>> {
119 use rustc_type_ir
::sty
::TyKind
::*;
120 trace
!("Casting {:?}: {:?} to {:?}", *src
, src
.layout
.ty
, cast_ty
);
122 match src
.layout
.ty
.kind() {
124 Float(FloatTy
::F32
) => {
125 return Ok(self.cast_from_float(src
.to_scalar()?
.to_f32()?
, cast_ty
).into());
127 Float(FloatTy
::F64
) => {
128 return Ok(self.cast_from_float(src
.to_scalar()?
.to_f64()?
, cast_ty
).into());
130 // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
131 // are represented as integers.
133 src
.layout
.ty
.is_bool()
134 || src
.layout
.ty
.is_char()
135 || src
.layout
.ty
.is_enum()
136 || src
.layout
.ty
.is_integral()
137 || src
.layout
.ty
.is_any_ptr(),
138 "Unexpected cast from type {:?}",
143 // # First handle non-scalar source values.
145 // Handle cast from a ZST enum (0 or 1 variants).
146 match src
.layout
.variants
{
147 Variants
::Single { index }
=> {
148 if src
.layout
.abi
.is_uninhabited() {
149 // This is dead code, because an uninhabited enum is UB to
151 throw_ub
!(Unreachable
);
153 if let Some(discr
) = src
.layout
.ty
.discriminant_for_variant(*self.tcx
, index
) {
154 assert
!(src
.layout
.is_zst());
155 let discr_layout
= self.layout_of(discr
.ty
)?
;
157 let scalar
= Scalar
::from_uint(discr
.val
, discr_layout
.layout
.size());
158 return Ok(self.cast_from_int_like(scalar
, discr_layout
, cast_ty
)?
.into());
161 Variants
::Multiple { .. }
=> {}
164 // Handle casting any ptr to raw ptr (might be a fat ptr).
165 if src
.layout
.ty
.is_any_ptr() && cast_ty
.is_unsafe_ptr() {
166 let dest_layout
= self.layout_of(cast_ty
)?
;
167 if dest_layout
.size
== src
.layout
.size
{
168 // Thin or fat pointer that just hast the ptr kind of target type changed.
171 // Casting the metadata away from a fat ptr.
172 assert_eq
!(src
.layout
.size
, 2 * self.pointer_size());
173 assert_eq
!(dest_layout
.size
, self.pointer_size());
174 assert
!(src
.layout
.ty
.is_unsafe_ptr());
176 Immediate
::ScalarPair(data
, _
) => Ok(data
.into()),
177 Immediate
::Scalar(..) => span_bug
!(
179 "{:?} input to a fat-to-thin cast ({:?} -> {:?})",
188 // # The remaining source values are scalar and "int-like".
189 let scalar
= src
.to_scalar()?
;
190 Ok(self.cast_from_int_like(scalar
, src
.layout
, cast_ty
)?
.into())
193 pub fn pointer_expose_address_cast(
195 src
: &ImmTy
<'tcx
, M
::PointerTag
>,
197 ) -> InterpResult
<'tcx
, Immediate
<M
::PointerTag
>> {
198 assert_matches
!(src
.layout
.ty
.kind(), ty
::RawPtr(_
) | ty
::FnPtr(_
));
199 assert
!(cast_ty
.is_integral());
201 let scalar
= src
.to_scalar()?
;
202 let ptr
= self.scalar_to_ptr(scalar
)?
;
203 match ptr
.into_pointer_or_addr() {
204 Ok(ptr
) => M
::expose_ptr(self, ptr
)?
,
205 Err(_
) => {}
// do nothing, exposing an invalid pointer has no meaning
207 Ok(self.cast_from_int_like(scalar
, src
.layout
, cast_ty
)?
.into())
210 pub fn pointer_from_exposed_address_cast(
212 src
: &ImmTy
<'tcx
, M
::PointerTag
>,
214 ) -> InterpResult
<'tcx
, Immediate
<M
::PointerTag
>> {
215 assert
!(src
.layout
.ty
.is_integral());
216 assert_matches
!(cast_ty
.kind(), ty
::RawPtr(_
));
218 // First cast to usize.
219 let scalar
= src
.to_scalar()?
;
220 let addr
= self.cast_from_int_like(scalar
, src
.layout
, self.tcx
.types
.usize)?
;
221 let addr
= addr
.to_machine_usize(self)?
;
223 // Then turn address into pointer.
224 let ptr
= M
::ptr_from_addr_cast(&self, addr
)?
;
225 Ok(Scalar
::from_maybe_pointer(ptr
, self).into())
228 pub fn cast_from_int_like(
230 scalar
: Scalar
<M
::PointerTag
>, // input value (there is no ScalarTy so we separate data+layout)
231 src_layout
: TyAndLayout
<'tcx
>,
233 ) -> InterpResult
<'tcx
, Scalar
<M
::PointerTag
>> {
234 // Let's make sure v is sign-extended *if* it has a signed type.
235 let signed
= src_layout
.abi
.is_signed(); // Also asserts that abi is `Scalar`.
237 let v
= scalar
.to_bits(src_layout
.size
)?
;
238 let v
= if signed { self.sign_extend(v, src_layout) }
else { v }
;
239 trace
!("cast_from_scalar: {}, {} -> {}", v
, src_layout
.ty
, cast_ty
);
241 Ok(match *cast_ty
.kind() {
242 Int(_
) | Uint(_
) => {
243 let size
= match *cast_ty
.kind() {
244 Int(t
) => Integer
::from_int_ty(self, t
).size(),
245 Uint(t
) => Integer
::from_uint_ty(self, t
).size(),
248 let v
= size
.truncate(v
);
249 Scalar
::from_uint(v
, size
)
252 Float(FloatTy
::F32
) if signed
=> Scalar
::from_f32(Single
::from_i128(v
as i128
).value
),
253 Float(FloatTy
::F64
) if signed
=> Scalar
::from_f64(Double
::from_i128(v
as i128
).value
),
254 Float(FloatTy
::F32
) => Scalar
::from_f32(Single
::from_u128(v
).value
),
255 Float(FloatTy
::F64
) => Scalar
::from_f64(Double
::from_u128(v
).value
),
258 // `u8` to `char` cast
259 Scalar
::from_u32(u8::try_from(v
).unwrap().into())
262 // Casts to bool are not permitted by rustc, no need to handle them here.
263 _
=> span_bug
!(self.cur_span(), "invalid int to {:?} cast", cast_ty
),
267 fn cast_from_float
<F
>(&self, f
: F
, dest_ty
: Ty
<'tcx
>) -> Scalar
<M
::PointerTag
>
269 F
: Float
+ Into
<Scalar
<M
::PointerTag
>> + FloatConvert
<Single
> + FloatConvert
<Double
>,
271 use rustc_type_ir
::sty
::TyKind
::*;
272 match *dest_ty
.kind() {
275 let size
= Integer
::from_uint_ty(self, t
).size();
276 // `to_u128` is a saturating cast, which is what we need
277 // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
278 let v
= f
.to_u128(size
.bits_usize()).value
;
279 // This should already fit the bit width
280 Scalar
::from_uint(v
, size
)
284 let size
= Integer
::from_int_ty(self, t
).size();
285 // `to_i128` is a saturating cast, which is what we need
286 // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
287 let v
= f
.to_i128(size
.bits_usize()).value
;
288 Scalar
::from_int(v
, size
)
291 Float(FloatTy
::F32
) => Scalar
::from_f32(f
.convert(&mut false).value
),
293 Float(FloatTy
::F64
) => Scalar
::from_f64(f
.convert(&mut false).value
),
295 _
=> span_bug
!(self.cur_span(), "invalid float to {:?} cast", dest_ty
),
301 src
: &OpTy
<'tcx
, M
::PointerTag
>,
302 dest
: &PlaceTy
<'tcx
, M
::PointerTag
>,
306 ) -> InterpResult
<'tcx
> {
307 // A<Struct> -> A<Trait> conversion
308 let (src_pointee_ty
, dest_pointee_ty
) =
309 self.tcx
.struct_lockstep_tails_erasing_lifetimes(source_ty
, cast_ty
, self.param_env
);
311 match (&src_pointee_ty
.kind(), &dest_pointee_ty
.kind()) {
312 (&ty
::Array(_
, length
), &ty
::Slice(_
)) => {
313 let ptr
= self.read_immediate(src
)?
.to_scalar()?
;
314 // u64 cast is from usize to u64, which is always good
316 Immediate
::new_slice(ptr
, length
.eval_usize(*self.tcx
, self.param_env
), self);
317 self.write_immediate(val
, dest
)
319 (&ty
::Dynamic(ref data_a
, ..), &ty
::Dynamic(ref data_b
, ..)) => {
320 let val
= self.read_immediate(src
)?
;
321 if data_a
.principal_def_id() == data_b
.principal_def_id() {
322 return self.write_immediate(*val
, dest
);
324 // trait upcasting coercion
325 let vptr_entry_idx
= self.tcx
.vtable_trait_upcasting_coercion_new_vptr_slot((
330 if let Some(entry_idx
) = vptr_entry_idx
{
331 let entry_idx
= u64::try_from(entry_idx
).unwrap();
332 let (old_data
, old_vptr
) = val
.to_scalar_pair()?
;
333 let old_vptr
= self.scalar_to_ptr(old_vptr
)?
;
335 .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr
, entry_idx
)?
;
336 self.write_immediate(Immediate
::new_dyn_trait(old_data
, new_vptr
, self), dest
)
338 self.write_immediate(*val
, dest
)
341 (_
, &ty
::Dynamic(ref data
, _
)) => {
342 // Initial cast from sized to dyn trait
343 let vtable
= self.get_vtable(src_pointee_ty
, data
.principal())?
;
344 let ptr
= self.read_immediate(src
)?
.to_scalar()?
;
345 let val
= Immediate
::new_dyn_trait(ptr
, vtable
, &*self.tcx
);
346 self.write_immediate(val
, dest
)
350 span_bug
!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src
.layout
.ty
, cast_ty
)
357 src
: &OpTy
<'tcx
, M
::PointerTag
>,
358 cast_ty
: TyAndLayout
<'tcx
>,
359 dest
: &PlaceTy
<'tcx
, M
::PointerTag
>,
360 ) -> InterpResult
<'tcx
> {
361 trace
!("Unsizing {:?} of type {} into {:?}", *src
, src
.layout
.ty
, cast_ty
.ty
);
362 match (&src
.layout
.ty
.kind(), &cast_ty
.ty
.kind()) {
363 (&ty
::Ref(_
, s
, _
), &ty
::Ref(_
, c
, _
) | &ty
::RawPtr(TypeAndMut { ty: c, .. }
))
364 | (&ty
::RawPtr(TypeAndMut { ty: s, .. }
), &ty
::RawPtr(TypeAndMut { ty: c, .. }
)) => {
365 self.unsize_into_ptr(src
, dest
, *s
, *c
)
367 (&ty
::Adt(def_a
, _
), &ty
::Adt(def_b
, _
)) => {
368 assert_eq
!(def_a
, def_b
);
369 if def_a
.is_box() || def_b
.is_box() {
370 if !def_a
.is_box() || !def_b
.is_box() {
373 "invalid unsizing between {:?} -> {:?}",
378 return self.unsize_into_ptr(
381 src
.layout
.ty
.boxed_ty(),
382 cast_ty
.ty
.boxed_ty(),
386 // unsizing of generic struct with pointer fields
387 // Example: `Arc<T>` -> `Arc<Trait>`
388 // here we need to increase the size of every &T thin ptr field to a fat ptr
389 for i
in 0..src
.layout
.fields
.count() {
390 let cast_ty_field
= cast_ty
.field(self, i
);
391 if cast_ty_field
.is_zst() {
394 let src_field
= self.operand_field(src
, i
)?
;
395 let dst_field
= self.place_field(dest
, i
)?
;
396 if src_field
.layout
.ty
== cast_ty_field
.ty
{
397 self.copy_op(&src_field
, &dst_field
)?
;
399 self.unsize_into(&src_field
, cast_ty_field
, &dst_field
)?
;
406 "unsize_into: invalid conversion: {:?} -> {:?}",