]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/interpret/place.rs
New upstream version 1.26.0+dfsg1
[rustc.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, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, '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 // No fast path for statics. Reading from statics is rare and would require another
107 // Machine function to handle differently in miri.
108 Static(_) => Ok(None),
109 Projection(ref proj) => self.try_read_place_projection(proj),
110 }
111 }
112
113 pub fn read_field(
114 &self,
115 base: Value,
116 variant: Option<usize>,
117 field: mir::Field,
118 base_ty: Ty<'tcx>,
119 ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> {
120 let mut base_layout = self.layout_of(base_ty)?;
121 if let Some(variant_index) = variant {
122 base_layout = base_layout.for_variant(self, variant_index);
123 }
124 let field_index = field.index();
125 let field = base_layout.field(self, field_index)?;
126 if field.size.bytes() == 0 {
127 return Ok(Some((Value::ByVal(PrimVal::Undef), field.ty)))
128 }
129 let offset = base_layout.fields.offset(field_index);
130 match base {
131 // the field covers the entire type
132 Value::ByValPair(..) |
133 Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some((base, field.ty))),
134 // split fat pointers, 2 element tuples, ...
135 Value::ByValPair(a, b) if base_layout.fields.count() == 2 => {
136 let val = [a, b][field_index];
137 Ok(Some((Value::ByVal(val), field.ty)))
138 },
139 _ => Ok(None),
140 }
141 }
142
143 fn try_read_place_projection(
144 &mut self,
145 proj: &mir::PlaceProjection<'tcx>,
146 ) -> EvalResult<'tcx, Option<Value>> {
147 use rustc::mir::ProjectionElem::*;
148 let base = match self.try_read_place(&proj.base)? {
149 Some(base) => base,
150 None => return Ok(None),
151 };
152 let base_ty = self.place_ty(&proj.base);
153 match proj.elem {
154 Field(field, _) => Ok(self.read_field(base, None, field, base_ty)?.map(|(f, _)| f)),
155 // The NullablePointer cases should work fine, need to take care for normal enums
156 Downcast(..) |
157 Subslice { .. } |
158 // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
159 ConstantIndex { .. } | Index(_) |
160 // No way to optimize this projection any better than the normal place path
161 Deref => Ok(None),
162 }
163 }
164
165 /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
166 pub(super) fn eval_and_read_place(
167 &mut self,
168 place: &mir::Place<'tcx>,
169 ) -> EvalResult<'tcx, Value> {
170 // Shortcut for things like accessing a fat pointer's field,
171 // which would otherwise (in the `eval_place` path) require moving a `ByValPair` to memory
172 // and returning an `Place::Ptr` to it
173 if let Some(val) = self.try_read_place(place)? {
174 return Ok(val);
175 }
176 let place = self.eval_place(place)?;
177 self.read_place(place)
178 }
179
180 pub fn read_place(&self, place: Place) -> EvalResult<'tcx, Value> {
181 match place {
182 Place::Ptr { ptr, align, extra } => {
183 assert_eq!(extra, PlaceExtra::None);
184 Ok(Value::ByRef(ptr, align))
185 }
186 Place::Local { frame, local } => self.stack[frame].get_local(local),
187 }
188 }
189
190 pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> {
191 use rustc::mir::Place::*;
192 let place = match *mir_place {
193 Local(mir::RETURN_PLACE) => self.frame().return_place,
194 Local(local) => Place::Local {
195 frame: self.cur_frame(),
196 local,
197 },
198
199 Static(ref static_) => {
200 let layout = self.layout_of(self.place_ty(mir_place))?;
201 let instance = ty::Instance::mono(*self.tcx, static_.def_id);
202 let cid = GlobalId {
203 instance,
204 promoted: None
205 };
206 let alloc = Machine::init_static(self, cid)?;
207 Place::Ptr {
208 ptr: MemoryPointer::new(alloc, 0).into(),
209 align: layout.align,
210 extra: PlaceExtra::None,
211 }
212 }
213
214 Projection(ref proj) => {
215 let ty = self.place_ty(&proj.base);
216 let place = self.eval_place(&proj.base)?;
217 return self.eval_place_projection(place, ty, &proj.elem);
218 }
219 };
220
221 if log_enabled!(::log::Level::Trace) {
222 self.dump_local(place);
223 }
224
225 Ok(place)
226 }
227
228 pub fn place_field(
229 &mut self,
230 base: Place,
231 field: mir::Field,
232 mut base_layout: TyLayout<'tcx>,
233 ) -> EvalResult<'tcx, (Place, TyLayout<'tcx>)> {
234 match base {
235 Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => {
236 base_layout = base_layout.for_variant(&self, variant_index);
237 }
238 _ => {}
239 }
240 let field_index = field.index();
241 let field = base_layout.field(&self, field_index)?;
242 let offset = base_layout.fields.offset(field_index);
243
244 // Do not allocate in trivial cases
245 let (base_ptr, base_align, base_extra) = match base {
246 Place::Ptr { ptr, align, extra } => (ptr, align, extra),
247 Place::Local { frame, local } => {
248 match (&self.stack[frame].get_local(local)?, &base_layout.abi) {
249 // in case the field covers the entire type, just return the value
250 (&Value::ByVal(_), &layout::Abi::Scalar(_)) |
251 (&Value::ByValPair(..), &layout::Abi::ScalarPair(..))
252 if offset.bytes() == 0 && field.size == base_layout.size =>
253 {
254 return Ok((base, field));
255 }
256 _ => self.force_allocation(base)?.to_ptr_align_extra(),
257 }
258 }
259 };
260
261 let offset = match base_extra {
262 PlaceExtra::Vtable(tab) => {
263 let (_, align) = self.size_and_align_of_dst(
264 base_layout.ty,
265 base_ptr.to_value_with_vtable(tab),
266 )?;
267 offset.abi_align(align).bytes()
268 }
269 _ => offset.bytes(),
270 };
271
272 let ptr = base_ptr.offset(offset, &self)?;
273 let align = base_align.min(base_layout.align).min(field.align);
274 let extra = if !field.is_unsized() {
275 PlaceExtra::None
276 } else {
277 match base_extra {
278 PlaceExtra::None => bug!("expected fat pointer"),
279 PlaceExtra::DowncastVariant(..) => {
280 bug!("Rust doesn't support unsized fields in enum variants")
281 }
282 PlaceExtra::Vtable(_) |
283 PlaceExtra::Length(_) => {}
284 }
285 base_extra
286 };
287
288 Ok((Place::Ptr { ptr, align, extra }, field))
289 }
290
291 pub fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> {
292 let layout = self.layout_of(ty)?;
293 Ok(match self.tcx.struct_tail(ty).sty {
294 ty::TyDynamic(..) => {
295 let (ptr, vtable) = self.into_ptr_vtable_pair(val)?;
296 Place::Ptr {
297 ptr,
298 align: layout.align,
299 extra: PlaceExtra::Vtable(vtable),
300 }
301 }
302 ty::TyStr | ty::TySlice(_) => {
303 let (ptr, len) = self.into_slice(val)?;
304 Place::Ptr {
305 ptr,
306 align: layout.align,
307 extra: PlaceExtra::Length(len),
308 }
309 }
310 _ => Place::from_primval_ptr(self.into_ptr(val)?, layout.align),
311 })
312 }
313
314 pub fn place_index(
315 &mut self,
316 base: Place,
317 outer_ty: Ty<'tcx>,
318 n: u64,
319 ) -> EvalResult<'tcx, Place> {
320 // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
321 let base = self.force_allocation(base)?;
322 let (base_ptr, align) = base.to_ptr_align();
323
324 let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
325 let elem_size = self.layout_of(elem_ty)?.size.bytes();
326 assert!(
327 n < len,
328 "Tried to access element {} of array/slice with length {}",
329 n,
330 len
331 );
332 let ptr = base_ptr.offset(n * elem_size, &*self)?;
333 Ok(Place::Ptr {
334 ptr,
335 align,
336 extra: PlaceExtra::None,
337 })
338 }
339
340 pub(super) fn place_downcast(
341 &mut self,
342 base: Place,
343 variant: usize,
344 ) -> EvalResult<'tcx, Place> {
345 // FIXME(solson)
346 let base = self.force_allocation(base)?;
347 let (ptr, align) = base.to_ptr_align();
348 let extra = PlaceExtra::DowncastVariant(variant);
349 Ok(Place::Ptr { ptr, align, extra })
350 }
351
352 pub fn eval_place_projection(
353 &mut self,
354 base: Place,
355 base_ty: Ty<'tcx>,
356 proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
357 ) -> EvalResult<'tcx, Place> {
358 use rustc::mir::ProjectionElem::*;
359 match *proj_elem {
360 Field(field, _) => {
361 let layout = self.layout_of(base_ty)?;
362 Ok(self.place_field(base, field, layout)?.0)
363 }
364
365 Downcast(_, variant) => {
366 self.place_downcast(base, variant)
367 }
368
369 Deref => {
370 let val = self.read_place(base)?;
371
372 let pointee_type = match base_ty.sty {
373 ty::TyRawPtr(ref tam) |
374 ty::TyRef(_, ref tam) => tam.ty,
375 ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
376 _ => bug!("can only deref pointer types"),
377 };
378
379 trace!("deref to {} on {:?}", pointee_type, val);
380
381 self.val_to_place(val, pointee_type)
382 }
383
384 Index(local) => {
385 let value = self.frame().get_local(local)?;
386 let ty = self.tcx.types.usize;
387 let n = self.value_to_primval(ValTy { value, ty })?.to_u64()?;
388 self.place_index(base, base_ty, n)
389 }
390
391 ConstantIndex {
392 offset,
393 min_length,
394 from_end,
395 } => {
396 // FIXME(solson)
397 let base = self.force_allocation(base)?;
398 let (base_ptr, align) = base.to_ptr_align();
399
400 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
401 let elem_size = self.layout_of(elem_ty)?.size.bytes();
402 assert!(n >= min_length as u64);
403
404 let index = if from_end {
405 n - u64::from(offset)
406 } else {
407 u64::from(offset)
408 };
409
410 let ptr = base_ptr.offset(index * elem_size, &self)?;
411 Ok(Place::Ptr { ptr, align, extra: PlaceExtra::None })
412 }
413
414 Subslice { from, to } => {
415 // FIXME(solson)
416 let base = self.force_allocation(base)?;
417 let (base_ptr, align) = base.to_ptr_align();
418
419 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
420 let elem_size = self.layout_of(elem_ty)?.size.bytes();
421 assert!(u64::from(from) <= n - u64::from(to));
422 let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?;
423 // sublicing arrays produces arrays
424 let extra = if self.type_is_sized(base_ty) {
425 PlaceExtra::None
426 } else {
427 PlaceExtra::Length(n - u64::from(to) - u64::from(from))
428 };
429 Ok(Place::Ptr { ptr, align, extra })
430 }
431 }
432 }
433
434 pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> {
435 self.monomorphize(
436 place.ty(self.mir(), *self.tcx).to_ty(*self.tcx),
437 self.substs(),
438 )
439 }
440 }