]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/cast.rs
Rollup merge of #56914 - glaubitz:ignore-tests, r=alexcrichton
[rust.git] / src / librustc_mir / interpret / cast.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::ty::{self, Ty, TypeAndMut};
12 use rustc::ty::layout::{self, TyLayout, Size};
13 use syntax::ast::{FloatTy, IntTy, UintTy};
14
15 use rustc_apfloat::ieee::{Single, Double};
16 use rustc::mir::interpret::{
17     Scalar, EvalResult, Pointer, PointerArithmetic, EvalErrorKind, truncate
18 };
19 use rustc::mir::CastKind;
20 use rustc_apfloat::Float;
21
22 use super::{EvalContext, Machine, PlaceTy, OpTy, Immediate};
23
24 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
25     fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
26         match ty.sty {
27             ty::RawPtr(ty::TypeAndMut { ty, .. }) |
28             ty::Ref(_, ty, _) => !self.type_is_sized(ty),
29             ty::Adt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()),
30             _ => false,
31         }
32     }
33
34     pub fn cast(
35         &mut self,
36         src: OpTy<'tcx, M::PointerTag>,
37         kind: CastKind,
38         dest: PlaceTy<'tcx, M::PointerTag>,
39     ) -> EvalResult<'tcx> {
40         use rustc::mir::CastKind::*;
41         match kind {
42             Unsize => {
43                 self.unsize_into(src, dest)?;
44             }
45
46             Misc => {
47                 let src = self.read_immediate(src)?;
48
49                 if self.type_is_fat_ptr(src.layout.ty) {
50                     match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
51                         // pointers to extern types
52                         (Immediate::Scalar(_),_) |
53                         // slices and trait objects to other slices/trait objects
54                         (Immediate::ScalarPair(..), true) => {
55                             // No change to immediate
56                             self.write_immediate(*src, dest)?;
57                         }
58                         // slices and trait objects to thin pointers (dropping the metadata)
59                         (Immediate::ScalarPair(data, _), false) => {
60                             self.write_scalar(data, dest)?;
61                         }
62                     }
63                 } else {
64                     match src.layout.variants {
65                         layout::Variants::Single { index } => {
66                             if let Some(def) = src.layout.ty.ty_adt_def() {
67                                 // Cast from a univariant enum
68                                 assert!(src.layout.is_zst());
69                                 let discr_val = def
70                                     .discriminant_for_variant(*self.tcx, index)
71                                     .val;
72                                 return self.write_scalar(
73                                     Scalar::from_uint(discr_val, dest.layout.size),
74                                     dest);
75                             }
76                         }
77                         layout::Variants::Tagged { .. } |
78                         layout::Variants::NicheFilling { .. } => {},
79                     }
80
81                     let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
82                     self.write_scalar(dest_val, dest)?;
83                 }
84             }
85
86             ReifyFnPointer => {
87                 // The src operand does not matter, just its type
88                 match src.layout.ty.sty {
89                     ty::FnDef(def_id, substs) => {
90                         if self.tcx.has_attr(def_id, "rustc_args_required_const") {
91                             bug!("reifying a fn ptr that requires \
92                                     const arguments");
93                         }
94                         let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
95                             *self.tcx,
96                             self.param_env,
97                             def_id,
98                             substs,
99                         ).ok_or_else(|| EvalErrorKind::TooGeneric.into());
100                         let fn_ptr = self.memory.create_fn_alloc(instance?).with_default_tag();
101                         self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
102                     }
103                     ref other => bug!("reify fn pointer on {:?}", other),
104                 }
105             }
106
107             UnsafeFnPointer => {
108                 let src = self.read_immediate(src)?;
109                 match dest.layout.ty.sty {
110                     ty::FnPtr(_) => {
111                         // No change to value
112                         self.write_immediate(*src, dest)?;
113                     }
114                     ref other => bug!("fn to unsafe fn cast on {:?}", other),
115                 }
116             }
117
118             ClosureFnPointer => {
119                 // The src operand does not matter, just its type
120                 match src.layout.ty.sty {
121                     ty::Closure(def_id, substs) => {
122                         let substs = self.tcx.subst_and_normalize_erasing_regions(
123                             self.substs(),
124                             ty::ParamEnv::reveal_all(),
125                             &substs,
126                         );
127                         let instance = ty::Instance::resolve_closure(
128                             *self.tcx,
129                             def_id,
130                             substs,
131                             ty::ClosureKind::FnOnce,
132                         );
133                         let fn_ptr = self.memory.create_fn_alloc(instance).with_default_tag();
134                         let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
135                         self.write_immediate(val, dest)?;
136                     }
137                     ref other => bug!("closure fn pointer on {:?}", other),
138                 }
139             }
140         }
141         Ok(())
142     }
143
144     pub(super) fn cast_scalar(
145         &self,
146         val: Scalar<M::PointerTag>,
147         src_layout: TyLayout<'tcx>,
148         dest_layout: TyLayout<'tcx>,
149     ) -> EvalResult<'tcx, Scalar<M::PointerTag>> {
150         use rustc::ty::TyKind::*;
151         trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);
152
153         match val {
154             Scalar::Ptr(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
155             Scalar::Bits { bits, size } => {
156                 debug_assert_eq!(size as u64, src_layout.size.bytes());
157                 debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
158                     "Unexpected value of size {} before casting", size);
159
160                 let res = match src_layout.ty.sty {
161                     Float(fty) => self.cast_from_float(bits, fty, dest_layout.ty)?,
162                     _ => self.cast_from_int(bits, src_layout, dest_layout)?,
163                 };
164
165                 // Sanity check
166                 match res {
167                     Scalar::Ptr(_) => bug!("Fabricated a ptr value from an int...?"),
168                     Scalar::Bits { bits, size } => {
169                         debug_assert_eq!(size as u64, dest_layout.size.bytes());
170                         debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
171                             "Unexpected value of size {} after casting", size);
172                     }
173                 }
174                 // Done
175                 Ok(res)
176             }
177         }
178     }
179
180     fn cast_from_int(
181         &self,
182         v: u128,
183         src_layout: TyLayout<'tcx>,
184         dest_layout: TyLayout<'tcx>,
185     ) -> EvalResult<'tcx, Scalar<M::PointerTag>> {
186         let signed = src_layout.abi.is_signed();
187         let v = if signed {
188             self.sign_extend(v, src_layout)
189         } else {
190             v
191         };
192         trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty);
193         use rustc::ty::TyKind::*;
194         match dest_layout.ty.sty {
195             Int(_) | Uint(_) => {
196                 let v = self.truncate(v, dest_layout);
197                 Ok(Scalar::from_uint(v, dest_layout.size))
198             }
199
200             Float(FloatTy::F32) if signed => Ok(Scalar::from_uint(
201                 Single::from_i128(v as i128).value.to_bits(),
202                 Size::from_bits(32)
203             )),
204             Float(FloatTy::F64) if signed => Ok(Scalar::from_uint(
205                 Double::from_i128(v as i128).value.to_bits(),
206                 Size::from_bits(64)
207             )),
208             Float(FloatTy::F32) => Ok(Scalar::from_uint(
209                 Single::from_u128(v).value.to_bits(),
210                 Size::from_bits(32)
211             )),
212             Float(FloatTy::F64) => Ok(Scalar::from_uint(
213                 Double::from_u128(v).value.to_bits(),
214                 Size::from_bits(64)
215             )),
216
217             Char => {
218                 // `u8` to `char` cast
219                 debug_assert_eq!(v as u8 as u128, v);
220                 Ok(Scalar::from_uint(v, Size::from_bytes(4)))
221             },
222
223             // No alignment check needed for raw pointers.
224             // But we have to truncate to target ptr size.
225             RawPtr(_) => {
226                 Ok(Scalar::from_uint(
227                     self.truncate_to_ptr(v).0,
228                     self.pointer_size(),
229                 ))
230             },
231
232             // Casts to bool are not permitted by rustc, no need to handle them here.
233             _ => err!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))),
234         }
235     }
236
237     fn cast_from_float(
238         &self,
239         bits: u128,
240         fty: FloatTy,
241         dest_ty: Ty<'tcx>
242     ) -> EvalResult<'tcx, Scalar<M::PointerTag>> {
243         use rustc::ty::TyKind::*;
244         use rustc_apfloat::FloatConvert;
245         match dest_ty.sty {
246             // float -> uint
247             Uint(t) => {
248                 let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
249                 let v = match fty {
250                     FloatTy::F32 => Single::from_bits(bits).to_u128(width).value,
251                     FloatTy::F64 => Double::from_bits(bits).to_u128(width).value,
252                 };
253                 // This should already fit the bit width
254                 Ok(Scalar::from_uint(v, Size::from_bits(width as u64)))
255             },
256             // float -> int
257             Int(t) => {
258                 let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
259                 let v = match fty {
260                     FloatTy::F32 => Single::from_bits(bits).to_i128(width).value,
261                     FloatTy::F64 => Double::from_bits(bits).to_i128(width).value,
262                 };
263                 Ok(Scalar::from_int(v, Size::from_bits(width as u64)))
264             },
265             // f64 -> f32
266             Float(FloatTy::F32) if fty == FloatTy::F64 => {
267                 Ok(Scalar::from_uint(
268                     Single::to_bits(Double::from_bits(bits).convert(&mut false).value),
269                     Size::from_bits(32),
270                 ))
271             },
272             // f32 -> f64
273             Float(FloatTy::F64) if fty == FloatTy::F32 => {
274                 Ok(Scalar::from_uint(
275                     Double::to_bits(Single::from_bits(bits).convert(&mut false).value),
276                     Size::from_bits(64),
277                 ))
278             },
279             // identity cast
280             Float(FloatTy:: F64) => Ok(Scalar::from_uint(bits, Size::from_bits(64))),
281             Float(FloatTy:: F32) => Ok(Scalar::from_uint(bits, Size::from_bits(32))),
282             _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
283         }
284     }
285
286     fn cast_from_ptr(
287         &self,
288         ptr: Pointer<M::PointerTag>,
289         ty: Ty<'tcx>
290     ) -> EvalResult<'tcx, Scalar<M::PointerTag>> {
291         use rustc::ty::TyKind::*;
292         match ty.sty {
293             // Casting to a reference or fn pointer is not permitted by rustc,
294             // no need to support it here.
295             RawPtr(_) |
296             Int(IntTy::Isize) |
297             Uint(UintTy::Usize) => Ok(ptr.into()),
298             Int(_) | Uint(_) => err!(ReadPointerAsBytes),
299             _ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
300         }
301     }
302
303     fn unsize_into_ptr(
304         &mut self,
305         src: OpTy<'tcx, M::PointerTag>,
306         dest: PlaceTy<'tcx, M::PointerTag>,
307         // The pointee types
308         sty: Ty<'tcx>,
309         dty: Ty<'tcx>,
310     ) -> EvalResult<'tcx> {
311         // A<Struct> -> A<Trait> conversion
312         let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty);
313
314         match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
315             (&ty::Array(_, length), &ty::Slice(_)) => {
316                 let ptr = self.read_immediate(src)?.to_scalar_ptr()?;
317                 // u64 cast is from usize to u64, which is always good
318                 let val = Immediate::new_slice(
319                     ptr,
320                     length.unwrap_usize(self.tcx.tcx),
321                     self,
322                 );
323                 self.write_immediate(val, dest)
324             }
325             (&ty::Dynamic(..), &ty::Dynamic(..)) => {
326                 // For now, upcasts are limited to changes in marker
327                 // traits, and hence never actually require an actual
328                 // change to the vtable.
329                 let val = self.read_immediate(src)?;
330                 self.write_immediate(*val, dest)
331             }
332             (_, &ty::Dynamic(ref data, _)) => {
333                 // Initial cast from sized to dyn trait
334                 let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
335                 let ptr = self.read_immediate(src)?.to_scalar_ptr()?;
336                 let val = Immediate::new_dyn_trait(ptr, vtable);
337                 self.write_immediate(val, dest)
338             }
339
340             _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, dest.layout.ty),
341         }
342     }
343
344     fn unsize_into(
345         &mut self,
346         src: OpTy<'tcx, M::PointerTag>,
347         dest: PlaceTy<'tcx, M::PointerTag>,
348     ) -> EvalResult<'tcx> {
349         match (&src.layout.ty.sty, &dest.layout.ty.sty) {
350             (&ty::Ref(_, s, _), &ty::Ref(_, d, _)) |
351             (&ty::Ref(_, s, _), &ty::RawPtr(TypeAndMut { ty: d, .. })) |
352             (&ty::RawPtr(TypeAndMut { ty: s, .. }),
353              &ty::RawPtr(TypeAndMut { ty: d, .. })) => {
354                 self.unsize_into_ptr(src, dest, s, d)
355             }
356             (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
357                 assert_eq!(def_a, def_b);
358                 if def_a.is_box() || def_b.is_box() {
359                     if !def_a.is_box() || !def_b.is_box() {
360                         bug!("invalid unsizing between {:?} -> {:?}", src.layout, dest.layout);
361                     }
362                     return self.unsize_into_ptr(
363                         src,
364                         dest,
365                         src.layout.ty.boxed_ty(),
366                         dest.layout.ty.boxed_ty(),
367                     );
368                 }
369
370                 // unsizing of generic struct with pointer fields
371                 // Example: `Arc<T>` -> `Arc<Trait>`
372                 // here we need to increase the size of every &T thin ptr field to a fat ptr
373                 for i in 0..src.layout.fields.count() {
374                     let dst_field = self.place_field(dest, i as u64)?;
375                     if dst_field.layout.is_zst() {
376                         continue;
377                     }
378                     let src_field = match src.try_as_mplace() {
379                         Ok(mplace) => {
380                             let src_field = self.mplace_field(mplace, i as u64)?;
381                             src_field.into()
382                         }
383                         Err(..) => {
384                             let src_field_layout = src.layout.field(self, i)?;
385                             // this must be a field covering the entire thing
386                             assert_eq!(src.layout.fields.offset(i).bytes(), 0);
387                             assert_eq!(src_field_layout.size, src.layout.size);
388                             // just sawp out the layout
389                             OpTy { op: src.op, layout: src_field_layout }
390                         }
391                     };
392                     if src_field.layout.ty == dst_field.layout.ty {
393                         self.copy_op(src_field, dst_field)?;
394                     } else {
395                         self.unsize_into(src_field, dst_field)?;
396                     }
397                 }
398                 Ok(())
399             }
400             _ => {
401                 bug!(
402                     "unsize_into: invalid conversion: {:?} -> {:?}",
403                     src.layout,
404                     dest.layout
405                 )
406             }
407         }
408     }
409 }