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