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