]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/place.rs
Auto merge of #61625 - RalfJung:eval-interp, r=oli-obk
[rust.git] / src / librustc_mir / interpret / place.rs
1 //! Computations on places -- field projections, going from mir::Place, and writing
2 //! into a place.
3 //! All high-level functions to write to memory work on places as destinations.
4
5 use std::convert::TryFrom;
6 use std::hash::Hash;
7
8 use rustc::mir;
9 use rustc::mir::interpret::truncate;
10 use rustc::ty::{self, Ty};
11 use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout, VariantIdx};
12 use rustc::ty::TypeFoldable;
13
14 use super::{
15     GlobalId, AllocId, Allocation, Scalar, InterpResult, Pointer, PointerArithmetic,
16     InterpretCx, Machine, AllocMap, AllocationExtra,
17     RawConst, Immediate, ImmTy, ScalarMaybeUndef, Operand, OpTy, MemoryKind, LocalValue
18 };
19
20 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
21 pub struct MemPlace<Tag=(), Id=AllocId> {
22     /// A place may have an integral pointer for ZSTs, and since it might
23     /// be turned back into a reference before ever being dereferenced.
24     /// However, it may never be undef.
25     pub ptr: Scalar<Tag, Id>,
26     pub align: Align,
27     /// Metadata for unsized places. Interpretation is up to the type.
28     /// Must not be present for sized types, but can be missing for unsized types
29     /// (e.g., `extern type`).
30     pub meta: Option<Scalar<Tag, Id>>,
31 }
32
33 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
34 pub enum Place<Tag=(), Id=AllocId> {
35     /// A place referring to a value allocated in the `Memory` system.
36     Ptr(MemPlace<Tag, Id>),
37
38     /// To support alloc-free locals, we are able to write directly to a local.
39     /// (Without that optimization, we'd just always be a `MemPlace`.)
40     Local {
41         frame: usize,
42         local: mir::Local,
43     },
44 }
45
46 #[derive(Copy, Clone, Debug)]
47 pub struct PlaceTy<'tcx, Tag=()> {
48     place: Place<Tag>,
49     pub layout: TyLayout<'tcx>,
50 }
51
52 impl<'tcx, Tag> ::std::ops::Deref for PlaceTy<'tcx, Tag> {
53     type Target = Place<Tag>;
54     #[inline(always)]
55     fn deref(&self) -> &Place<Tag> {
56         &self.place
57     }
58 }
59
60 /// A MemPlace with its layout. Constructing it is only possible in this module.
61 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
62 pub struct MPlaceTy<'tcx, Tag=()> {
63     mplace: MemPlace<Tag>,
64     pub layout: TyLayout<'tcx>,
65 }
66
67 impl<'tcx, Tag> ::std::ops::Deref for MPlaceTy<'tcx, Tag> {
68     type Target = MemPlace<Tag>;
69     #[inline(always)]
70     fn deref(&self) -> &MemPlace<Tag> {
71         &self.mplace
72     }
73 }
74
75 impl<'tcx, Tag> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
76     #[inline(always)]
77     fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
78         PlaceTy {
79             place: Place::Ptr(mplace.mplace),
80             layout: mplace.layout
81         }
82     }
83 }
84
85 impl<Tag> MemPlace<Tag> {
86     /// Replace ptr tag, maintain vtable tag (if any)
87     #[inline]
88     pub fn replace_tag(self, new_tag: Tag) -> Self {
89         MemPlace {
90             ptr: self.ptr.erase_tag().with_tag(new_tag),
91             align: self.align,
92             meta: self.meta,
93         }
94     }
95
96     #[inline]
97     pub fn erase_tag(self) -> MemPlace {
98         MemPlace {
99             ptr: self.ptr.erase_tag(),
100             align: self.align,
101             meta: self.meta.map(Scalar::erase_tag),
102         }
103     }
104
105     #[inline(always)]
106     pub fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
107         MemPlace {
108             ptr,
109             align,
110             meta: None,
111         }
112     }
113
114     /// Produces a Place that will error if attempted to be read from or written to
115     #[inline(always)]
116     pub fn null(cx: &impl HasDataLayout) -> Self {
117         Self::from_scalar_ptr(Scalar::ptr_null(cx), Align::from_bytes(1).unwrap())
118     }
119
120     #[inline(always)]
121     pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
122         Self::from_scalar_ptr(ptr.into(), align)
123     }
124
125     #[inline(always)]
126     pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
127         assert!(self.meta.is_none());
128         (self.ptr, self.align)
129     }
130
131     /// metact the ptr part of the mplace
132     #[inline(always)]
133     pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
134         // At this point, we forget about the alignment information --
135         // the place has been turned into a reference, and no matter where it came from,
136         // it now must be aligned.
137         self.to_scalar_ptr_align().0.to_ptr()
138     }
139
140     /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
141     /// This is the inverse of `ref_to_mplace`.
142     #[inline(always)]
143     pub fn to_ref(self) -> Immediate<Tag> {
144         match self.meta {
145             None => Immediate::Scalar(self.ptr.into()),
146             Some(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
147         }
148     }
149
150     pub fn offset(
151         self,
152         offset: Size,
153         meta: Option<Scalar<Tag>>,
154         cx: &impl HasDataLayout,
155     ) -> InterpResult<'tcx, Self> {
156         Ok(MemPlace {
157             ptr: self.ptr.ptr_offset(offset, cx)?,
158             align: self.align.restrict_for_offset(offset),
159             meta,
160         })
161     }
162 }
163
164 impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
165     /// Produces a MemPlace that works for ZST but nothing else
166     #[inline]
167     pub fn dangling(layout: TyLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
168         MPlaceTy {
169             mplace: MemPlace::from_scalar_ptr(
170                 Scalar::from_uint(layout.align.abi.bytes(), cx.pointer_size()),
171                 layout.align.abi
172             ),
173             layout
174         }
175     }
176
177     /// Replace ptr tag, maintain vtable tag (if any)
178     #[inline]
179     pub fn replace_tag(self, new_tag: Tag) -> Self {
180         MPlaceTy {
181             mplace: self.mplace.replace_tag(new_tag),
182             layout: self.layout,
183         }
184     }
185
186     #[inline]
187     pub fn offset(
188         self,
189         offset: Size,
190         meta: Option<Scalar<Tag>>,
191         layout: TyLayout<'tcx>,
192         cx: &impl HasDataLayout,
193     ) -> InterpResult<'tcx, Self> {
194         Ok(MPlaceTy {
195             mplace: self.mplace.offset(offset, meta, cx)?,
196             layout,
197         })
198     }
199
200     #[inline]
201     fn from_aligned_ptr(ptr: Pointer<Tag>, layout: TyLayout<'tcx>) -> Self {
202         MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout }
203     }
204
205     #[inline]
206     pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
207         if self.layout.is_unsized() {
208             // We need to consult `meta` metadata
209             match self.layout.ty.sty {
210                 ty::Slice(..) | ty::Str =>
211                     return self.mplace.meta.unwrap().to_usize(cx),
212                 _ => bug!("len not supported on unsized type {:?}", self.layout.ty),
213             }
214         } else {
215             // Go through the layout.  There are lots of types that support a length,
216             // e.g., SIMD types.
217             match self.layout.fields {
218                 layout::FieldPlacement::Array { count, .. } => Ok(count),
219                 _ => bug!("len not supported on sized type {:?}", self.layout.ty),
220             }
221         }
222     }
223
224     #[inline]
225     pub(super) fn vtable(self) -> InterpResult<'tcx, Pointer<Tag>> {
226         match self.layout.ty.sty {
227             ty::Dynamic(..) => self.mplace.meta.unwrap().to_ptr(),
228             _ => bug!("vtable not supported on type {:?}", self.layout.ty),
229         }
230     }
231 }
232
233 impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> {
234     #[inline(always)]
235     pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, Immediate<Tag>> {
236         match *self {
237             Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }),
238             Operand::Immediate(imm) => Err(imm),
239         }
240     }
241
242     #[inline(always)]
243     pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
244         self.try_as_mplace().unwrap()
245     }
246 }
247
248 impl<'tcx, Tag: ::std::fmt::Debug> Place<Tag> {
249     /// Produces a Place that will error if attempted to be read from or written to
250     #[inline(always)]
251     pub fn null(cx: &impl HasDataLayout) -> Self {
252         Place::Ptr(MemPlace::null(cx))
253     }
254
255     #[inline(always)]
256     pub fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
257         Place::Ptr(MemPlace::from_scalar_ptr(ptr, align))
258     }
259
260     #[inline(always)]
261     pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
262         Place::Ptr(MemPlace::from_ptr(ptr, align))
263     }
264
265     #[inline]
266     pub fn to_mem_place(self) -> MemPlace<Tag> {
267         match self {
268             Place::Ptr(mplace) => mplace,
269             _ => bug!("to_mem_place: expected Place::Ptr, got {:?}", self),
270
271         }
272     }
273
274     #[inline]
275     pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
276         self.to_mem_place().to_scalar_ptr_align()
277     }
278
279     #[inline]
280     pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
281         self.to_mem_place().to_ptr()
282     }
283 }
284
285 impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> {
286     #[inline]
287     pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
288         MPlaceTy { mplace: self.place.to_mem_place(), layout: self.layout }
289     }
290 }
291
292 // separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385
293 impl<'a, 'mir, 'tcx, Tag, M> InterpretCx<'a, 'mir, 'tcx, M>
294 where
295     // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
296     Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static,
297     M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>,
298     // FIXME: Working around https://github.com/rust-lang/rust/issues/24159
299     M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
300     M::AllocExtra: AllocationExtra<Tag>,
301 {
302     /// Take a value, which represents a (thin or fat) reference, and make it a place.
303     /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
304     /// This does NOT call the "deref" machine hook, so it does NOT count as a
305     /// deref as far as Stacked Borrows is concerned.  Use `deref_operand` for that!
306     pub fn ref_to_mplace(
307         &self,
308         val: ImmTy<'tcx, M::PointerTag>,
309     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
310         let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
311         let layout = self.layout_of(pointee_type)?;
312
313         let mplace = MemPlace {
314             ptr: val.to_scalar_ptr()?,
315             // We could use the run-time alignment here. For now, we do not, because
316             // the point of tracking the alignment here is to make sure that the *static*
317             // alignment information emitted with the loads is correct. The run-time
318             // alignment can only be more restrictive.
319             align: layout.align.abi,
320             meta: val.to_meta()?,
321         };
322         Ok(MPlaceTy { mplace, layout })
323     }
324
325     // Take an operand, representing a pointer, and dereference it to a place -- that
326     // will always be a MemPlace.  Lives in `place.rs` because it creates a place.
327     pub fn deref_operand(
328         &self,
329         src: OpTy<'tcx, M::PointerTag>,
330     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
331         let val = self.read_immediate(src)?;
332         trace!("deref to {} on {:?}", val.layout.ty, *val);
333         self.ref_to_mplace(val)
334     }
335
336     /// Offset a pointer to project to a field. Unlike `place_field`, this is always
337     /// possible without allocating, so it can take `&self`. Also return the field's layout.
338     /// This supports both struct and array fields.
339     #[inline(always)]
340     pub fn mplace_field(
341         &self,
342         base: MPlaceTy<'tcx, M::PointerTag>,
343         field: u64,
344     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
345         // Not using the layout method because we want to compute on u64
346         let offset = match base.layout.fields {
347             layout::FieldPlacement::Arbitrary { ref offsets, .. } =>
348                 offsets[usize::try_from(field).unwrap()],
349             layout::FieldPlacement::Array { stride, .. } => {
350                 let len = base.len(self)?;
351                 assert!(field < len, "Tried to access element {} of array/slice with length {}",
352                     field, len);
353                 stride * field
354             }
355             layout::FieldPlacement::Union(count) => {
356                 assert!(field < count as u64,
357                         "Tried to access field {} of union with {} fields", field, count);
358                 // Offset is always 0
359                 Size::from_bytes(0)
360             }
361         };
362         // the only way conversion can fail if is this is an array (otherwise we already panicked
363         // above). In that case, all fields are equal.
364         let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?;
365
366         // Offset may need adjustment for unsized fields.
367         let (meta, offset) = if field_layout.is_unsized() {
368             // Re-use parent metadata to determine dynamic field layout.
369             // With custom DSTS, this *will* execute user-defined code, but the same
370             // happens at run-time so that's okay.
371             let align = match self.size_and_align_of(base.meta, field_layout)? {
372                 Some((_, align)) => align,
373                 None if offset == Size::ZERO =>
374                     // An extern type at offset 0, we fall back to its static alignment.
375                     // FIXME: Once we have made decisions for how to handle size and alignment
376                     // of `extern type`, this should be adapted.  It is just a temporary hack
377                     // to get some code to work that probably ought to work.
378                     field_layout.align.abi,
379                 None =>
380                     bug!("Cannot compute offset for extern type field at non-0 offset"),
381             };
382             (base.meta, offset.align_to(align))
383         } else {
384             // base.meta could be present; we might be accessing a sized field of an unsized
385             // struct.
386             (None, offset)
387         };
388
389         // We do not look at `base.layout.align` nor `field_layout.align`, unlike
390         // codegen -- mostly to see if we can get away with that
391         base.offset(offset, meta, field_layout, self)
392     }
393
394     // Iterates over all fields of an array. Much more efficient than doing the
395     // same by repeatedly calling `mplace_array`.
396     pub fn mplace_array_fields(
397         &self,
398         base: MPlaceTy<'tcx, Tag>,
399     ) ->
400         InterpResult<'tcx, impl Iterator<Item=InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a>
401     {
402         let len = base.len(self)?; // also asserts that we have a type where this makes sense
403         let stride = match base.layout.fields {
404             layout::FieldPlacement::Array { stride, .. } => stride,
405             _ => bug!("mplace_array_fields: expected an array layout"),
406         };
407         let layout = base.layout.field(self, 0)?;
408         let dl = &self.tcx.data_layout;
409         Ok((0..len).map(move |i| base.offset(i * stride, None, layout, dl)))
410     }
411
412     pub fn mplace_subslice(
413         &self,
414         base: MPlaceTy<'tcx, M::PointerTag>,
415         from: u64,
416         to: u64,
417     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
418         let len = base.len(self)?; // also asserts that we have a type where this makes sense
419         assert!(from <= len - to);
420
421         // Not using layout method because that works with usize, and does not work with slices
422         // (that have count 0 in their layout).
423         let from_offset = match base.layout.fields {
424             layout::FieldPlacement::Array { stride, .. } =>
425                 stride * from,
426             _ => bug!("Unexpected layout of index access: {:#?}", base.layout),
427         };
428
429         // Compute meta and new layout
430         let inner_len = len - to - from;
431         let (meta, ty) = match base.layout.ty.sty {
432             // It is not nice to match on the type, but that seems to be the only way to
433             // implement this.
434             ty::Array(inner, _) =>
435                 (None, self.tcx.mk_array(inner, inner_len)),
436             ty::Slice(..) => {
437                 let len = Scalar::from_uint(inner_len, self.pointer_size());
438                 (Some(len), base.layout.ty)
439             }
440             _ =>
441                 bug!("cannot subslice non-array type: `{:?}`", base.layout.ty),
442         };
443         let layout = self.layout_of(ty)?;
444         base.offset(from_offset, meta, layout, self)
445     }
446
447     pub fn mplace_downcast(
448         &self,
449         base: MPlaceTy<'tcx, M::PointerTag>,
450         variant: VariantIdx,
451     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
452         // Downcasts only change the layout
453         assert!(base.meta.is_none());
454         Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
455     }
456
457     /// Project into an mplace
458     pub fn mplace_projection(
459         &self,
460         base: MPlaceTy<'tcx, M::PointerTag>,
461         proj_elem: &mir::PlaceElem<'tcx>,
462     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
463         use rustc::mir::ProjectionElem::*;
464         Ok(match *proj_elem {
465             Field(field, _) => self.mplace_field(base, field.index() as u64)?,
466             Downcast(_, variant) => self.mplace_downcast(base, variant)?,
467             Deref => self.deref_operand(base.into())?,
468
469             Index(local) => {
470                 let layout = self.layout_of(self.tcx.types.usize)?;
471                 let n = self.access_local(self.frame(), local, Some(layout))?;
472                 let n = self.read_scalar(n)?;
473                 let n = n.to_bits(self.tcx.data_layout.pointer_size)?;
474                 self.mplace_field(base, u64::try_from(n).unwrap())?
475             }
476
477             ConstantIndex {
478                 offset,
479                 min_length,
480                 from_end,
481             } => {
482                 let n = base.len(self)?;
483                 assert!(n >= min_length as u64);
484
485                 let index = if from_end {
486                     n - u64::from(offset)
487                 } else {
488                     u64::from(offset)
489                 };
490
491                 self.mplace_field(base, index)?
492             }
493
494             Subslice { from, to } =>
495                 self.mplace_subslice(base, u64::from(from), u64::from(to))?,
496         })
497     }
498
499     /// Gets the place of a field inside the place, and also the field's type.
500     /// Just a convenience function, but used quite a bit.
501     /// This is the only projection that might have a side-effect: We cannot project
502     /// into the field of a local `ScalarPair`, we have to first allocate it.
503     pub fn place_field(
504         &mut self,
505         base: PlaceTy<'tcx, M::PointerTag>,
506         field: u64,
507     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
508         // FIXME: We could try to be smarter and avoid allocation for fields that span the
509         // entire place.
510         let mplace = self.force_allocation(base)?;
511         Ok(self.mplace_field(mplace, field)?.into())
512     }
513
514     pub fn place_downcast(
515         &self,
516         base: PlaceTy<'tcx, M::PointerTag>,
517         variant: VariantIdx,
518     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
519         // Downcast just changes the layout
520         Ok(match base.place {
521             Place::Ptr(mplace) =>
522                 self.mplace_downcast(MPlaceTy { mplace, layout: base.layout }, variant)?.into(),
523             Place::Local { .. } => {
524                 let layout = base.layout.for_variant(self, variant);
525                 PlaceTy { layout, ..base }
526             }
527         })
528     }
529
530     /// Projects into a place.
531     pub fn place_projection(
532         &mut self,
533         base: PlaceTy<'tcx, M::PointerTag>,
534         proj_elem: &mir::ProjectionElem<mir::Local, Ty<'tcx>>,
535     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
536         use rustc::mir::ProjectionElem::*;
537         Ok(match *proj_elem {
538             Field(field, _) =>  self.place_field(base, field.index() as u64)?,
539             Downcast(_, variant) => self.place_downcast(base, variant)?,
540             Deref => self.deref_operand(self.place_to_op(base)?)?.into(),
541             // For the other variants, we have to force an allocation.
542             // This matches `operand_projection`.
543             Subslice { .. } | ConstantIndex { .. } | Index(_) => {
544                 let mplace = self.force_allocation(base)?;
545                 self.mplace_projection(mplace, proj_elem)?.into()
546             }
547         })
548     }
549
550     /// Evaluate statics and promoteds to an `MPlace`. Used to share some code between
551     /// `eval_place` and `eval_place_to_op`.
552     pub(super) fn eval_static_to_mplace(
553         &self,
554         place_static: &mir::Static<'tcx>
555     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
556         use rustc::mir::StaticKind;
557
558         Ok(match place_static.kind {
559             StaticKind::Promoted(promoted) => {
560                 let instance = self.frame().instance;
561                 self.const_eval_raw(GlobalId {
562                     instance,
563                     promoted: Some(promoted),
564                 })?
565             }
566
567             StaticKind::Static(def_id) => {
568                 let ty = place_static.ty;
569                 assert!(!ty.needs_subst());
570                 let layout = self.layout_of(ty)?;
571                 let instance = ty::Instance::mono(*self.tcx, def_id);
572                 let cid = GlobalId {
573                     instance,
574                     promoted: None
575                 };
576                 // Just create a lazy reference, so we can support recursive statics.
577                 // tcx takes care of assigning every static one and only one unique AllocId.
578                 // When the data here is ever actually used, memory will notice,
579                 // and it knows how to deal with alloc_id that are present in the
580                 // global table but not in its local memory: It calls back into tcx through
581                 // a query, triggering the CTFE machinery to actually turn this lazy reference
582                 // into a bunch of bytes.  IOW, statics are evaluated with CTFE even when
583                 // this InterpretCx uses another Machine (e.g., in miri).  This is what we
584                 // want!  This way, computing statics works consistently between codegen
585                 // and miri: They use the same query to eventually obtain a `ty::Const`
586                 // and use that for further computation.
587                 //
588                 // Notice that statics have *two* AllocIds: the lazy one, and the resolved
589                 // one.  Here we make sure that the interpreted program never sees the
590                 // resolved ID.  Also see the doc comment of `Memory::get_static_alloc`.
591                 let alloc_id = self.tcx.alloc_map.lock().create_static_alloc(cid.instance.def_id());
592                 let ptr = self.tag_static_base_pointer(Pointer::from(alloc_id));
593                 MPlaceTy::from_aligned_ptr(ptr, layout)
594             }
595         })
596     }
597
598     /// Computes a place. You should only use this if you intend to write into this
599     /// place; for reading, a more efficient alternative is `eval_place_for_read`.
600     pub fn eval_place(
601         &mut self,
602         mir_place: &mir::Place<'tcx>,
603     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
604         use rustc::mir::PlaceBase;
605
606         mir_place.iterate(|place_base, place_projection| {
607             let mut place = match place_base {
608                 PlaceBase::Local(mir::RETURN_PLACE) => match self.frame().return_place {
609                     Some(return_place) => {
610                         // We use our layout to verify our assumption; caller will validate
611                         // their layout on return.
612                         PlaceTy {
613                             place: *return_place,
614                             layout: self
615                                 .layout_of(self.monomorphize(self.frame().mir.return_ty())?)?,
616                         }
617                     }
618                     None => return err!(InvalidNullPointerUsage),
619                 },
620                 PlaceBase::Local(local) => PlaceTy {
621                     // This works even for dead/uninitialized locals; we check further when writing
622                     place: Place::Local {
623                         frame: self.cur_frame(),
624                         local: *local,
625                     },
626                     layout: self.layout_of_local(self.frame(), *local, None)?,
627                 },
628                 PlaceBase::Static(place_static) => self.eval_static_to_mplace(place_static)?.into(),
629             };
630
631             for proj in place_projection {
632                 place = self.place_projection(place, &proj.elem)?
633             }
634
635             self.dump_place(place.place);
636             Ok(place)
637         })
638     }
639
640     /// Write a scalar to a place
641     pub fn write_scalar(
642         &mut self,
643         val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
644         dest: PlaceTy<'tcx, M::PointerTag>,
645     ) -> InterpResult<'tcx> {
646         self.write_immediate(Immediate::Scalar(val.into()), dest)
647     }
648
649     /// Write an immediate to a place
650     #[inline(always)]
651     pub fn write_immediate(
652         &mut self,
653         src: Immediate<M::PointerTag>,
654         dest: PlaceTy<'tcx, M::PointerTag>,
655     ) -> InterpResult<'tcx> {
656         self.write_immediate_no_validate(src, dest)?;
657
658         if M::enforce_validity(self) {
659             // Data got changed, better make sure it matches the type!
660             self.validate_operand(self.place_to_op(dest)?, vec![], None, /*const_mode*/false)?;
661         }
662
663         Ok(())
664     }
665
666     /// Write an `Immediate` to memory.
667     #[inline(always)]
668     pub fn write_immediate_to_mplace(
669         &mut self,
670         src: Immediate<M::PointerTag>,
671         dest: MPlaceTy<'tcx, M::PointerTag>,
672     ) -> InterpResult<'tcx> {
673         self.write_immediate_to_mplace_no_validate(src, dest)?;
674
675         if M::enforce_validity(self) {
676             // Data got changed, better make sure it matches the type!
677             self.validate_operand(dest.into(), vec![], None, /*const_mode*/ false)?;
678         }
679
680         Ok(())
681     }
682
683     /// Write an immediate to a place.
684     /// If you use this you are responsible for validating that things got copied at the
685     /// right type.
686     fn write_immediate_no_validate(
687         &mut self,
688         src: Immediate<M::PointerTag>,
689         dest: PlaceTy<'tcx, M::PointerTag>,
690     ) -> InterpResult<'tcx> {
691         if cfg!(debug_assertions) {
692             // This is a very common path, avoid some checks in release mode
693             assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
694             match src {
695                 Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Ptr(_))) =>
696                     assert_eq!(self.pointer_size(), dest.layout.size,
697                         "Size mismatch when writing pointer"),
698                 Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Raw { size, .. })) =>
699                     assert_eq!(Size::from_bytes(size.into()), dest.layout.size,
700                         "Size mismatch when writing bits"),
701                 Immediate::Scalar(ScalarMaybeUndef::Undef) => {}, // undef can have any size
702                 Immediate::ScalarPair(_, _) => {
703                     // FIXME: Can we check anything here?
704                 }
705             }
706         }
707         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
708
709         // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`,
710         // but not factored as a separate function.
711         let mplace = match dest.place {
712             Place::Local { frame, local } => {
713                 match self.stack[frame].locals[local].access_mut()? {
714                     Ok(local) => {
715                         // Local can be updated in-place.
716                         *local = LocalValue::Live(Operand::Immediate(src));
717                         return Ok(());
718                     }
719                     Err(mplace) => {
720                         // The local is in memory, go on below.
721                         mplace
722                     }
723                 }
724             },
725             Place::Ptr(mplace) => mplace, // already referring to memory
726         };
727         let dest = MPlaceTy { mplace, layout: dest.layout };
728
729         // This is already in memory, write there.
730         self.write_immediate_to_mplace_no_validate(src, dest)
731     }
732
733     /// Write an immediate to memory.
734     /// If you use this you are responsible for validating that things got copied at the
735     /// right type.
736     fn write_immediate_to_mplace_no_validate(
737         &mut self,
738         value: Immediate<M::PointerTag>,
739         dest: MPlaceTy<'tcx, M::PointerTag>,
740     ) -> InterpResult<'tcx> {
741         let (ptr, ptr_align) = dest.to_scalar_ptr_align();
742         // Note that it is really important that the type here is the right one, and matches the
743         // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
744         // to handle padding properly, which is only correct if we never look at this data with the
745         // wrong type.
746
747         // Nothing to do for ZSTs, other than checking alignment
748         if dest.layout.is_zst() {
749             return self.memory.check_align(ptr, ptr_align);
750         }
751
752         // check for integer pointers before alignment to report better errors
753         let ptr = ptr.to_ptr()?;
754         self.memory.check_align(ptr.into(), ptr_align)?;
755         let tcx = &*self.tcx;
756         // FIXME: We should check that there are dest.layout.size many bytes available in
757         // memory.  The code below is not sufficient, with enough padding it might not
758         // cover all the bytes!
759         match value {
760             Immediate::Scalar(scalar) => {
761                 match dest.layout.abi {
762                     layout::Abi::Scalar(_) => {}, // fine
763                     _ => bug!("write_immediate_to_mplace: invalid Scalar layout: {:#?}",
764                             dest.layout)
765                 }
766                 self.memory.get_mut(ptr.alloc_id)?.write_scalar(
767                     tcx, ptr, scalar, dest.layout.size
768                 )
769             }
770             Immediate::ScalarPair(a_val, b_val) => {
771                 let (a, b) = match dest.layout.abi {
772                     layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value),
773                     _ => bug!("write_immediate_to_mplace: invalid ScalarPair layout: {:#?}",
774                               dest.layout)
775                 };
776                 let (a_size, b_size) = (a.size(self), b.size(self));
777                 let b_offset = a_size.align_to(b.align(self).abi);
778                 let b_align = ptr_align.restrict_for_offset(b_offset);
779                 let b_ptr = ptr.offset(b_offset, self)?;
780
781                 self.memory.check_align(b_ptr.into(), b_align)?;
782
783                 // It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
784                 // but that does not work: We could be a newtype around a pair, then the
785                 // fields do not match the `ScalarPair` components.
786
787                 self.memory
788                     .get_mut(ptr.alloc_id)?
789                     .write_scalar(tcx, ptr, a_val, a_size)?;
790                 self.memory
791                     .get_mut(b_ptr.alloc_id)?
792                     .write_scalar(tcx, b_ptr, b_val, b_size)
793             }
794         }
795     }
796
797     /// Copies the data from an operand to a place. This does not support transmuting!
798     /// Use `copy_op_transmute` if the layouts could disagree.
799     #[inline(always)]
800     pub fn copy_op(
801         &mut self,
802         src: OpTy<'tcx, M::PointerTag>,
803         dest: PlaceTy<'tcx, M::PointerTag>,
804     ) -> InterpResult<'tcx> {
805         self.copy_op_no_validate(src, dest)?;
806
807         if M::enforce_validity(self) {
808             // Data got changed, better make sure it matches the type!
809             self.validate_operand(self.place_to_op(dest)?, vec![], None, /*const_mode*/false)?;
810         }
811
812         Ok(())
813     }
814
815     /// Copies the data from an operand to a place. This does not support transmuting!
816     /// Use `copy_op_transmute` if the layouts could disagree.
817     /// Also, if you use this you are responsible for validating that things get copied at the
818     /// right type.
819     fn copy_op_no_validate(
820         &mut self,
821         src: OpTy<'tcx, M::PointerTag>,
822         dest: PlaceTy<'tcx, M::PointerTag>,
823     ) -> InterpResult<'tcx> {
824         // We do NOT compare the types for equality, because well-typed code can
825         // actually "transmute" `&mut T` to `&T` in an assignment without a cast.
826         assert!(src.layout.details == dest.layout.details,
827             "Layout mismatch when copying!\nsrc: {:#?}\ndest: {:#?}", src, dest);
828
829         // Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
830         let src = match self.try_read_immediate(src)? {
831             Ok(src_val) => {
832                 assert!(!src.layout.is_unsized(), "cannot have unsized immediates");
833                 // Yay, we got a value that we can write directly.
834                 // FIXME: Add a check to make sure that if `src` is indirect,
835                 // it does not overlap with `dest`.
836                 return self.write_immediate_no_validate(src_val, dest);
837             }
838             Err(mplace) => mplace,
839         };
840         // Slow path, this does not fit into an immediate. Just memcpy.
841         trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
842
843         // This interprets `src.meta` with the `dest` local's layout, if an unsized local
844         // is being initialized!
845         let (dest, size) = self.force_allocation_maybe_sized(dest, src.meta)?;
846         let size = size.unwrap_or_else(|| {
847             assert!(!dest.layout.is_unsized(),
848                 "Cannot copy into already initialized unsized place");
849             dest.layout.size
850         });
851         assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances");
852         self.memory.copy(
853             src.ptr, src.align,
854             dest.ptr, dest.align,
855             size,
856             /*nonoverlapping*/ true,
857         )?;
858
859         Ok(())
860     }
861
862     /// Copies the data from an operand to a place. The layouts may disagree, but they must
863     /// have the same size.
864     pub fn copy_op_transmute(
865         &mut self,
866         src: OpTy<'tcx, M::PointerTag>,
867         dest: PlaceTy<'tcx, M::PointerTag>,
868     ) -> InterpResult<'tcx> {
869         if src.layout.details == dest.layout.details {
870             // Fast path: Just use normal `copy_op`
871             return self.copy_op(src, dest);
872         }
873         // We still require the sizes to match.
874         assert!(src.layout.size == dest.layout.size,
875             "Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest);
876         // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want
877         // to avoid that here.
878         assert!(!src.layout.is_unsized() && !dest.layout.is_unsized(),
879             "Cannot transmute unsized data");
880
881         // The hard case is `ScalarPair`.  `src` is already read from memory in this case,
882         // using `src.layout` to figure out which bytes to use for the 1st and 2nd field.
883         // We have to write them to `dest` at the offsets they were *read at*, which is
884         // not necessarily the same as the offsets in `dest.layout`!
885         // Hence we do the copy with the source layout on both sides.  We also make sure to write
886         // into memory, because if `dest` is a local we would not even have a way to write
887         // at the `src` offsets; the fact that we came from a different layout would
888         // just be lost.
889         let dest = self.force_allocation(dest)?;
890         self.copy_op_no_validate(
891             src,
892             PlaceTy::from(MPlaceTy { mplace: *dest, layout: src.layout }),
893         )?;
894
895         if M::enforce_validity(self) {
896             // Data got changed, better make sure it matches the type!
897             self.validate_operand(dest.into(), vec![], None, /*const_mode*/false)?;
898         }
899
900         Ok(())
901     }
902
903     /// Ensures that a place is in memory, and returns where it is.
904     /// If the place currently refers to a local that doesn't yet have a matching allocation,
905     /// create such an allocation.
906     /// This is essentially `force_to_memplace`.
907     ///
908     /// This supports unsized types and returns the computed size to avoid some
909     /// redundant computation when copying; use `force_allocation` for a simpler, sized-only
910     /// version.
911     pub fn force_allocation_maybe_sized(
912         &mut self,
913         place: PlaceTy<'tcx, M::PointerTag>,
914         meta: Option<Scalar<M::PointerTag>>,
915     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option<Size>)> {
916         let (mplace, size) = match place.place {
917             Place::Local { frame, local } => {
918                 match self.stack[frame].locals[local].access_mut()? {
919                     Ok(local_val) => {
920                         // We need to make an allocation.
921                         // FIXME: Consider not doing anything for a ZST, and just returning
922                         // a fake pointer?  Are we even called for ZST?
923
924                         // We cannot hold on to the reference `local_val` while allocating,
925                         // but we can hold on to the value in there.
926                         let old_val =
927                             if let LocalValue::Live(Operand::Immediate(value)) = *local_val {
928                                 Some(value)
929                             } else {
930                                 None
931                             };
932
933                         // We need the layout of the local.  We can NOT use the layout we got,
934                         // that might e.g., be an inner field of a struct with `Scalar` layout,
935                         // that has different alignment than the outer field.
936                         // We also need to support unsized types, and hence cannot use `allocate`.
937                         let local_layout = self.layout_of_local(&self.stack[frame], local, None)?;
938                         let (size, align) = self.size_and_align_of(meta, local_layout)?
939                             .expect("Cannot allocate for non-dyn-sized type");
940                         let ptr = self.memory.allocate(size, align, MemoryKind::Stack);
941                         let mplace = MemPlace { ptr: ptr.into(), align, meta };
942                         if let Some(value) = old_val {
943                             // Preserve old value.
944                             // We don't have to validate as we can assume the local
945                             // was already valid for its type.
946                             let mplace = MPlaceTy { mplace, layout: local_layout };
947                             self.write_immediate_to_mplace_no_validate(value, mplace)?;
948                         }
949                         // Now we can call `access_mut` again, asserting it goes well,
950                         // and actually overwrite things.
951                         *self.stack[frame].locals[local].access_mut().unwrap().unwrap() =
952                             LocalValue::Live(Operand::Indirect(mplace));
953                         (mplace, Some(size))
954                     }
955                     Err(mplace) => (mplace, None), // this already was an indirect local
956                 }
957             }
958             Place::Ptr(mplace) => (mplace, None)
959         };
960         // Return with the original layout, so that the caller can go on
961         Ok((MPlaceTy { mplace, layout: place.layout }, size))
962     }
963
964     #[inline(always)]
965     pub fn force_allocation(
966         &mut self,
967         place: PlaceTy<'tcx, M::PointerTag>,
968     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
969         Ok(self.force_allocation_maybe_sized(place, None)?.0)
970     }
971
972     pub fn allocate(
973         &mut self,
974         layout: TyLayout<'tcx>,
975         kind: MemoryKind<M::MemoryKinds>,
976     ) -> MPlaceTy<'tcx, M::PointerTag> {
977         let ptr = self.memory.allocate(layout.size, layout.align.abi, kind);
978         MPlaceTy::from_aligned_ptr(ptr, layout)
979     }
980
981     pub fn write_discriminant_index(
982         &mut self,
983         variant_index: VariantIdx,
984         dest: PlaceTy<'tcx, M::PointerTag>,
985     ) -> InterpResult<'tcx> {
986         match dest.layout.variants {
987             layout::Variants::Single { index } => {
988                 assert_eq!(index, variant_index);
989             }
990             layout::Variants::Multiple {
991                 discr_kind: layout::DiscriminantKind::Tag,
992                 ref discr,
993                 discr_index,
994                 ..
995             } => {
996                 assert!(dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index));
997                 let discr_val =
998                     dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
999
1000                 // raw discriminants for enums are isize or bigger during
1001                 // their computation, but the in-memory tag is the smallest possible
1002                 // representation
1003                 let size = discr.value.size(self);
1004                 let discr_val = truncate(discr_val, size);
1005
1006                 let discr_dest = self.place_field(dest, discr_index as u64)?;
1007                 self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?;
1008             }
1009             layout::Variants::Multiple {
1010                 discr_kind: layout::DiscriminantKind::Niche {
1011                     dataful_variant,
1012                     ref niche_variants,
1013                     niche_start,
1014                 },
1015                 discr_index,
1016                 ..
1017             } => {
1018                 assert!(
1019                     variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len(),
1020                 );
1021                 if variant_index != dataful_variant {
1022                     let niche_dest =
1023                         self.place_field(dest, discr_index as u64)?;
1024                     let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
1025                     let niche_value = (niche_value as u128)
1026                         .wrapping_add(niche_start);
1027                     self.write_scalar(
1028                         Scalar::from_uint(niche_value, niche_dest.layout.size),
1029                         niche_dest
1030                     )?;
1031                 }
1032             }
1033         }
1034
1035         Ok(())
1036     }
1037
1038     pub fn raw_const_to_mplace(
1039         &self,
1040         raw: RawConst<'tcx>,
1041     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
1042         // This must be an allocation in `tcx`
1043         assert!(self.tcx.alloc_map.lock().get(raw.alloc_id).is_some());
1044         let ptr = self.tag_static_base_pointer(Pointer::from(raw.alloc_id));
1045         let layout = self.layout_of(raw.ty)?;
1046         Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
1047     }
1048
1049     /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
1050     /// Also return some more information so drop doesn't have to run the same code twice.
1051     pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx, M::PointerTag>)
1052     -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
1053         let vtable = mplace.vtable()?; // also sanity checks the type
1054         let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
1055         let layout = self.layout_of(ty)?;
1056
1057         // More sanity checks
1058         if cfg!(debug_assertions) {
1059             let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
1060             assert_eq!(size, layout.size);
1061             // only ABI alignment is preserved
1062             assert_eq!(align, layout.align.abi);
1063         }
1064
1065         let mplace = MPlaceTy {
1066             mplace: MemPlace { meta: None, ..*mplace },
1067             layout
1068         };
1069         Ok((instance, mplace))
1070     }
1071 }