]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/place.rs
Nuke the entire ctfe from orbit, it's the only way to be sure
[rust.git] / src / librustc_mir / interpret / place.rs
1 use rustc::mir;
2 use rustc::ty::{self, Ty};
3 use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
4 use rustc_data_structures::indexed_vec::Idx;
5
6 use rustc::mir::interpret::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer};
7 use super::{EvalContext, Machine, ValTy};
8 use interpret::memory::HasMemory;
9
10 #[derive(Copy, Clone, Debug)]
11 pub enum Place {
12     /// A place referring to a value allocated in the `Memory` system.
13     Ptr {
14         /// A place may have an invalid (integral or undef) pointer,
15         /// since it might be turned back into a reference
16         /// before ever being dereferenced.
17         ptr: Pointer,
18         align: Align,
19         extra: PlaceExtra,
20     },
21
22     /// A place referring to a value on the stack. Represented by a stack frame index paired with
23     /// a Mir local index.
24     Local { frame: usize, local: mir::Local },
25 }
26
27 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
28 pub enum PlaceExtra {
29     None,
30     Length(u64),
31     Vtable(MemoryPointer),
32     DowncastVariant(usize),
33 }
34
35 impl<'tcx> Place {
36     /// Produces a Place that will error if attempted to be read from
37     pub fn undef() -> Self {
38         Self::from_primval_ptr(PrimVal::Undef.into(), Align::from_bytes(1, 1).unwrap())
39     }
40
41     pub fn from_primval_ptr(ptr: Pointer, align: Align) -> Self {
42         Place::Ptr {
43             ptr,
44             align,
45             extra: PlaceExtra::None,
46         }
47     }
48
49     pub fn from_ptr(ptr: MemoryPointer, align: Align) -> Self {
50         Self::from_primval_ptr(ptr.into(), align)
51     }
52
53     pub fn to_ptr_align_extra(self) -> (Pointer, Align, PlaceExtra) {
54         match self {
55             Place::Ptr { ptr, align, extra } => (ptr, align, extra),
56             _ => bug!("to_ptr_and_extra: expected Place::Ptr, got {:?}", self),
57
58         }
59     }
60
61     pub fn to_ptr_align(self) -> (Pointer, Align) {
62         let (ptr, align, _extra) = self.to_ptr_align_extra();
63         (ptr, align)
64     }
65
66     pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> {
67         // At this point, we forget about the alignment information -- the place has been turned into a reference,
68         // and no matter where it came from, it now must be aligned.
69         self.to_ptr_align().0.to_ptr()
70     }
71
72     pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
73         match ty.sty {
74             ty::TyArray(elem, n) => (elem, n.val.unwrap_u64() as u64),
75
76             ty::TySlice(elem) => {
77                 match self {
78                     Place::Ptr { extra: PlaceExtra::Length(len), .. } => (elem, len),
79                     _ => {
80                         bug!(
81                             "elem_ty_and_len of a TySlice given non-slice place: {:?}",
82                             self
83                         )
84                     }
85                 }
86             }
87
88             _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
89         }
90     }
91 }
92
93 impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
94     /// Reads a value from the place without going through the intermediate step of obtaining
95     /// a `miri::Place`
96     pub fn try_read_place(
97         &mut self,
98         place: &mir::Place<'tcx>,
99     ) -> EvalResult<'tcx, Option<Value>> {
100         use rustc::mir::Place::*;
101         match *place {
102             // Might allow this in the future, right now there's no way to do this from Rust code anyway
103             Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer),
104             // Directly reading a local will always succeed
105             Local(local) => self.frame().get_local(local).map(Some),
106             // Directly reading a static will always succeed
107             Static(ref static_) => {
108                 let instance = ty::Instance::mono(self.tcx, static_.def_id);
109                 Ok(Some(self.read_global_as_value(GlobalId {
110                     instance,
111                     promoted: None,
112                 }, self.layout_of(self.place_ty(place))?)))
113             }
114             Projection(ref proj) => self.try_read_place_projection(proj),
115         }
116     }
117
118     pub fn read_field(
119         &self,
120         base: Value,
121         variant: Option<usize>,
122         field: mir::Field,
123         base_ty: Ty<'tcx>,
124     ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> {
125         let mut base_layout = self.layout_of(base_ty)?;
126         if let Some(variant_index) = variant {
127             base_layout = base_layout.for_variant(self, variant_index);
128         }
129         let field_index = field.index();
130         let field = base_layout.field(self, field_index)?;
131         let offset = base_layout.fields.offset(field_index);
132         match base {
133             // the field covers the entire type
134             Value::ByValPair(..) |
135             Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some((base, field.ty))),
136             // split fat pointers, 2 element tuples, ...
137             Value::ByValPair(a, b) if base_layout.fields.count() == 2 => {
138                 let val = [a, b][field_index];
139                 Ok(Some((Value::ByVal(val), field.ty)))
140             },
141             _ => Ok(None),
142         }
143     }
144
145     fn try_read_place_projection(
146         &mut self,
147         proj: &mir::PlaceProjection<'tcx>,
148     ) -> EvalResult<'tcx, Option<Value>> {
149         use rustc::mir::ProjectionElem::*;
150         let base = match self.try_read_place(&proj.base)? {
151             Some(base) => base,
152             None => return Ok(None),
153         };
154         let base_ty = self.place_ty(&proj.base);
155         match proj.elem {
156             Field(field, _) => Ok(self.read_field(base, None, field, base_ty)?.map(|(f, _)| f)),
157             // The NullablePointer cases should work fine, need to take care for normal enums
158             Downcast(..) |
159             Subslice { .. } |
160             // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
161             ConstantIndex { .. } | Index(_) |
162             // No way to optimize this projection any better than the normal place path
163             Deref => Ok(None),
164         }
165     }
166
167     /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
168     pub(super) fn eval_and_read_place(
169         &mut self,
170         place: &mir::Place<'tcx>,
171     ) -> EvalResult<'tcx, Value> {
172         // Shortcut for things like accessing a fat pointer's field,
173         // which would otherwise (in the `eval_place` path) require moving a `ByValPair` to memory
174         // and returning an `Place::Ptr` to it
175         if let Some(val) = self.try_read_place(place)? {
176             return Ok(val);
177         }
178         let place = self.eval_place(place)?;
179         self.read_place(place)
180     }
181
182     pub fn read_place(&self, place: Place) -> EvalResult<'tcx, Value> {
183         match place {
184             Place::Ptr { ptr, align, extra } => {
185                 assert_eq!(extra, PlaceExtra::None);
186                 Ok(Value::ByRef(ptr, align))
187             }
188             Place::Local { frame, local } => self.stack[frame].get_local(local),
189         }
190     }
191
192     pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> {
193         use rustc::mir::Place::*;
194         let place = match *mir_place {
195             Local(mir::RETURN_PLACE) => self.frame().return_place,
196             Local(local) => Place::Local {
197                 frame: self.cur_frame(),
198                 local,
199             },
200
201             Static(ref static_) => {
202                 let instance = ty::Instance::mono(self.tcx, static_.def_id);
203                 let gid = GlobalId {
204                     instance,
205                     promoted: None,
206                 };
207                 let layout = self.layout_of(self.place_ty(mir_place))?;
208                 let alloc = self.tcx.interpret_interner.borrow().get_cached(gid).expect("uncached global");
209                 Place::Ptr {
210                     ptr: MemoryPointer::new(alloc, 0).into(),
211                     align: layout.align,
212                     extra: PlaceExtra::None,
213                 }
214             }
215
216             Projection(ref proj) => {
217                 let ty = self.place_ty(&proj.base);
218                 let place = self.eval_place(&proj.base)?;
219                 return self.eval_place_projection(place, ty, &proj.elem);
220             }
221         };
222
223         if log_enabled!(::log::Level::Trace) {
224             self.dump_local(place);
225         }
226
227         Ok(place)
228     }
229
230     pub fn place_field(
231         &mut self,
232         base: Place,
233         field: mir::Field,
234         mut base_layout: TyLayout<'tcx>,
235     ) -> EvalResult<'tcx, (Place, TyLayout<'tcx>)> {
236         match base {
237             Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => {
238                 base_layout = base_layout.for_variant(&self, variant_index);
239             }
240             _ => {}
241         }
242         let field_index = field.index();
243         let field = base_layout.field(&self, field_index)?;
244         let offset = base_layout.fields.offset(field_index);
245
246         // Do not allocate in trivial cases
247         let (base_ptr, base_align, base_extra) = match base {
248             Place::Ptr { ptr, align, extra } => (ptr, align, extra),
249             Place::Local { frame, local } => {
250                 match (&self.stack[frame].get_local(local)?, &base_layout.abi) {
251                     // in case the field covers the entire type, just return the value
252                     (&Value::ByVal(_), &layout::Abi::Scalar(_)) |
253                     (&Value::ByValPair(..), &layout::Abi::ScalarPair(..))
254                         if offset.bytes() == 0 && field.size == base_layout.size =>
255                     {
256                         return Ok((base, field));
257                     }
258                     _ => self.force_allocation(base)?.to_ptr_align_extra(),
259                 }
260             }
261         };
262
263         let offset = match base_extra {
264             PlaceExtra::Vtable(tab) => {
265                 let (_, align) = self.size_and_align_of_dst(
266                     base_layout.ty,
267                     base_ptr.to_value_with_vtable(tab),
268                 )?;
269                 offset.abi_align(align).bytes()
270             }
271             _ => offset.bytes(),
272         };
273
274         let ptr = base_ptr.offset(offset, &self)?;
275         let align = base_align.min(base_layout.align).min(field.align);
276         let extra = if !field.is_unsized() {
277             PlaceExtra::None
278         } else {
279             match base_extra {
280                 PlaceExtra::None => bug!("expected fat pointer"),
281                 PlaceExtra::DowncastVariant(..) => {
282                     bug!("Rust doesn't support unsized fields in enum variants")
283                 }
284                 PlaceExtra::Vtable(_) |
285                 PlaceExtra::Length(_) => {}
286             }
287             base_extra
288         };
289
290         Ok((Place::Ptr { ptr, align, extra }, field))
291     }
292
293     pub fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> {
294         let layout = self.layout_of(ty)?;
295         Ok(match self.tcx.struct_tail(ty).sty {
296             ty::TyDynamic(..) => {
297                 let (ptr, vtable) = self.into_ptr_vtable_pair(val)?;
298                 Place::Ptr {
299                     ptr,
300                     align: layout.align,
301                     extra: PlaceExtra::Vtable(vtable),
302                 }
303             }
304             ty::TyStr | ty::TySlice(_) => {
305                 let (ptr, len) = self.into_slice(val)?;
306                 Place::Ptr {
307                     ptr,
308                     align: layout.align,
309                     extra: PlaceExtra::Length(len),
310                 }
311             }
312             _ => Place::from_primval_ptr(self.into_ptr(val)?, layout.align),
313         })
314     }
315
316     pub fn place_index(
317         &mut self,
318         base: Place,
319         outer_ty: Ty<'tcx>,
320         n: u64,
321     ) -> EvalResult<'tcx, Place> {
322         // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
323         let base = self.force_allocation(base)?;
324         let (base_ptr, align) = base.to_ptr_align();
325
326         let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
327         let elem_size = self.layout_of(elem_ty)?.size.bytes();
328         assert!(
329             n < len,
330             "Tried to access element {} of array/slice with length {}",
331             n,
332             len
333         );
334         let ptr = base_ptr.offset(n * elem_size, &*self)?;
335         Ok(Place::Ptr {
336             ptr,
337             align,
338             extra: PlaceExtra::None,
339         })
340     }
341
342     pub(super) fn place_downcast(
343         &mut self,
344         base: Place,
345         variant: usize,
346     ) -> EvalResult<'tcx, Place> {
347         // FIXME(solson)
348         let base = self.force_allocation(base)?;
349         let (ptr, align) = base.to_ptr_align();
350         let extra = PlaceExtra::DowncastVariant(variant);
351         Ok(Place::Ptr { ptr, align, extra })
352     }
353
354     pub fn eval_place_projection(
355         &mut self,
356         base: Place,
357         base_ty: Ty<'tcx>,
358         proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
359     ) -> EvalResult<'tcx, Place> {
360         use rustc::mir::ProjectionElem::*;
361         match *proj_elem {
362             Field(field, _) => {
363                 let layout = self.layout_of(base_ty)?;
364                 Ok(self.place_field(base, field, layout)?.0)
365             }
366
367             Downcast(_, variant) => {
368                 self.place_downcast(base, variant)
369             }
370
371             Deref => {
372                 let val = self.read_place(base)?;
373
374                 let pointee_type = match base_ty.sty {
375                     ty::TyRawPtr(ref tam) |
376                     ty::TyRef(_, ref tam) => tam.ty,
377                     ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
378                     _ => bug!("can only deref pointer types"),
379                 };
380
381                 trace!("deref to {} on {:?}", pointee_type, val);
382
383                 self.val_to_place(val, pointee_type)
384             }
385
386             Index(local) => {
387                 let value = self.frame().get_local(local)?;
388                 let ty = self.tcx.types.usize;
389                 let n = self.value_to_primval(ValTy { value, ty })?.to_u64()?;
390                 self.place_index(base, base_ty, n)
391             }
392
393             ConstantIndex {
394                 offset,
395                 min_length,
396                 from_end,
397             } => {
398                 // FIXME(solson)
399                 let base = self.force_allocation(base)?;
400                 let (base_ptr, align) = base.to_ptr_align();
401
402                 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
403                 let elem_size = self.layout_of(elem_ty)?.size.bytes();
404                 assert!(n >= min_length as u64);
405
406                 let index = if from_end {
407                     n - u64::from(offset)
408                 } else {
409                     u64::from(offset)
410                 };
411
412                 let ptr = base_ptr.offset(index * elem_size, &self)?;
413                 Ok(Place::Ptr { ptr, align, extra: PlaceExtra::None })
414             }
415
416             Subslice { from, to } => {
417                 // FIXME(solson)
418                 let base = self.force_allocation(base)?;
419                 let (base_ptr, align) = base.to_ptr_align();
420
421                 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
422                 let elem_size = self.layout_of(elem_ty)?.size.bytes();
423                 assert!(u64::from(from) <= n - u64::from(to));
424                 let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?;
425                 // sublicing arrays produces arrays
426                 let extra = if self.type_is_sized(base_ty) {
427                     PlaceExtra::None
428                 } else {
429                     PlaceExtra::Length(n - u64::from(to) - u64::from(from))
430                 };
431                 Ok(Place::Ptr { ptr, align, extra })
432             }
433         }
434     }
435
436     pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> {
437         self.monomorphize(
438             place.ty(self.mir(), self.tcx).to_ty(self.tcx),
439             self.substs(),
440         )
441     }
442 }