2 use rustc
::ty
::{self, Ty}
;
3 use rustc
::ty
::layout
::{self, Align, LayoutOf, TyLayout}
;
4 use rustc_data_structures
::indexed_vec
::Idx
;
6 use rustc
::mir
::interpret
::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer}
;
7 use super::{EvalContext, Machine, ValTy}
;
8 use interpret
::memory
::HasMemory
;
10 #[derive(Copy, Clone, Debug)]
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.
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, Eq, PartialEq)]
31 Vtable(MemoryPointer
),
32 DowncastVariant(usize),
36 /// Produces a Place that will error if attempted to be read from
37 pub fn undef() -> Self {
38 Self::from_primval_ptr(PrimVal
::Undef
.into(), Align
::from_bytes(1, 1).unwrap())
41 pub fn from_primval_ptr(ptr
: Pointer
, align
: Align
) -> Self {
45 extra
: PlaceExtra
::None
,
49 pub fn from_ptr(ptr
: MemoryPointer
, align
: Align
) -> Self {
50 Self::from_primval_ptr(ptr
.into(), align
)
53 pub fn to_ptr_align_extra(self) -> (Pointer
, 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) -> (Pointer
, Align
) {
62 let (ptr
, align
, _extra
) = self.to_ptr_align_extra();
66 pub fn to_ptr(self) -> EvalResult
<'tcx
, MemoryPointer
> {
67 // At this point, we forget about the alignment information -- the place has been turned into a reference,
68 // and no matter where it came from, it now must be aligned.
69 self.to_ptr_align().0.to_ptr()
72 pub(super) fn elem_ty_and_len(self, ty
: Ty
<'tcx
>) -> (Ty
<'tcx
>, u64) {
74 ty
::TyArray(elem
, n
) => (elem
, n
.val
.unwrap_u64() as u64),
76 ty
::TySlice(elem
) => {
78 Place
::Ptr { extra: PlaceExtra::Length(len), .. }
=> (elem
, len
),
81 "elem_ty_and_len of a TySlice given non-slice place: {:?}",
88 _
=> bug
!("elem_ty_and_len expected array or slice, got {:?}", ty
),
93 impl<'a
, 'mir
, 'tcx
, M
: Machine
<'mir
, 'tcx
>> EvalContext
<'a
, 'mir
, 'tcx
, M
> {
94 /// Reads a value from the place without going through the intermediate step of obtaining
96 pub fn try_read_place(
98 place
: &mir
::Place
<'tcx
>,
99 ) -> EvalResult
<'tcx
, Option
<Value
>> {
100 use rustc
::mir
::Place
::*;
102 // Might allow this in the future, right now there's no way to do this from Rust code anyway
103 Local(mir
::RETURN_PLACE
) => err
!(ReadFromReturnPointer
),
104 // Directly reading a local will always succeed
105 Local(local
) => self.frame().get_local(local
).map(Some
),
106 // No fast path for statics. Reading from statics is rare and would require another
107 // Machine function to handle differently in miri.
108 Static(_
) => Ok(None
),
109 Projection(ref proj
) => self.try_read_place_projection(proj
),
116 variant
: Option
<usize>,
119 ) -> EvalResult
<'tcx
, Option
<(Value
, Ty
<'tcx
>)>> {
120 let mut base_layout
= self.layout_of(base_ty
)?
;
121 if let Some(variant_index
) = variant
{
122 base_layout
= base_layout
.for_variant(self, variant_index
);
124 let field_index
= field
.index();
125 let field
= base_layout
.field(self, field_index
)?
;
126 if field
.size
.bytes() == 0 {
127 return Ok(Some((Value
::ByVal(PrimVal
::Undef
), field
.ty
)))
129 let offset
= base_layout
.fields
.offset(field_index
);
131 // the field covers the entire type
132 Value
::ByValPair(..) |
133 Value
::ByVal(_
) if offset
.bytes() == 0 && field
.size
== base_layout
.size
=> Ok(Some((base
, field
.ty
))),
134 // split fat pointers, 2 element tuples, ...
135 Value
::ByValPair(a
, b
) if base_layout
.fields
.count() == 2 => {
136 let val
= [a
, b
][field_index
];
137 Ok(Some((Value
::ByVal(val
), field
.ty
)))
143 fn try_read_place_projection(
145 proj
: &mir
::PlaceProjection
<'tcx
>,
146 ) -> EvalResult
<'tcx
, Option
<Value
>> {
147 use rustc
::mir
::ProjectionElem
::*;
148 let base
= match self.try_read_place(&proj
.base
)?
{
150 None
=> return Ok(None
),
152 let base_ty
= self.place_ty(&proj
.base
);
154 Field(field
, _
) => Ok(self.read_field(base
, None
, field
, base_ty
)?
.map(|(f
, _
)| f
)),
155 // The NullablePointer cases should work fine, need to take care for normal enums
158 // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
159 ConstantIndex { .. }
| Index(_
) |
160 // No way to optimize this projection any better than the normal place path
165 /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
166 pub(super) fn eval_and_read_place(
168 place
: &mir
::Place
<'tcx
>,
169 ) -> EvalResult
<'tcx
, Value
> {
170 // Shortcut for things like accessing a fat pointer's field,
171 // which would otherwise (in the `eval_place` path) require moving a `ByValPair` to memory
172 // and returning an `Place::Ptr` to it
173 if let Some(val
) = self.try_read_place(place
)?
{
176 let place
= self.eval_place(place
)?
;
177 self.read_place(place
)
180 pub fn read_place(&self, place
: Place
) -> EvalResult
<'tcx
, Value
> {
182 Place
::Ptr { ptr, align, extra }
=> {
183 assert_eq
!(extra
, PlaceExtra
::None
);
184 Ok(Value
::ByRef(ptr
, align
))
186 Place
::Local { frame, local }
=> self.stack
[frame
].get_local(local
),
190 pub fn eval_place(&mut self, mir_place
: &mir
::Place
<'tcx
>) -> EvalResult
<'tcx
, Place
> {
191 use rustc
::mir
::Place
::*;
192 let place
= match *mir_place
{
193 Local(mir
::RETURN_PLACE
) => self.frame().return_place
,
194 Local(local
) => Place
::Local
{
195 frame
: self.cur_frame(),
199 Static(ref static_
) => {
200 let layout
= self.layout_of(self.place_ty(mir_place
))?
;
201 let instance
= ty
::Instance
::mono(*self.tcx
, static_
.def_id
);
206 let alloc
= Machine
::init_static(self, cid
)?
;
208 ptr
: MemoryPointer
::new(alloc
, 0).into(),
210 extra
: PlaceExtra
::None
,
214 Projection(ref proj
) => {
215 let ty
= self.place_ty(&proj
.base
);
216 let place
= self.eval_place(&proj
.base
)?
;
217 return self.eval_place_projection(place
, ty
, &proj
.elem
);
221 if log_enabled
!(::log
::Level
::Trace
) {
222 self.dump_local(place
);
232 mut base_layout
: TyLayout
<'tcx
>,
233 ) -> EvalResult
<'tcx
, (Place
, TyLayout
<'tcx
>)> {
235 Place
::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. }
=> {
236 base_layout
= base_layout
.for_variant(&self, variant_index
);
240 let field_index
= field
.index();
241 let field
= base_layout
.field(&self, field_index
)?
;
242 let offset
= base_layout
.fields
.offset(field_index
);
244 // Do not allocate in trivial cases
245 let (base_ptr
, base_align
, base_extra
) = match base
{
246 Place
::Ptr { ptr, align, extra }
=> (ptr
, align
, extra
),
247 Place
::Local { frame, local }
=> {
248 match (&self.stack
[frame
].get_local(local
)?
, &base_layout
.abi
) {
249 // in case the field covers the entire type, just return the value
250 (&Value
::ByVal(_
), &layout
::Abi
::Scalar(_
)) |
251 (&Value
::ByValPair(..), &layout
::Abi
::ScalarPair(..))
252 if offset
.bytes() == 0 && field
.size
== base_layout
.size
=>
254 return Ok((base
, field
));
256 _
=> self.force_allocation(base
)?
.to_ptr_align_extra(),
261 let offset
= match base_extra
{
262 PlaceExtra
::Vtable(tab
) => {
263 let (_
, align
) = self.size_and_align_of_dst(
265 base_ptr
.to_value_with_vtable(tab
),
267 offset
.abi_align(align
).bytes()
272 let ptr
= base_ptr
.offset(offset
, &self)?
;
273 let align
= base_align
.min(base_layout
.align
).min(field
.align
);
274 let extra
= if !field
.is_unsized() {
278 PlaceExtra
::None
=> bug
!("expected fat pointer"),
279 PlaceExtra
::DowncastVariant(..) => {
280 bug
!("Rust doesn't support unsized fields in enum variants")
282 PlaceExtra
::Vtable(_
) |
283 PlaceExtra
::Length(_
) => {}
288 Ok((Place
::Ptr { ptr, align, extra }
, field
))
291 pub fn val_to_place(&self, val
: Value
, ty
: Ty
<'tcx
>) -> EvalResult
<'tcx
, Place
> {
292 let layout
= self.layout_of(ty
)?
;
293 Ok(match self.tcx
.struct_tail(ty
).sty
{
294 ty
::TyDynamic(..) => {
295 let (ptr
, vtable
) = self.into_ptr_vtable_pair(val
)?
;
299 extra
: PlaceExtra
::Vtable(vtable
),
302 ty
::TyStr
| ty
::TySlice(_
) => {
303 let (ptr
, len
) = self.into_slice(val
)?
;
307 extra
: PlaceExtra
::Length(len
),
310 _
=> Place
::from_primval_ptr(self.into_ptr(val
)?
, layout
.align
),
319 ) -> EvalResult
<'tcx
, Place
> {
320 // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
321 let base
= self.force_allocation(base
)?
;
322 let (base_ptr
, align
) = base
.to_ptr_align();
324 let (elem_ty
, len
) = base
.elem_ty_and_len(outer_ty
);
325 let elem_size
= self.layout_of(elem_ty
)?
.size
.bytes();
328 "Tried to access element {} of array/slice with length {}",
332 let ptr
= base_ptr
.offset(n
* elem_size
, &*self)?
;
336 extra
: PlaceExtra
::None
,
340 pub(super) fn place_downcast(
344 ) -> EvalResult
<'tcx
, Place
> {
346 let base
= self.force_allocation(base
)?
;
347 let (ptr
, align
) = base
.to_ptr_align();
348 let extra
= PlaceExtra
::DowncastVariant(variant
);
349 Ok(Place
::Ptr { ptr, align, extra }
)
352 pub fn eval_place_projection(
356 proj_elem
: &mir
::ProjectionElem
<'tcx
, mir
::Local
, Ty
<'tcx
>>,
357 ) -> EvalResult
<'tcx
, Place
> {
358 use rustc
::mir
::ProjectionElem
::*;
361 let layout
= self.layout_of(base_ty
)?
;
362 Ok(self.place_field(base
, field
, layout
)?
.0)
365 Downcast(_
, variant
) => {
366 self.place_downcast(base
, variant
)
370 let val
= self.read_place(base
)?
;
372 let pointee_type
= match base_ty
.sty
{
373 ty
::TyRawPtr(ref tam
) |
374 ty
::TyRef(_
, ref tam
) => tam
.ty
,
375 ty
::TyAdt(def
, _
) if def
.is_box() => base_ty
.boxed_ty(),
376 _
=> bug
!("can only deref pointer types"),
379 trace
!("deref to {} on {:?}", pointee_type
, val
);
381 self.val_to_place(val
, pointee_type
)
385 let value
= self.frame().get_local(local
)?
;
386 let ty
= self.tcx
.types
.usize;
387 let n
= self.value_to_primval(ValTy { value, ty }
)?
.to_u64()?
;
388 self.place_index(base
, base_ty
, n
)
397 let base
= self.force_allocation(base
)?
;
398 let (base_ptr
, align
) = base
.to_ptr_align();
400 let (elem_ty
, n
) = base
.elem_ty_and_len(base_ty
);
401 let elem_size
= self.layout_of(elem_ty
)?
.size
.bytes();
402 assert
!(n
>= min_length
as u64);
404 let index
= if from_end
{
405 n
- u64::from(offset
)
410 let ptr
= base_ptr
.offset(index
* elem_size
, &self)?
;
411 Ok(Place
::Ptr { ptr, align, extra: PlaceExtra::None }
)
414 Subslice { from, to }
=> {
416 let base
= self.force_allocation(base
)?
;
417 let (base_ptr
, align
) = base
.to_ptr_align();
419 let (elem_ty
, n
) = base
.elem_ty_and_len(base_ty
);
420 let elem_size
= self.layout_of(elem_ty
)?
.size
.bytes();
421 assert
!(u64::from(from
) <= n
- u64::from(to
));
422 let ptr
= base_ptr
.offset(u64::from(from
) * elem_size
, &self)?
;
423 // sublicing arrays produces arrays
424 let extra
= if self.type_is_sized(base_ty
) {
427 PlaceExtra
::Length(n
- u64::from(to
) - u64::from(from
))
429 Ok(Place
::Ptr { ptr, align, extra }
)
434 pub fn place_ty(&self, place
: &mir
::Place
<'tcx
>) -> Ty
<'tcx
> {
436 place
.ty(self.mir(), *self.tcx
).to_ty(*self.tcx
),