1 use std::convert::TryFrom;
3 use rustc_apfloat::ieee::{Double, Single};
4 use rustc_apfloat::{Float, FloatConvert};
5 use rustc_ast::ast::FloatTy;
6 use rustc_attr as attr;
7 use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
8 use rustc_middle::mir::CastKind;
9 use rustc_middle::ty::adjustment::PointerCast;
10 use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
11 use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
12 use rustc_span::symbol::sym;
13 use rustc_target::abi::{Integer, LayoutOf, Variants};
15 use super::{truncate, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy};
17 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
20 src: OpTy<'tcx, M::PointerTag>,
23 dest: PlaceTy<'tcx, M::PointerTag>,
24 ) -> InterpResult<'tcx> {
25 use rustc_middle::mir::CastKind::*;
26 // FIXME: In which cases should we trigger UB when the source is uninit?
28 Pointer(PointerCast::Unsize) => {
29 let cast_ty = self.layout_of(cast_ty)?;
30 self.unsize_into(src, cast_ty, dest)?;
34 let src = self.read_immediate(src)?;
35 let res = self.misc_cast(src, cast_ty)?;
36 self.write_immediate(res, dest)?;
39 Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => {
40 // These are NOPs, but can be wide pointers.
41 let v = self.read_immediate(src)?;
42 self.write_immediate(*v, dest)?;
45 Pointer(PointerCast::ReifyFnPointer) => {
46 // The src operand does not matter, just its type
47 match src.layout.ty.kind {
48 ty::FnDef(def_id, substs) => {
49 // All reifications must be monomorphic, bail out otherwise.
50 if src.layout.ty.needs_subst() {
51 throw_inval!(TooGeneric);
54 if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
55 bug!("reifying a fn ptr that requires const arguments");
58 let instance = ty::Instance::resolve_for_fn_ptr(
64 .ok_or_else(|| err_inval!(TooGeneric))?;
66 let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
67 self.write_scalar(fn_ptr, dest)?;
69 _ => bug!("reify fn pointer on {:?}", src.layout.ty),
73 Pointer(PointerCast::UnsafeFnPointer) => {
74 let src = self.read_immediate(src)?;
78 self.write_immediate(*src, dest)?;
80 _ => bug!("fn to unsafe fn cast on {:?}", cast_ty),
84 Pointer(PointerCast::ClosureFnPointer(_)) => {
85 // The src operand does not matter, just its type
86 match src.layout.ty.kind {
87 ty::Closure(def_id, substs) => {
88 // All reifications must be monomorphic, bail out otherwise.
89 if src.layout.ty.needs_subst() {
90 throw_inval!(TooGeneric);
93 let instance = ty::Instance::resolve_closure(
97 ty::ClosureKind::FnOnce,
99 let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
100 self.write_scalar(fn_ptr, dest)?;
102 _ => bug!("closure fn pointer on {:?}", src.layout.ty),
111 src: ImmTy<'tcx, M::PointerTag>,
113 ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
114 use rustc_middle::ty::TyKind::*;
115 trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty);
117 match src.layout.ty.kind {
119 Float(FloatTy::F32) => {
120 return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into());
122 Float(FloatTy::F64) => {
123 return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
125 // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
126 // are represented as integers.
128 src.layout.ty.is_bool()
129 || src.layout.ty.is_char()
130 || src.layout.ty.is_enum()
131 || src.layout.ty.is_integral()
132 || src.layout.ty.is_any_ptr(),
133 "Unexpected cast from type {:?}",
138 // # First handle non-scalar source values.
140 // Handle cast from a univariant (ZST) enum.
141 match src.layout.variants {
142 Variants::Single { index } => {
143 if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) {
144 assert!(src.layout.is_zst());
145 let discr_layout = self.layout_of(discr.ty)?;
146 return Ok(self.cast_from_scalar(discr.val, discr_layout, cast_ty).into());
149 Variants::Multiple { .. } => {}
152 // Handle casting any ptr to raw ptr (might be a fat ptr).
153 if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() {
154 let dest_layout = self.layout_of(cast_ty)?;
155 if dest_layout.size == src.layout.size {
156 // Thin or fat pointer that just hast the ptr kind of target type changed.
159 // Casting the metadata away from a fat ptr.
160 assert_eq!(src.layout.size, 2 * self.memory.pointer_size());
161 assert_eq!(dest_layout.size, self.memory.pointer_size());
162 assert!(src.layout.ty.is_unsafe_ptr());
164 Immediate::ScalarPair(data, _) => Ok(data.into()),
165 Immediate::Scalar(..) => bug!(
166 "{:?} input to a fat-to-thin cast ({:?} -> {:?})",
175 // # The remaining source values are scalar.
177 // For all remaining casts, we either
178 // (a) cast a raw ptr to usize, or
179 // (b) cast from an integer-like (including bool, char, enums).
180 // In both cases we want the bits.
181 let bits = self.force_bits(src.to_scalar()?, src.layout.size)?;
182 Ok(self.cast_from_scalar(bits, src.layout, cast_ty).into())
185 pub(super) fn cast_from_scalar(
187 v: u128, // raw bits (there is no ScalarTy so we separate data+layout)
188 src_layout: TyAndLayout<'tcx>,
190 ) -> Scalar<M::PointerTag> {
191 // Let's make sure v is sign-extended *if* it has a signed type.
192 let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
193 let v = if signed { self.sign_extend(v, src_layout) } else { v };
194 trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
195 use rustc_middle::ty::TyKind::*;
197 Int(_) | Uint(_) | RawPtr(_) => {
198 let size = match cast_ty.kind {
199 Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(),
200 Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(),
201 RawPtr(_) => self.pointer_size(),
204 let v = truncate(v, size);
205 Scalar::from_uint(v, size)
208 Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value),
209 Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value),
210 Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value),
211 Float(FloatTy::F64) => Scalar::from_f64(Double::from_u128(v).value),
214 // `u8` to `char` cast
215 Scalar::from_u32(u8::try_from(v).unwrap().into())
218 // Casts to bool are not permitted by rustc, no need to handle them here.
219 _ => bug!("invalid int to {:?} cast", cast_ty),
223 fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag>
225 F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>,
227 use rustc_middle::ty::TyKind::*;
231 let size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size();
232 // `to_u128` is a saturating cast, which is what we need
233 // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
234 let v = f.to_u128(size.bits_usize()).value;
235 // This should already fit the bit width
236 Scalar::from_uint(v, size)
240 let size = Integer::from_attr(self, attr::IntType::SignedInt(t)).size();
241 // `to_i128` is a saturating cast, which is what we need
242 // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r).
243 let v = f.to_i128(size.bits_usize()).value;
244 Scalar::from_int(v, size)
247 Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value),
249 Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value),
251 _ => bug!("invalid float to {:?} cast", dest_ty),
257 src: OpTy<'tcx, M::PointerTag>,
258 dest: PlaceTy<'tcx, M::PointerTag>,
262 ) -> InterpResult<'tcx> {
263 // A<Struct> -> A<Trait> conversion
264 let (src_pointee_ty, dest_pointee_ty) =
265 self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env);
267 match (&src_pointee_ty.kind, &dest_pointee_ty.kind) {
268 (&ty::Array(_, length), &ty::Slice(_)) => {
269 let ptr = self.read_immediate(src)?.to_scalar()?;
270 // u64 cast is from usize to u64, which is always good
272 Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
273 self.write_immediate(val, dest)
275 (&ty::Dynamic(..), &ty::Dynamic(..)) => {
276 // For now, upcasts are limited to changes in marker
277 // traits, and hence never actually require an actual
278 // change to the vtable.
279 let val = self.read_immediate(src)?;
280 self.write_immediate(*val, dest)
282 (_, &ty::Dynamic(ref data, _)) => {
283 // Initial cast from sized to dyn trait
284 let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
285 let ptr = self.read_immediate(src)?.to_scalar()?;
286 let val = Immediate::new_dyn_trait(ptr, vtable);
287 self.write_immediate(val, dest)
290 _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty),
296 src: OpTy<'tcx, M::PointerTag>,
297 cast_ty: TyAndLayout<'tcx>,
298 dest: PlaceTy<'tcx, M::PointerTag>,
299 ) -> InterpResult<'tcx> {
300 trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty);
301 match (&src.layout.ty.kind, &cast_ty.ty.kind) {
302 (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. }))
303 | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => {
304 self.unsize_into_ptr(src, dest, s, c)
306 (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
307 assert_eq!(def_a, def_b);
308 if def_a.is_box() || def_b.is_box() {
309 if !def_a.is_box() || !def_b.is_box() {
310 bug!("invalid unsizing between {:?} -> {:?}", src.layout.ty, cast_ty.ty);
312 return self.unsize_into_ptr(
315 src.layout.ty.boxed_ty(),
316 cast_ty.ty.boxed_ty(),
320 // unsizing of generic struct with pointer fields
321 // Example: `Arc<T>` -> `Arc<Trait>`
322 // here we need to increase the size of every &T thin ptr field to a fat ptr
323 for i in 0..src.layout.fields.count() {
324 let cast_ty_field = cast_ty.field(self, i)?;
325 if cast_ty_field.is_zst() {
328 let src_field = self.operand_field(src, i)?;
329 let dst_field = self.place_field(dest, i)?;
330 if src_field.layout.ty == cast_ty_field.ty {
331 self.copy_op(src_field, dst_field)?;
333 self.unsize_into(src_field, cast_ty_field, dst_field)?;
338 _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", src.layout, dest.layout),