]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/place.rs
Reintroduce `Undef` and properly check constant value sizes
[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, ScalarMaybeUndef};
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: ScalarMaybeUndef,
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(ScalarMaybeUndef::Undef, Align::from_bytes(1, 1).unwrap())
39     }
40
41     pub fn from_scalar_ptr(ptr: ScalarMaybeUndef, 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(ScalarMaybeUndef::Scalar(ptr.into()), align)
51     }
52
53     pub fn to_ptr_align_extra(self) -> (ScalarMaybeUndef, 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) -> (ScalarMaybeUndef, 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().locals[local].access().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(ScalarMaybeUndef::Scalar(Scalar::Bits { bits: 0, size: 0 })),
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.read()?, align))
201             }
202             Place::Local { frame, local } => self.stack[frame].locals[local].access(),
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: ptr.into(),
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: ScalarMaybeUndef::Scalar(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].locals[local].access()?, &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                         return Ok((base, field))
285                     },
286                     _ => self.force_allocation(base)?.to_ptr_align_extra(),
287                 }
288             }
289         };
290
291         let offset = match base_extra {
292             PlaceExtra::Vtable(tab) => {
293                 let (_, align) = self.size_and_align_of_dst(
294                     base_layout.ty,
295                     base_ptr.to_value_with_vtable(tab),
296                 )?;
297                 offset.abi_align(align)
298             }
299             _ => offset,
300         };
301
302         let ptr = base_ptr.ptr_offset(offset, &self)?;
303         let align = base_align.min(base_layout.align).min(field.align);
304         let extra = if !field.is_unsized() {
305             PlaceExtra::None
306         } else {
307             match base_extra {
308                 PlaceExtra::None => bug!("expected fat pointer"),
309                 PlaceExtra::DowncastVariant(..) => {
310                     bug!("Rust doesn't support unsized fields in enum variants")
311                 }
312                 PlaceExtra::Vtable(_) |
313                 PlaceExtra::Length(_) => {}
314             }
315             base_extra
316         };
317
318         Ok((Place::Ptr { ptr, align, extra }, field))
319     }
320
321     pub fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> {
322         let layout = self.layout_of(ty)?;
323         Ok(match self.tcx.struct_tail(ty).sty {
324             ty::TyDynamic(..) => {
325                 let (ptr, vtable) = self.into_ptr_vtable_pair(val)?;
326                 Place::Ptr {
327                     ptr,
328                     align: layout.align,
329                     extra: PlaceExtra::Vtable(vtable),
330                 }
331             }
332             ty::TyStr | ty::TySlice(_) => {
333                 let (ptr, len) = self.into_slice(val)?;
334                 Place::Ptr {
335                     ptr,
336                     align: layout.align,
337                     extra: PlaceExtra::Length(len),
338                 }
339             }
340             _ => Place::from_scalar_ptr(self.into_ptr(val)?, layout.align),
341         })
342     }
343
344     pub fn place_index(
345         &mut self,
346         base: Place,
347         outer_ty: Ty<'tcx>,
348         n: u64,
349     ) -> EvalResult<'tcx, Place> {
350         // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
351         let base = self.force_allocation(base)?;
352         let (base_ptr, align) = base.to_ptr_align();
353
354         let (elem_ty, len) = base.elem_ty_and_len(outer_ty, self.tcx.tcx);
355         let elem_size = self.layout_of(elem_ty)?.size;
356         assert!(
357             n < len,
358             "Tried to access element {} of array/slice with length {}",
359             n,
360             len
361         );
362         let ptr = base_ptr.ptr_offset(elem_size * n, &*self)?;
363         Ok(Place::Ptr {
364             ptr,
365             align,
366             extra: PlaceExtra::None,
367         })
368     }
369
370     pub(super) fn place_downcast(
371         &mut self,
372         base: Place,
373         variant: usize,
374     ) -> EvalResult<'tcx, Place> {
375         // FIXME(solson)
376         let base = self.force_allocation(base)?;
377         let (ptr, align) = base.to_ptr_align();
378         let extra = PlaceExtra::DowncastVariant(variant);
379         Ok(Place::Ptr { ptr, align, extra })
380     }
381
382     pub fn eval_place_projection(
383         &mut self,
384         base: Place,
385         base_ty: Ty<'tcx>,
386         proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
387     ) -> EvalResult<'tcx, Place> {
388         use rustc::mir::ProjectionElem::*;
389         match *proj_elem {
390             Field(field, _) => {
391                 let layout = self.layout_of(base_ty)?;
392                 Ok(self.place_field(base, field, layout)?.0)
393             }
394
395             Downcast(_, variant) => {
396                 self.place_downcast(base, variant)
397             }
398
399             Deref => {
400                 let val = self.read_place(base)?;
401
402                 let pointee_type = match base_ty.sty {
403                     ty::TyRawPtr(ref tam) => tam.ty,
404                     ty::TyRef(_, ty, _) => ty,
405                     ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
406                     _ => bug!("can only deref pointer types"),
407                 };
408
409                 trace!("deref to {} on {:?}", pointee_type, val);
410
411                 self.val_to_place(val, pointee_type)
412             }
413
414             Index(local) => {
415                 let value = self.frame().locals[local].access()?;
416                 let ty = self.tcx.types.usize;
417                 let n = self
418                     .value_to_scalar(ValTy { value, ty })?
419                     .to_bits(self.tcx.data_layout.pointer_size)?;
420                 self.place_index(base, base_ty, n as u64)
421             }
422
423             ConstantIndex {
424                 offset,
425                 min_length,
426                 from_end,
427             } => {
428                 // FIXME(solson)
429                 let base = self.force_allocation(base)?;
430                 let (base_ptr, align) = base.to_ptr_align();
431
432                 let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx);
433                 let elem_size = self.layout_of(elem_ty)?.size;
434                 assert!(n >= min_length as u64);
435
436                 let index = if from_end {
437                     n - u64::from(offset)
438                 } else {
439                     u64::from(offset)
440                 };
441
442                 let ptr = base_ptr.ptr_offset(elem_size * index, &self)?;
443                 Ok(Place::Ptr { ptr, align, extra: PlaceExtra::None })
444             }
445
446             Subslice { from, to } => {
447                 // FIXME(solson)
448                 let base = self.force_allocation(base)?;
449                 let (base_ptr, align) = base.to_ptr_align();
450
451                 let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx);
452                 let elem_size = self.layout_of(elem_ty)?.size;
453                 assert!(u64::from(from) <= n - u64::from(to));
454                 let ptr = base_ptr.ptr_offset(elem_size * u64::from(from), &self)?;
455                 // sublicing arrays produces arrays
456                 let extra = if self.type_is_sized(base_ty) {
457                     PlaceExtra::None
458                 } else {
459                     PlaceExtra::Length(n - u64::from(to) - u64::from(from))
460                 };
461                 Ok(Place::Ptr { ptr, align, extra })
462             }
463         }
464     }
465
466     pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> {
467         self.monomorphize(
468             place.ty(self.mir(), *self.tcx).to_ty(*self.tcx),
469             self.substs(),
470         )
471     }
472 }