]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/place.rs
preserve order if blocks are between items
[rust.git] / src / librustc_mir / interpret / place.rs
1 use rustc::mir;
2 use rustc::ty::{self, Ty, TyCtxt};
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, Scalar, EvalResult, Pointer};
7 use super::{EvalContext, Machine, ValTy};
8 use interpret::memory::HasMemory;
9
10 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
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: Scalar,
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, Hash, PartialEq, Eq)]
28 pub enum PlaceExtra {
29     None,
30     Length(u64),
31     Vtable(Pointer),
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_scalar_ptr(Scalar::undef().into(), Align::from_bytes(1, 1).unwrap())
39     }
40
41     pub fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self {
42         Place::Ptr {
43             ptr,
44             align,
45             extra: PlaceExtra::None,
46         }
47     }
48
49     pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
50         Self::from_scalar_ptr(ptr.into(), align)
51     }
52
53     pub fn to_ptr_align_extra(self) -> (Scalar, 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) -> (Scalar, Align) {
62         let (ptr, align, _extra) = self.to_ptr_align_extra();
63         (ptr, align)
64     }
65
66     pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
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(
73         self,
74         ty: Ty<'tcx>,
75         tcx: TyCtxt<'_, 'tcx, '_>
76     ) -> (Ty<'tcx>, u64) {
77         match ty.sty {
78             ty::TyArray(elem, n) => (elem, n.unwrap_usize(tcx)),
79
80             ty::TySlice(elem) => {
81                 match self {
82                     Place::Ptr { extra: PlaceExtra::Length(len), .. } => (elem, len),
83                     _ => {
84                         bug!(
85                             "elem_ty_and_len of a TySlice given non-slice place: {:?}",
86                             self
87                         )
88                     }
89                 }
90             }
91
92             _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
93         }
94     }
95 }
96
97 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
98     /// Reads a value from the place without going through the intermediate step of obtaining
99     /// a `miri::Place`
100     pub fn try_read_place(
101         &self,
102         place: &mir::Place<'tcx>,
103     ) -> EvalResult<'tcx, Option<Value>> {
104         use rustc::mir::Place::*;
105         match *place {
106             // Might allow this in the future, right now there's no way to do this from Rust code anyway
107             Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer),
108             // Directly reading a local will always succeed
109             Local(local) => self.frame().get_local(local).map(Some),
110             // No fast path for statics. Reading from statics is rare and would require another
111             // Machine function to handle differently in miri.
112             Promoted(_) |
113             Static(_) => Ok(None),
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         mut base_layout: TyLayout<'tcx>,
124     ) -> EvalResult<'tcx, (Value, TyLayout<'tcx>)> {
125         if let Some(variant_index) = variant {
126             base_layout = base_layout.for_variant(self, variant_index);
127         }
128         let field_index = field.index();
129         let field = base_layout.field(self, field_index)?;
130         if field.size.bytes() == 0 {
131             return Ok((
132                 Value::Scalar(Scalar::undef()),
133                 field,
134             ));
135         }
136         let offset = base_layout.fields.offset(field_index);
137         let value = match base {
138             // the field covers the entire type
139             Value::ScalarPair(..) |
140             Value::Scalar(_) if offset.bytes() == 0 && field.size == base_layout.size => base,
141             // extract fields from types with `ScalarPair` ABI
142             Value::ScalarPair(a, b) => {
143                 let val = if offset.bytes() == 0 { a } else { b };
144                 Value::Scalar(val)
145             },
146             Value::ByRef(base_ptr, align) => {
147                 let offset = base_layout.fields.offset(field_index);
148                 let ptr = base_ptr.ptr_offset(offset, self)?;
149                 let align = align.min(base_layout.align).min(field.align);
150                 assert!(!field.is_unsized());
151                 Value::ByRef(ptr, align)
152             },
153             Value::Scalar(val) => bug!("field access on non aggregate {:#?}, {:#?}", val, base_layout),
154         };
155         Ok((value, field))
156     }
157
158     fn try_read_place_projection(
159         &self,
160         proj: &mir::PlaceProjection<'tcx>,
161     ) -> EvalResult<'tcx, Option<Value>> {
162         use rustc::mir::ProjectionElem::*;
163         let base = match self.try_read_place(&proj.base)? {
164             Some(base) => base,
165             None => return Ok(None),
166         };
167         let base_ty = self.place_ty(&proj.base);
168         let base_layout = self.layout_of(base_ty)?;
169         match proj.elem {
170             Field(field, _) => Ok(Some(self.read_field(base, None, field, base_layout)?.0)),
171             // The NullablePointer cases should work fine, need to take care for normal enums
172             Downcast(..) |
173             Subslice { .. } |
174             // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
175             ConstantIndex { .. } | Index(_) |
176             // No way to optimize this projection any better than the normal place path
177             Deref => Ok(None),
178         }
179     }
180
181     /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
182     pub(super) fn eval_and_read_place(
183         &mut self,
184         place: &mir::Place<'tcx>,
185     ) -> EvalResult<'tcx, Value> {
186         // Shortcut for things like accessing a fat pointer's field,
187         // which would otherwise (in the `eval_place` path) require moving a `ScalarPair` to memory
188         // and returning an `Place::Ptr` to it
189         if let Some(val) = self.try_read_place(place)? {
190             return Ok(val);
191         }
192         let place = self.eval_place(place)?;
193         self.read_place(place)
194     }
195
196     pub fn read_place(&self, place: Place) -> EvalResult<'tcx, Value> {
197         match place {
198             Place::Ptr { ptr, align, extra } => {
199                 assert_eq!(extra, PlaceExtra::None);
200                 Ok(Value::ByRef(ptr, align))
201             }
202             Place::Local { frame, local } => self.stack[frame].get_local(local),
203         }
204     }
205
206     pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> {
207         use rustc::mir::Place::*;
208         let place = match *mir_place {
209             Local(mir::RETURN_PLACE) => self.frame().return_place,
210             Local(local) => Place::Local {
211                 frame: self.cur_frame(),
212                 local,
213             },
214
215             Promoted(ref promoted) => {
216                 let instance = self.frame().instance;
217                 let val = self.read_global_as_value(GlobalId {
218                     instance,
219                     promoted: Some(promoted.0),
220                 })?;
221                 if let Value::ByRef(ptr, align) = val {
222                     Place::Ptr {
223                         ptr,
224                         align,
225                         extra: PlaceExtra::None,
226                     }
227                 } else {
228                     bug!("evaluated promoted and got {:#?}", val);
229                 }
230             }
231
232             Static(ref static_) => {
233                 let layout = self.layout_of(self.place_ty(mir_place))?;
234                 let instance = ty::Instance::mono(*self.tcx, static_.def_id);
235                 let cid = GlobalId {
236                     instance,
237                     promoted: None
238                 };
239                 let alloc = Machine::init_static(self, cid)?;
240                 Place::Ptr {
241                     ptr: Scalar::Ptr(alloc.into()),
242                     align: layout.align,
243                     extra: PlaceExtra::None,
244                 }
245             }
246
247             Projection(ref proj) => {
248                 let ty = self.place_ty(&proj.base);
249                 let place = self.eval_place(&proj.base)?;
250                 return self.eval_place_projection(place, ty, &proj.elem);
251             }
252         };
253
254         self.dump_local(place);
255
256         Ok(place)
257     }
258
259     pub fn place_field(
260         &mut self,
261         base: Place,
262         field: mir::Field,
263         mut base_layout: TyLayout<'tcx>,
264     ) -> EvalResult<'tcx, (Place, TyLayout<'tcx>)> {
265         match base {
266             Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => {
267                 base_layout = base_layout.for_variant(&self, variant_index);
268             }
269             _ => {}
270         }
271         let field_index = field.index();
272         let field = base_layout.field(&self, field_index)?;
273         let offset = base_layout.fields.offset(field_index);
274
275         // Do not allocate in trivial cases
276         let (base_ptr, base_align, base_extra) = match base {
277             Place::Ptr { ptr, align, extra } => (ptr, align, extra),
278             Place::Local { frame, local } => {
279                 match (&self.stack[frame].get_local(local)?, &base_layout.abi) {
280                     // in case the field covers the entire type, just return the value
281                     (&Value::Scalar(_), &layout::Abi::Scalar(_)) |
282                     (&Value::ScalarPair(..), &layout::Abi::ScalarPair(..))
283                         if offset.bytes() == 0 && field.size == base_layout.size =>
284                     {
285                         return Ok((base, field));
286                     }
287                     _ => self.force_allocation(base)?.to_ptr_align_extra(),
288                 }
289             }
290         };
291
292         let offset = match base_extra {
293             PlaceExtra::Vtable(tab) => {
294                 let (_, align) = self.size_and_align_of_dst(
295                     base_layout.ty,
296                     base_ptr.to_value_with_vtable(tab),
297                 )?;
298                 offset.abi_align(align)
299             }
300             _ => offset,
301         };
302
303         let ptr = base_ptr.ptr_offset(offset, &self)?;
304         let align = base_align.min(base_layout.align).min(field.align);
305         let extra = if !field.is_unsized() {
306             PlaceExtra::None
307         } else {
308             match base_extra {
309                 PlaceExtra::None => bug!("expected fat pointer"),
310                 PlaceExtra::DowncastVariant(..) => {
311                     bug!("Rust doesn't support unsized fields in enum variants")
312                 }
313                 PlaceExtra::Vtable(_) |
314                 PlaceExtra::Length(_) => {}
315             }
316             base_extra
317         };
318
319         Ok((Place::Ptr { ptr, align, extra }, field))
320     }
321
322     pub fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> {
323         let layout = self.layout_of(ty)?;
324         Ok(match self.tcx.struct_tail(ty).sty {
325             ty::TyDynamic(..) => {
326                 let (ptr, vtable) = self.into_ptr_vtable_pair(val)?;
327                 Place::Ptr {
328                     ptr,
329                     align: layout.align,
330                     extra: PlaceExtra::Vtable(vtable),
331                 }
332             }
333             ty::TyStr | ty::TySlice(_) => {
334                 let (ptr, len) = self.into_slice(val)?;
335                 Place::Ptr {
336                     ptr,
337                     align: layout.align,
338                     extra: PlaceExtra::Length(len),
339                 }
340             }
341             _ => Place::from_scalar_ptr(self.into_ptr(val)?, layout.align),
342         })
343     }
344
345     pub fn place_index(
346         &mut self,
347         base: Place,
348         outer_ty: Ty<'tcx>,
349         n: u64,
350     ) -> EvalResult<'tcx, Place> {
351         // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
352         let base = self.force_allocation(base)?;
353         let (base_ptr, align) = base.to_ptr_align();
354
355         let (elem_ty, len) = base.elem_ty_and_len(outer_ty, self.tcx.tcx);
356         let elem_size = self.layout_of(elem_ty)?.size;
357         assert!(
358             n < len,
359             "Tried to access element {} of array/slice with length {}",
360             n,
361             len
362         );
363         let ptr = base_ptr.ptr_offset(elem_size * n, &*self)?;
364         Ok(Place::Ptr {
365             ptr,
366             align,
367             extra: PlaceExtra::None,
368         })
369     }
370
371     pub(super) fn place_downcast(
372         &mut self,
373         base: Place,
374         variant: usize,
375     ) -> EvalResult<'tcx, Place> {
376         // FIXME(solson)
377         let base = self.force_allocation(base)?;
378         let (ptr, align) = base.to_ptr_align();
379         let extra = PlaceExtra::DowncastVariant(variant);
380         Ok(Place::Ptr { ptr, align, extra })
381     }
382
383     pub fn eval_place_projection(
384         &mut self,
385         base: Place,
386         base_ty: Ty<'tcx>,
387         proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
388     ) -> EvalResult<'tcx, Place> {
389         use rustc::mir::ProjectionElem::*;
390         match *proj_elem {
391             Field(field, _) => {
392                 let layout = self.layout_of(base_ty)?;
393                 Ok(self.place_field(base, field, layout)?.0)
394             }
395
396             Downcast(_, variant) => {
397                 self.place_downcast(base, variant)
398             }
399
400             Deref => {
401                 let val = self.read_place(base)?;
402
403                 let pointee_type = match base_ty.sty {
404                     ty::TyRawPtr(ref tam) => tam.ty,
405                     ty::TyRef(_, ty, _) => ty,
406                     ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
407                     _ => bug!("can only deref pointer types"),
408                 };
409
410                 trace!("deref to {} on {:?}", pointee_type, val);
411
412                 self.val_to_place(val, pointee_type)
413             }
414
415             Index(local) => {
416                 let value = self.frame().get_local(local)?;
417                 let ty = self.tcx.types.usize;
418                 let n = self
419                     .value_to_scalar(ValTy { value, ty })?
420                     .to_bits(self.tcx.data_layout.pointer_size)?;
421                 self.place_index(base, base_ty, n as u64)
422             }
423
424             ConstantIndex {
425                 offset,
426                 min_length,
427                 from_end,
428             } => {
429                 // FIXME(solson)
430                 let base = self.force_allocation(base)?;
431                 let (base_ptr, align) = base.to_ptr_align();
432
433                 let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx);
434                 let elem_size = self.layout_of(elem_ty)?.size;
435                 assert!(n >= min_length as u64);
436
437                 let index = if from_end {
438                     n - u64::from(offset)
439                 } else {
440                     u64::from(offset)
441                 };
442
443                 let ptr = base_ptr.ptr_offset(elem_size * index, &self)?;
444                 Ok(Place::Ptr { ptr, align, extra: PlaceExtra::None })
445             }
446
447             Subslice { from, to } => {
448                 // FIXME(solson)
449                 let base = self.force_allocation(base)?;
450                 let (base_ptr, align) = base.to_ptr_align();
451
452                 let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx);
453                 let elem_size = self.layout_of(elem_ty)?.size;
454                 assert!(u64::from(from) <= n - u64::from(to));
455                 let ptr = base_ptr.ptr_offset(elem_size * u64::from(from), &self)?;
456                 // sublicing arrays produces arrays
457                 let extra = if self.type_is_sized(base_ty) {
458                     PlaceExtra::None
459                 } else {
460                     PlaceExtra::Length(n - u64::from(to) - u64::from(from))
461                 };
462                 Ok(Place::Ptr { ptr, align, extra })
463             }
464         }
465     }
466
467     pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> {
468         self.monomorphize(
469             place.ty(self.mir(), *self.tcx).to_ty(*self.tcx),
470             self.substs(),
471         )
472     }
473 }