]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/cast.rs
Rollup merge of #67059 - TommasoBianchi:dropck_fix_pr, r=pnkfelix
[rust.git] / src / librustc_mir / interpret / cast.rs
1 use rustc::ty::{self, Ty, TypeAndMut, TypeFoldable};
2 use rustc::ty::layout::{self, TyLayout, Size};
3 use rustc::ty::adjustment::{PointerCast};
4 use syntax::ast::FloatTy;
5 use syntax::symbol::sym;
6
7 use rustc_apfloat::ieee::{Single, Double};
8 use rustc_apfloat::{Float, FloatConvert};
9 use rustc::mir::interpret::{
10     Scalar, InterpResult, PointerArithmetic,
11 };
12 use rustc::mir::CastKind;
13
14 use super::{InterpCx, Machine, PlaceTy, OpTy, ImmTy, Immediate, FnVal};
15
16 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
17     pub fn cast(
18         &mut self,
19         src: OpTy<'tcx, M::PointerTag>,
20         kind: CastKind,
21         dest: PlaceTy<'tcx, M::PointerTag>,
22     ) -> InterpResult<'tcx> {
23         use rustc::mir::CastKind::*;
24         match kind {
25             Pointer(PointerCast::Unsize) => {
26                 self.unsize_into(src, dest)?;
27             }
28
29             Misc
30             | Pointer(PointerCast::MutToConstPointer)
31             | Pointer(PointerCast::ArrayToPointer) => {
32                 let src = self.read_immediate(src)?;
33                 let res = self.cast_immediate(src, dest.layout)?;
34                 self.write_immediate(res, dest)?;
35             }
36
37             Pointer(PointerCast::ReifyFnPointer) => {
38                 // The src operand does not matter, just its type
39                 match src.layout.ty.kind {
40                     ty::FnDef(def_id, substs) => {
41                         // All reifications must be monomorphic, bail out otherwise.
42                         if src.layout.ty.needs_subst() {
43                             throw_inval!(TooGeneric);
44                         }
45
46                         if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
47                             bug!("reifying a fn ptr that requires const arguments");
48                         }
49
50                         let instance = ty::Instance::resolve_for_fn_ptr(
51                             *self.tcx,
52                             self.param_env,
53                             def_id,
54                             substs,
55                         ).ok_or_else(|| err_inval!(TooGeneric))?;
56
57                         let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
58                         self.write_scalar(fn_ptr, dest)?;
59                     }
60                     _ => bug!("reify fn pointer on {:?}", src.layout.ty),
61                 }
62             }
63
64             Pointer(PointerCast::UnsafeFnPointer) => {
65                 let src = self.read_immediate(src)?;
66                 match dest.layout.ty.kind {
67                     ty::FnPtr(_) => {
68                         // No change to value
69                         self.write_immediate(*src, dest)?;
70                     }
71                     _ => bug!("fn to unsafe fn cast on {:?}", dest.layout.ty),
72                 }
73             }
74
75             Pointer(PointerCast::ClosureFnPointer(_)) => {
76                 // The src operand does not matter, just its type
77                 match src.layout.ty.kind {
78                     ty::Closure(def_id, substs) => {
79                         // All reifications must be monomorphic, bail out otherwise.
80                         if src.layout.ty.needs_subst() {
81                             throw_inval!(TooGeneric);
82                         }
83
84                         let instance = ty::Instance::resolve_closure(
85                             *self.tcx,
86                             def_id,
87                             substs,
88                             ty::ClosureKind::FnOnce,
89                         );
90                         let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
91                         self.write_scalar(fn_ptr, dest)?;
92                     }
93                     _ => bug!("closure fn pointer on {:?}", src.layout.ty),
94                 }
95             }
96         }
97         Ok(())
98     }
99
100     fn cast_immediate(
101         &self,
102         src: ImmTy<'tcx, M::PointerTag>,
103         dest_layout: TyLayout<'tcx>,
104     ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
105         use rustc::ty::TyKind::*;
106         trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, dest_layout.ty);
107
108         match src.layout.ty.kind {
109             // Floating point
110             Float(FloatTy::F32) =>
111                 return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, dest_layout.ty)?.into()),
112             Float(FloatTy::F64) =>
113                 return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, dest_layout.ty)?.into()),
114             // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
115             // are represented as integers.
116             _ =>
117                 assert!(
118                     src.layout.ty.is_bool()       || src.layout.ty.is_char()     ||
119                     src.layout.ty.is_enum()       || src.layout.ty.is_integral() ||
120                     src.layout.ty.is_any_ptr(),
121                     "Unexpected cast from type {:?}", src.layout.ty
122                 )
123         }
124
125         // Handle cast from a univariant (ZST) enum.
126         match src.layout.variants {
127             layout::Variants::Single { index } => {
128                 if let Some(discr) =
129                     src.layout.ty.discriminant_for_variant(*self.tcx, index)
130                 {
131                     assert!(src.layout.is_zst());
132                     return Ok(Scalar::from_uint(discr.val, dest_layout.size).into());
133                 }
134             }
135             layout::Variants::Multiple { .. } => {},
136         }
137
138         // Handle casting the metadata away from a fat pointer.
139         if src.layout.ty.is_unsafe_ptr() && dest_layout.ty.is_unsafe_ptr() &&
140             dest_layout.size != src.layout.size
141         {
142             assert_eq!(src.layout.size, 2*self.memory.pointer_size());
143             assert_eq!(dest_layout.size, self.memory.pointer_size());
144             assert!(dest_layout.ty.is_unsafe_ptr());
145             match *src {
146                 Immediate::ScalarPair(data, _) =>
147                     return Ok(data.into()),
148                 Immediate::Scalar(..) =>
149                     bug!(
150                         "{:?} input to a fat-to-thin cast ({:?} -> {:?})",
151                         *src, src.layout.ty, dest_layout.ty
152                     ),
153             };
154         }
155
156         // Handle casting any ptr to raw ptr (might be a fat ptr).
157         if src.layout.ty.is_any_ptr() && dest_layout.ty.is_unsafe_ptr()
158         {
159             // The only possible size-unequal case was handled above.
160             assert_eq!(src.layout.size, dest_layout.size);
161             return Ok(*src);
162         }
163
164         // For all remaining casts, we either
165         // (a) cast a raw ptr to usize, or
166         // (b) cast from an integer-like (including bool, char, enums).
167         // In both cases we want the bits.
168         let bits = self.force_bits(src.to_scalar()?, src.layout.size)?;
169         Ok(self.cast_from_int(bits, src.layout, dest_layout)?.into())
170     }
171
172     fn cast_from_int(
173         &self,
174         v: u128, // raw bits
175         src_layout: TyLayout<'tcx>,
176         dest_layout: TyLayout<'tcx>,
177     ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
178         // Let's make sure v is sign-extended *if* it has a signed type.
179         let signed = src_layout.abi.is_signed();
180         let v = if signed {
181             self.sign_extend(v, src_layout)
182         } else {
183             v
184         };
185         trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty);
186         use rustc::ty::TyKind::*;
187         match dest_layout.ty.kind {
188             Int(_) | Uint(_) | RawPtr(_) => {
189                 let v = self.truncate(v, dest_layout);
190                 Ok(Scalar::from_uint(v, dest_layout.size))
191             }
192
193             Float(FloatTy::F32) if signed => Ok(Scalar::from_f32(
194                 Single::from_i128(v as i128).value
195             )),
196             Float(FloatTy::F64) if signed => Ok(Scalar::from_f64(
197                 Double::from_i128(v as i128).value
198             )),
199             Float(FloatTy::F32) => Ok(Scalar::from_f32(
200                 Single::from_u128(v).value
201             )),
202             Float(FloatTy::F64) => Ok(Scalar::from_f64(
203                 Double::from_u128(v).value
204             )),
205
206             Char => {
207                 // `u8` to `char` cast
208                 debug_assert_eq!(v as u8 as u128, v);
209                 Ok(Scalar::from_uint(v, Size::from_bytes(4)))
210             },
211
212             // Casts to bool are not permitted by rustc, no need to handle them here.
213             _ => bug!("invalid int to {:?} cast", dest_layout.ty),
214         }
215     }
216
217     fn cast_from_float<F>(
218         &self,
219         f: F,
220         dest_ty: Ty<'tcx>
221     ) -> InterpResult<'tcx, Scalar<M::PointerTag>>
222     where F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>
223     {
224         use rustc::ty::TyKind::*;
225         match dest_ty.kind {
226             // float -> uint
227             Uint(t) => {
228                 let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
229                 let v = f.to_u128(width).value;
230                 // This should already fit the bit width
231                 Ok(Scalar::from_uint(v, Size::from_bits(width as u64)))
232             },
233             // float -> int
234             Int(t) => {
235                 let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
236                 let v = f.to_i128(width).value;
237                 Ok(Scalar::from_int(v, Size::from_bits(width as u64)))
238             },
239             // float -> f32
240             Float(FloatTy::F32) =>
241                 Ok(Scalar::from_f32(f.convert(&mut false).value)),
242             // float -> f64
243             Float(FloatTy::F64) =>
244                 Ok(Scalar::from_f64(f.convert(&mut false).value)),
245             // That's it.
246             _ => bug!("invalid float to {:?} cast", dest_ty),
247         }
248     }
249
250     fn unsize_into_ptr(
251         &mut self,
252         src: OpTy<'tcx, M::PointerTag>,
253         dest: PlaceTy<'tcx, M::PointerTag>,
254         // The pointee types
255         source_ty: Ty<'tcx>,
256         dest_ty: Ty<'tcx>,
257     ) -> InterpResult<'tcx> {
258         // A<Struct> -> A<Trait> conversion
259         let (src_pointee_ty, dest_pointee_ty) =
260             self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, dest_ty, self.param_env);
261
262         match (&src_pointee_ty.kind, &dest_pointee_ty.kind) {
263             (&ty::Array(_, length), &ty::Slice(_)) => {
264                 let ptr = self.read_immediate(src)?.to_scalar()?;
265                 // u64 cast is from usize to u64, which is always good
266                 let val = Immediate::new_slice(
267                     ptr,
268                     length.eval_usize(self.tcx.tcx, self.param_env),
269                     self,
270                 );
271                 self.write_immediate(val, dest)
272             }
273             (&ty::Dynamic(..), &ty::Dynamic(..)) => {
274                 // For now, upcasts are limited to changes in marker
275                 // traits, and hence never actually require an actual
276                 // change to the vtable.
277                 let val = self.read_immediate(src)?;
278                 self.write_immediate(*val, dest)
279             }
280             (_, &ty::Dynamic(ref data, _)) => {
281                 // Initial cast from sized to dyn trait
282                 let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
283                 let ptr = self.read_immediate(src)?.to_scalar()?;
284                 let val = Immediate::new_dyn_trait(ptr, vtable);
285                 self.write_immediate(val, dest)
286             }
287
288             _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, dest.layout.ty),
289         }
290     }
291
292     fn unsize_into(
293         &mut self,
294         src: OpTy<'tcx, M::PointerTag>,
295         dest: PlaceTy<'tcx, M::PointerTag>,
296     ) -> InterpResult<'tcx> {
297         trace!("Unsizing {:?} into {:?}", src, dest);
298         match (&src.layout.ty.kind, &dest.layout.ty.kind) {
299             (&ty::Ref(_, s, _), &ty::Ref(_, d, _)) |
300             (&ty::Ref(_, s, _), &ty::RawPtr(TypeAndMut { ty: d, .. })) |
301             (&ty::RawPtr(TypeAndMut { ty: s, .. }),
302              &ty::RawPtr(TypeAndMut { ty: d, .. })) => {
303                 self.unsize_into_ptr(src, dest, s, d)
304             }
305             (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
306                 assert_eq!(def_a, def_b);
307                 if def_a.is_box() || def_b.is_box() {
308                     if !def_a.is_box() || !def_b.is_box() {
309                         bug!("invalid unsizing between {:?} -> {:?}", src.layout, dest.layout);
310                     }
311                     return self.unsize_into_ptr(
312                         src,
313                         dest,
314                         src.layout.ty.boxed_ty(),
315                         dest.layout.ty.boxed_ty(),
316                     );
317                 }
318
319                 // unsizing of generic struct with pointer fields
320                 // Example: `Arc<T>` -> `Arc<Trait>`
321                 // here we need to increase the size of every &T thin ptr field to a fat ptr
322                 for i in 0..src.layout.fields.count() {
323                     let dst_field = self.place_field(dest, i as u64)?;
324                     if dst_field.layout.is_zst() {
325                         continue;
326                     }
327                     let src_field = self.operand_field(src, i as u64)?;
328                     if src_field.layout.ty == dst_field.layout.ty {
329                         self.copy_op(src_field, dst_field)?;
330                     } else {
331                         self.unsize_into(src_field, dst_field)?;
332                     }
333                 }
334                 Ok(())
335             }
336             _ => {
337                 bug!(
338                     "unsize_into: invalid conversion: {:?} -> {:?}",
339                     src.layout,
340                     dest.layout
341                 )
342             }
343         }
344     }
345 }