]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/cast.rs
cfe856abe36dda2ca6c6c073417d1c864c711138
[rust.git] / src / librustc_mir / interpret / cast.rs
1 use std::convert::TryFrom;
2
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};
14
15 use super::{truncate, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy};
16
17 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
18     pub fn cast(
19         &mut self,
20         src: OpTy<'tcx, M::PointerTag>,
21         cast_kind: CastKind,
22         cast_ty: Ty<'tcx>,
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?
27         match cast_kind {
28             Pointer(PointerCast::Unsize) => {
29                 let cast_ty = self.layout_of(cast_ty)?;
30                 self.unsize_into(src, cast_ty, dest)?;
31             }
32
33             Misc => {
34                 let src = self.read_immediate(src)?;
35                 let res = self.misc_cast(src, cast_ty)?;
36                 self.write_immediate(res, dest)?;
37             }
38
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)?;
43             }
44
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);
52                         }
53
54                         if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
55                             bug!("reifying a fn ptr that requires const arguments");
56                         }
57
58                         let instance = ty::Instance::resolve_for_fn_ptr(
59                             *self.tcx,
60                             self.param_env,
61                             def_id,
62                             substs,
63                         )
64                         .ok_or_else(|| err_inval!(TooGeneric))?;
65
66                         let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
67                         self.write_scalar(fn_ptr, dest)?;
68                     }
69                     _ => bug!("reify fn pointer on {:?}", src.layout.ty),
70                 }
71             }
72
73             Pointer(PointerCast::UnsafeFnPointer) => {
74                 let src = self.read_immediate(src)?;
75                 match cast_ty.kind {
76                     ty::FnPtr(_) => {
77                         // No change to value
78                         self.write_immediate(*src, dest)?;
79                     }
80                     _ => bug!("fn to unsafe fn cast on {:?}", cast_ty),
81                 }
82             }
83
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);
91                         }
92
93                         let instance = ty::Instance::resolve_closure(
94                             *self.tcx,
95                             def_id,
96                             substs,
97                             ty::ClosureKind::FnOnce,
98                         );
99                         let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
100                         self.write_scalar(fn_ptr, dest)?;
101                     }
102                     _ => bug!("closure fn pointer on {:?}", src.layout.ty),
103                 }
104             }
105         }
106         Ok(())
107     }
108
109     fn misc_cast(
110         &self,
111         src: ImmTy<'tcx, M::PointerTag>,
112         cast_ty: Ty<'tcx>,
113     ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
114         use rustc_middle::ty::TyKind::*;
115         trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty);
116
117         match src.layout.ty.kind {
118             // Floating point
119             Float(FloatTy::F32) => {
120                 return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into());
121             }
122             Float(FloatTy::F64) => {
123                 return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
124             }
125             // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
126             // are represented as integers.
127             _ => assert!(
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 {:?}",
134                 src.layout.ty
135             ),
136         }
137
138         // # First handle non-scalar source values.
139
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());
147                 }
148             }
149             Variants::Multiple { .. } => {}
150         }
151
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.
157                 return Ok(*src);
158             } else {
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());
163                 return match *src {
164                     Immediate::ScalarPair(data, _) => Ok(data.into()),
165                     Immediate::Scalar(..) => bug!(
166                         "{:?} input to a fat-to-thin cast ({:?} -> {:?})",
167                         *src,
168                         src.layout.ty,
169                         cast_ty
170                     ),
171                 };
172             }
173         }
174
175         // # The remaining source values are scalar.
176
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())
183     }
184
185     pub(super) fn cast_from_scalar(
186         &self,
187         v: u128, // raw bits (there is no ScalarTy so we separate data+layout)
188         src_layout: TyAndLayout<'tcx>,
189         cast_ty: Ty<'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::*;
196         match cast_ty.kind {
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(),
202                     _ => bug!(),
203                 };
204                 let v = truncate(v, size);
205                 Scalar::from_uint(v, size)
206             }
207
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),
212
213             Char => {
214                 // `u8` to `char` cast
215                 Scalar::from_u32(u8::try_from(v).unwrap().into())
216             }
217
218             // Casts to bool are not permitted by rustc, no need to handle them here.
219             _ => bug!("invalid int to {:?} cast", cast_ty),
220         }
221     }
222
223     fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag>
224     where
225         F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>,
226     {
227         use rustc_middle::ty::TyKind::*;
228         match dest_ty.kind {
229             // float -> uint
230             Uint(t) => {
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)
237             }
238             // float -> int
239             Int(t) => {
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)
245             }
246             // float -> f32
247             Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value),
248             // float -> f64
249             Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value),
250             // That's it.
251             _ => bug!("invalid float to {:?} cast", dest_ty),
252         }
253     }
254
255     fn unsize_into_ptr(
256         &mut self,
257         src: OpTy<'tcx, M::PointerTag>,
258         dest: PlaceTy<'tcx, M::PointerTag>,
259         // The pointee types
260         source_ty: Ty<'tcx>,
261         cast_ty: Ty<'tcx>,
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);
266
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
271                 let val =
272                     Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
273                 self.write_immediate(val, dest)
274             }
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)
281             }
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)
288             }
289
290             _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty),
291         }
292     }
293
294     fn unsize_into(
295         &mut self,
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)
305             }
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);
311                     }
312                     return self.unsize_into_ptr(
313                         src,
314                         dest,
315                         src.layout.ty.boxed_ty(),
316                         cast_ty.ty.boxed_ty(),
317                     );
318                 }
319
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() {
326                         continue;
327                     }
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)?;
332                     } else {
333                         self.unsize_into(src_field, cast_ty_field, dst_field)?;
334                     }
335                 }
336                 Ok(())
337             }
338             _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", src.layout, dest.layout),
339         }
340     }
341 }