]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/cast.rs
Auto merge of #52712 - oli-obk:const_eval_cleanups, r=RalfJung
[rust.git] / src / librustc_mir / interpret / cast.rs
1 use rustc::ty::{self, Ty};
2 use rustc::ty::layout::{self, LayoutOf, TyLayout};
3 use syntax::ast::{FloatTy, IntTy, UintTy};
4
5 use rustc_apfloat::ieee::{Single, Double};
6 use super::{EvalContext, Machine};
7 use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic, Value, EvalErrorKind};
8 use rustc::mir::CastKind;
9 use rustc_apfloat::Float;
10 use interpret::eval_context::ValTy;
11 use interpret::Place;
12
13 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
14     crate fn cast(
15         &mut self,
16         src: ValTy<'tcx>,
17         kind: CastKind,
18         dest_ty: Ty<'tcx>,
19         dest: Place,
20     ) -> EvalResult<'tcx> {
21         let src_layout = self.layout_of(src.ty)?;
22         let dst_layout = self.layout_of(dest_ty)?;
23         use rustc::mir::CastKind::*;
24         match kind {
25             Unsize => {
26                 self.unsize_into(src.value, src_layout, dest, dst_layout)?;
27             }
28
29             Misc => {
30                 if self.type_is_fat_ptr(src.ty) {
31                     match (src.value, self.type_is_fat_ptr(dest_ty)) {
32                         (Value::ByRef { .. }, _) |
33                         // pointers to extern types
34                         (Value::Scalar(_),_) |
35                         // slices and trait objects to other slices/trait objects
36                         (Value::ScalarPair(..), true) => {
37                             let valty = ValTy {
38                                 value: src.value,
39                                 ty: dest_ty,
40                             };
41                             self.write_value(valty, dest)?;
42                         }
43                         // slices and trait objects to thin pointers (dropping the metadata)
44                         (Value::ScalarPair(data, _), false) => {
45                             let valty = ValTy {
46                                 value: Value::Scalar(data),
47                                 ty: dest_ty,
48                             };
49                             self.write_value(valty, dest)?;
50                         }
51                     }
52                 } else {
53                     let src_layout = self.layout_of(src.ty)?;
54                     match src_layout.variants {
55                         layout::Variants::Single { index } => {
56                             if let Some(def) = src.ty.ty_adt_def() {
57                                 let discr_val = def
58                                     .discriminant_for_variant(*self.tcx, index)
59                                     .val;
60                                 return self.write_scalar(
61                                     dest,
62                                     Scalar::Bits {
63                                         bits: discr_val,
64                                         size: dst_layout.size.bytes() as u8,
65                                     },
66                                     dest_ty);
67                             }
68                         }
69                         layout::Variants::Tagged { .. } |
70                         layout::Variants::NicheFilling { .. } => {},
71                     }
72
73                     let src_val = self.value_to_scalar(src)?;
74                     let dest_val = self.cast_scalar(src_val, src_layout, dst_layout)?;
75                     let valty = ValTy {
76                         value: Value::Scalar(dest_val.into()),
77                         ty: dest_ty,
78                     };
79                     self.write_value(valty, dest)?;
80                 }
81             }
82
83             ReifyFnPointer => {
84                 match src.ty.sty {
85                     ty::TyFnDef(def_id, substs) => {
86                         if self.tcx.has_attr(def_id, "rustc_args_required_const") {
87                             bug!("reifying a fn ptr that requires \
88                                     const arguments");
89                         }
90                         let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
91                             *self.tcx,
92                             self.param_env,
93                             def_id,
94                             substs,
95                         ).ok_or_else(|| EvalErrorKind::TooGeneric.into());
96                         let fn_ptr = self.memory.create_fn_alloc(instance?);
97                         let valty = ValTy {
98                             value: Value::Scalar(Scalar::Ptr(fn_ptr.into()).into()),
99                             ty: dest_ty,
100                         };
101                         self.write_value(valty, dest)?;
102                     }
103                     ref other => bug!("reify fn pointer on {:?}", other),
104                 }
105             }
106
107             UnsafeFnPointer => {
108                 match dest_ty.sty {
109                     ty::TyFnPtr(_) => {
110                         let mut src = src;
111                         src.ty = dest_ty;
112                         self.write_value(src, dest)?;
113                     }
114                     ref other => bug!("fn to unsafe fn cast on {:?}", other),
115                 }
116             }
117
118             ClosureFnPointer => {
119                 match src.ty.sty {
120                     ty::TyClosure(def_id, substs) => {
121                         let substs = self.tcx.subst_and_normalize_erasing_regions(
122                             self.substs(),
123                             ty::ParamEnv::reveal_all(),
124                             &substs,
125                         );
126                         let instance = ty::Instance::resolve_closure(
127                             *self.tcx,
128                             def_id,
129                             substs,
130                             ty::ClosureKind::FnOnce,
131                         );
132                         let fn_ptr = self.memory.create_fn_alloc(instance);
133                         let valty = ValTy {
134                             value: Value::Scalar(Scalar::Ptr(fn_ptr.into()).into()),
135                             ty: dest_ty,
136                         };
137                         self.write_value(valty, dest)?;
138                     }
139                     ref other => bug!("closure fn pointer on {:?}", other),
140                 }
141             }
142         }
143         Ok(())
144     }
145
146     pub(super) fn cast_scalar(
147         &self,
148         val: Scalar,
149         src_layout: TyLayout<'tcx>,
150         dest_layout: TyLayout<'tcx>,
151     ) -> EvalResult<'tcx, Scalar> {
152         use rustc::ty::TypeVariants::*;
153         trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);
154
155         match val {
156             Scalar::Ptr(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
157             Scalar::Bits { bits, size } => {
158                 assert_eq!(size as u64, src_layout.size.bytes());
159                 match src_layout.ty.sty {
160                     TyFloat(fty) => self.cast_from_float(bits, fty, dest_layout.ty),
161                     _ => self.cast_from_int(bits, src_layout, dest_layout),
162                 }
163             }
164         }
165     }
166
167     fn cast_from_int(
168         &self,
169         v: u128,
170         src_layout: TyLayout<'tcx>,
171         dest_layout: TyLayout<'tcx>,
172     ) -> EvalResult<'tcx, Scalar> {
173         let signed = src_layout.abi.is_signed();
174         let v = if signed {
175             self.sign_extend(v, src_layout)
176         } else {
177             v
178         };
179         trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty);
180         use rustc::ty::TypeVariants::*;
181         match dest_layout.ty.sty {
182             TyInt(_) | TyUint(_) => {
183                 let v = self.truncate(v, dest_layout);
184                 Ok(Scalar::Bits {
185                     bits: v,
186                     size: dest_layout.size.bytes() as u8,
187                 })
188             }
189
190             TyFloat(FloatTy::F32) if signed => Ok(Scalar::Bits {
191                 bits: Single::from_i128(v as i128).value.to_bits(),
192                 size: 4,
193             }),
194             TyFloat(FloatTy::F64) if signed => Ok(Scalar::Bits {
195                 bits: Double::from_i128(v as i128).value.to_bits(),
196                 size: 8,
197             }),
198             TyFloat(FloatTy::F32) => Ok(Scalar::Bits {
199                 bits: Single::from_u128(v).value.to_bits(),
200                 size: 4,
201             }),
202             TyFloat(FloatTy::F64) => Ok(Scalar::Bits {
203                 bits: Double::from_u128(v).value.to_bits(),
204                 size: 8,
205             }),
206
207             TyChar => {
208                 assert_eq!(v as u8 as u128, v);
209                 Ok(Scalar::Bits { bits: v, size: 4 })
210             },
211
212             // No alignment check needed for raw pointers.  But we have to truncate to target ptr size.
213             TyRawPtr(_) => {
214                 Ok(Scalar::Bits {
215                     bits: self.memory.truncate_to_ptr(v).0 as u128,
216                     size: self.memory.pointer_size().bytes() as u8,
217                 })
218             },
219
220             // Casts to bool are not permitted by rustc, no need to handle them here.
221             _ => err!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))),
222         }
223     }
224
225     fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, Scalar> {
226         use rustc::ty::TypeVariants::*;
227         use rustc_apfloat::FloatConvert;
228         match dest_ty.sty {
229             // float -> uint
230             TyUint(t) => {
231                 let width = t.bit_width().unwrap_or(self.memory.pointer_size().bits() as usize);
232                 match fty {
233                     FloatTy::F32 => Ok(Scalar::Bits {
234                         bits: Single::from_bits(bits).to_u128(width).value,
235                         size: (width / 8) as u8,
236                     }),
237                     FloatTy::F64 => Ok(Scalar::Bits {
238                         bits: Double::from_bits(bits).to_u128(width).value,
239                         size: (width / 8) as u8,
240                     }),
241                 }
242             },
243             // float -> int
244             TyInt(t) => {
245                 let width = t.bit_width().unwrap_or(self.memory.pointer_size().bits() as usize);
246                 match fty {
247                     FloatTy::F32 => Ok(Scalar::Bits {
248                         bits: Single::from_bits(bits).to_i128(width).value as u128,
249                         size: (width / 8) as u8,
250                     }),
251                     FloatTy::F64 => Ok(Scalar::Bits {
252                         bits: Double::from_bits(bits).to_i128(width).value as u128,
253                         size: (width / 8) as u8,
254                     }),
255                 }
256             },
257             // f64 -> f32
258             TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
259                 Ok(Scalar::Bits {
260                     bits: Single::to_bits(Double::from_bits(bits).convert(&mut false).value),
261                     size: 4,
262                 })
263             },
264             // f32 -> f64
265             TyFloat(FloatTy::F64) if fty == FloatTy::F32 => {
266                 Ok(Scalar::Bits {
267                     bits: Double::to_bits(Single::from_bits(bits).convert(&mut false).value),
268                     size: 8,
269                 })
270             },
271             // identity cast
272             TyFloat(FloatTy:: F64) => Ok(Scalar::Bits {
273                 bits,
274                 size: 8,
275             }),
276             TyFloat(FloatTy:: F32) => Ok(Scalar::Bits {
277                 bits,
278                 size: 4,
279             }),
280             _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
281         }
282     }
283
284     fn cast_from_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Scalar> {
285         use rustc::ty::TypeVariants::*;
286         match ty.sty {
287             // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
288             TyRawPtr(_) |
289             TyInt(IntTy::Isize) |
290             TyUint(UintTy::Usize) => Ok(ptr.into()),
291             TyInt(_) | TyUint(_) => err!(ReadPointerAsBytes),
292             _ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
293         }
294     }
295 }