2 use rustc::ty::{self, Ty, TyCtxt};
3 use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
4 use rustc_data_structures::indexed_vec::Idx;
6 use rustc::mir::interpret::{GlobalId, Value, Scalar, EvalResult, Pointer, ScalarMaybeUndef};
7 use super::{EvalContext, Machine, ValTy};
8 use interpret::memory::HasMemory;
10 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
12 /// A place referring to a value allocated in the `Memory` system.
14 /// A place may have an invalid (integral or undef) pointer,
15 /// since it might be turned back into a reference
16 /// before ever being dereferenced.
17 ptr: ScalarMaybeUndef,
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 },
27 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
32 DowncastVariant(usize),
36 /// Produces a Place that will error if attempted to be read from
37 pub fn undef() -> Self {
38 Self::from_scalar_ptr(ScalarMaybeUndef::Undef, Align::from_bytes(1, 1).unwrap())
41 pub fn from_scalar_ptr(ptr: ScalarMaybeUndef, align: Align) -> Self {
45 extra: PlaceExtra::None,
49 pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
50 Self::from_scalar_ptr(ScalarMaybeUndef::Scalar(ptr.into()), align)
53 pub fn to_ptr_align_extra(self) -> (ScalarMaybeUndef, Align, PlaceExtra) {
55 Place::Ptr { ptr, align, extra } => (ptr, align, extra),
56 _ => bug!("to_ptr_and_extra: expected Place::Ptr, got {:?}", self),
61 pub fn to_ptr_align(self) -> (ScalarMaybeUndef, Align) {
62 let (ptr, align, _extra) = self.to_ptr_align_extra();
66 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
67 // At this point, we forget about the alignment information -- the place has been turned into a reference,
68 // and no matter where it came from, it now must be aligned.
69 self.to_ptr_align().0.unwrap_or_err()?.to_ptr()
72 pub(super) fn elem_ty_and_len(
75 tcx: TyCtxt<'_, 'tcx, '_>
76 ) -> (Ty<'tcx>, u64) {
78 ty::TyArray(elem, n) => (elem, n.unwrap_usize(tcx)),
80 ty::TySlice(elem) => {
82 Place::Ptr { extra: PlaceExtra::Length(len), .. } => (elem, len),
85 "elem_ty_and_len of a TySlice given non-slice place: {:?}",
92 _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
97 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
98 /// Reads a value from the place without going through the intermediate step of obtaining
100 pub fn try_read_place(
102 place: &mir::Place<'tcx>,
103 ) -> EvalResult<'tcx, Option<Value>> {
104 use rustc::mir::Place::*;
106 // Might allow this in the future, right now there's no way to do this from Rust code anyway
107 Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer),
108 // Directly reading a local will always succeed
109 Local(local) => self.frame().locals[local].access().map(Some),
110 // No fast path for statics. Reading from statics is rare and would require another
111 // Machine function to handle differently in miri.
113 Static(_) => Ok(None),
114 Projection(ref proj) => self.try_read_place_projection(proj),
121 variant: Option<usize>,
123 mut base_layout: TyLayout<'tcx>,
124 ) -> EvalResult<'tcx, (Value, TyLayout<'tcx>)> {
125 if let Some(variant_index) = variant {
126 base_layout = base_layout.for_variant(self, variant_index);
128 let field_index = field.index();
129 let field = base_layout.field(self, field_index)?;
130 if field.size.bytes() == 0 {
132 Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits: 0, size: 0 })),
136 let offset = base_layout.fields.offset(field_index);
137 let value = match base {
138 // the field covers the entire type
139 Value::ScalarPair(..) |
140 Value::Scalar(_) if offset.bytes() == 0 && field.size == base_layout.size => base,
141 // extract fields from types with `ScalarPair` ABI
142 Value::ScalarPair(a, b) => {
143 let val = if offset.bytes() == 0 { a } else { b };
146 Value::ByRef(base_ptr, align) => {
147 let offset = base_layout.fields.offset(field_index);
148 let ptr = base_ptr.ptr_offset(offset, self)?;
149 let align = align.min(base_layout.align).min(field.align);
150 assert!(!field.is_unsized());
151 Value::ByRef(ptr, align)
153 Value::Scalar(val) => bug!("field access on non aggregate {:#?}, {:#?}", val, base_layout),
158 fn try_read_place_projection(
160 proj: &mir::PlaceProjection<'tcx>,
161 ) -> EvalResult<'tcx, Option<Value>> {
162 use rustc::mir::ProjectionElem::*;
163 let base = match self.try_read_place(&proj.base)? {
165 None => return Ok(None),
167 let base_ty = self.place_ty(&proj.base);
168 let base_layout = self.layout_of(base_ty)?;
170 Field(field, _) => Ok(Some(self.read_field(base, None, field, base_layout)?.0)),
171 // The NullablePointer cases should work fine, need to take care for normal enums
174 // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
175 ConstantIndex { .. } | Index(_) |
176 // No way to optimize this projection any better than the normal place path
181 /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
182 pub(super) fn eval_and_read_place(
184 place: &mir::Place<'tcx>,
185 ) -> EvalResult<'tcx, Value> {
186 // Shortcut for things like accessing a fat pointer's field,
187 // which would otherwise (in the `eval_place` path) require moving a `ScalarPair` to memory
188 // and returning an `Place::Ptr` to it
189 if let Some(val) = self.try_read_place(place)? {
192 let place = self.eval_place(place)?;
193 self.read_place(place)
196 pub fn read_place(&self, place: Place) -> EvalResult<'tcx, Value> {
198 Place::Ptr { ptr, align, extra } => {
199 assert_eq!(extra, PlaceExtra::None);
200 Ok(Value::ByRef(ptr.unwrap_or_err()?, align))
202 Place::Local { frame, local } => self.stack[frame].locals[local].access(),
206 pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> {
207 use rustc::mir::Place::*;
208 let place = match *mir_place {
209 Local(mir::RETURN_PLACE) => self.frame().return_place,
210 Local(local) => Place::Local {
211 frame: self.cur_frame(),
215 Promoted(ref promoted) => {
216 let instance = self.frame().instance;
217 let val = self.read_global_as_value(GlobalId {
219 promoted: Some(promoted.0),
221 if let Value::ByRef(ptr, align) = val {
225 extra: PlaceExtra::None,
228 bug!("evaluated promoted and got {:#?}", val);
232 Static(ref static_) => {
233 let layout = self.layout_of(self.place_ty(mir_place))?;
234 let instance = ty::Instance::mono(*self.tcx, static_.def_id);
239 let alloc = Machine::init_static(self, cid)?;
241 ptr: ScalarMaybeUndef::Scalar(Scalar::Ptr(alloc.into())),
243 extra: PlaceExtra::None,
247 Projection(ref proj) => {
248 let ty = self.place_ty(&proj.base);
249 let place = self.eval_place(&proj.base)?;
250 return self.eval_place_projection(place, ty, &proj.elem);
254 self.dump_local(place);
263 mut base_layout: TyLayout<'tcx>,
264 ) -> EvalResult<'tcx, (Place, TyLayout<'tcx>)> {
266 Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => {
267 base_layout = base_layout.for_variant(&self, variant_index);
271 let field_index = field.index();
272 let field = base_layout.field(&self, field_index)?;
273 let offset = base_layout.fields.offset(field_index);
275 // Do not allocate in trivial cases
276 let (base_ptr, base_align, base_extra) = match base {
277 Place::Ptr { ptr, align, extra } => (ptr, align, extra),
278 Place::Local { frame, local } => {
279 match (self.stack[frame].locals[local].access()?, &base_layout.abi) {
280 // in case the field covers the entire type, just return the value
281 (Value::Scalar(_), &layout::Abi::Scalar(_)) |
282 (Value::ScalarPair(..), &layout::Abi::ScalarPair(..))
283 if offset.bytes() == 0 && field.size == base_layout.size => {
284 return Ok((base, field))
286 _ => self.force_allocation(base)?.to_ptr_align_extra(),
291 let offset = match base_extra {
292 PlaceExtra::Vtable(tab) => {
293 let (_, align) = self.size_and_align_of_dst(
295 base_ptr.to_value_with_vtable(tab),
297 offset.abi_align(align)
302 let ptr = base_ptr.ptr_offset(offset, &self)?;
303 let align = base_align.min(base_layout.align).min(field.align);
304 let extra = if !field.is_unsized() {
308 PlaceExtra::None => bug!("expected fat pointer"),
309 PlaceExtra::DowncastVariant(..) => {
310 bug!("Rust doesn't support unsized fields in enum variants")
312 PlaceExtra::Vtable(_) |
313 PlaceExtra::Length(_) => {}
318 Ok((Place::Ptr { ptr, align, extra }, field))
321 pub fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> {
322 let layout = self.layout_of(ty)?;
323 Ok(match self.tcx.struct_tail(ty).sty {
324 ty::TyDynamic(..) => {
325 let (ptr, vtable) = self.into_ptr_vtable_pair(val)?;
329 extra: PlaceExtra::Vtable(vtable),
332 ty::TyStr | ty::TySlice(_) => {
333 let (ptr, len) = self.into_slice(val)?;
337 extra: PlaceExtra::Length(len),
340 _ => Place::from_scalar_ptr(self.into_ptr(val)?, layout.align),
349 ) -> EvalResult<'tcx, Place> {
350 // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
351 let base = self.force_allocation(base)?;
352 let (base_ptr, align) = base.to_ptr_align();
354 let (elem_ty, len) = base.elem_ty_and_len(outer_ty, self.tcx.tcx);
355 let elem_size = self.layout_of(elem_ty)?.size;
358 "Tried to access element {} of array/slice with length {}",
362 let ptr = base_ptr.ptr_offset(elem_size * n, &*self)?;
366 extra: PlaceExtra::None,
370 pub(super) fn place_downcast(
374 ) -> EvalResult<'tcx, Place> {
376 let base = self.force_allocation(base)?;
377 let (ptr, align) = base.to_ptr_align();
378 let extra = PlaceExtra::DowncastVariant(variant);
379 Ok(Place::Ptr { ptr, align, extra })
382 pub fn eval_place_projection(
386 proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
387 ) -> EvalResult<'tcx, Place> {
388 use rustc::mir::ProjectionElem::*;
391 let layout = self.layout_of(base_ty)?;
392 Ok(self.place_field(base, field, layout)?.0)
395 Downcast(_, variant) => {
396 self.place_downcast(base, variant)
400 let val = self.read_place(base)?;
402 let pointee_type = match base_ty.sty {
403 ty::TyRawPtr(ref tam) => tam.ty,
404 ty::TyRef(_, ty, _) => ty,
405 ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
406 _ => bug!("can only deref pointer types"),
409 trace!("deref to {} on {:?}", pointee_type, val);
411 self.val_to_place(val, pointee_type)
415 let value = self.frame().locals[local].access()?;
416 let ty = self.tcx.types.usize;
418 .value_to_scalar(ValTy { value, ty })?
419 .to_bits(self.tcx.data_layout.pointer_size)?;
420 self.place_index(base, base_ty, n as u64)
429 let base = self.force_allocation(base)?;
430 let (base_ptr, align) = base.to_ptr_align();
432 let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx);
433 let elem_size = self.layout_of(elem_ty)?.size;
434 assert!(n >= min_length as u64);
436 let index = if from_end {
437 n - u64::from(offset)
442 let ptr = base_ptr.ptr_offset(elem_size * index, &self)?;
443 Ok(Place::Ptr { ptr, align, extra: PlaceExtra::None })
446 Subslice { from, to } => {
448 let base = self.force_allocation(base)?;
449 let (base_ptr, align) = base.to_ptr_align();
451 let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx);
452 let elem_size = self.layout_of(elem_ty)?.size;
453 assert!(u64::from(from) <= n - u64::from(to));
454 let ptr = base_ptr.ptr_offset(elem_size * u64::from(from), &self)?;
455 // sublicing arrays produces arrays
456 let extra = if self.type_is_sized(base_ty) {
459 PlaceExtra::Length(n - u64::from(to) - u64::from(from))
461 Ok(Place::Ptr { ptr, align, extra })
466 pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> {
468 place.ty(self.mir(), *self.tcx).to_ty(*self.tcx),