1 use rustc::hir::Mutability as TyMutability;
3 use rustc::ty::layout::{Size, Align};
4 use rustc::ty::{self, Ty, TypeAndMut};
5 use rustc_data_structures::indexed_vec::Idx;
6 use syntax::ast::Mutability;
8 use error::{EvalError, EvalResult};
9 use eval_context::EvalContext;
10 use memory::{MemoryPointer, AccessKind};
11 use value::{PrimVal, Pointer, Value};
13 #[derive(Copy, Clone, Debug)]
14 pub enum Lvalue<'tcx> {
15 /// An lvalue referring to a value allocated in the `Memory` system.
17 /// An lvalue may have an invalid (integral or undef) pointer,
18 /// since it might be turned back into a reference
19 /// before ever being dereferenced.
22 /// Remember whether this lvalue is *supposed* to be aligned.
26 /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with
27 /// a Mir local index.
33 /// An lvalue referring to a global
34 Global(GlobalId<'tcx>),
37 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
38 pub enum LvalueExtra {
41 Vtable(MemoryPointer),
42 DowncastVariant(usize),
45 /// Uniquely identifies a specific constant or static.
46 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
47 pub struct GlobalId<'tcx> {
48 /// For a constant or static, the `Instance` of the item itself.
49 /// For a promoted global, the `Instance` of the function they belong to.
50 pub(super) instance: ty::Instance<'tcx>,
52 /// The index for promoted globals within their function's `Mir`.
53 pub(super) promoted: Option<mir::Promoted>,
56 #[derive(Clone, Debug)]
57 pub struct Global<'tcx> {
58 pub(super) value: Value,
59 /// Only used in `force_allocation` to ensure we don't mark the memory
60 /// before the static is initialized. It is possible to convert a
61 /// global which initially is `Value::ByVal(PrimVal::Undef)` and gets
62 /// lifted to an allocation before the static is fully initialized
63 pub(super) initialized: bool,
64 pub(super) mutable: Mutability,
65 pub(super) ty: Ty<'tcx>,
68 impl<'tcx> Lvalue<'tcx> {
69 /// Produces an Lvalue that will error if attempted to be read from
70 pub fn undef() -> Self {
71 Self::from_primval_ptr(PrimVal::Undef.into())
74 pub(crate) fn from_primval_ptr(ptr: Pointer) -> Self {
75 Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true }
78 pub(crate) fn from_ptr(ptr: MemoryPointer) -> Self {
79 Self::from_primval_ptr(ptr.into())
82 pub(super) fn to_ptr_extra_aligned(self) -> (Pointer, LvalueExtra, bool) {
84 Lvalue::Ptr { ptr, extra, aligned } => (ptr, extra, aligned),
85 _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self),
90 pub(super) fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> {
91 let (ptr, extra, _aligned) = self.to_ptr_extra_aligned();
92 // At this point, we forget about the alignment information -- the lvalue has been turned into a reference,
93 // and no matter where it came from, it now must be aligned.
94 assert_eq!(extra, LvalueExtra::None);
98 pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
100 ty::TyArray(elem, n) => (elem, n as u64),
102 ty::TySlice(elem) => {
104 Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len),
105 _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self),
109 _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
114 impl<'tcx> Global<'tcx> {
115 pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self {
117 value: Value::ByVal(PrimVal::Undef),
118 mutable: Mutability::Mutable,
124 pub(super) fn initialized(ty: Ty<'tcx>, value: Value, mutable: Mutability) -> Self {
134 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
135 /// Reads a value from the lvalue without going through the intermediate step of obtaining
137 pub fn try_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Option<Value>> {
138 use rustc::mir::Lvalue::*;
140 // Might allow this in the future, right now there's no way to do this from Rust code anyway
141 Local(mir::RETURN_POINTER) => Err(EvalError::ReadFromReturnPointer),
142 // Directly reading a local will always succeed
143 Local(local) => self.frame().get_local(local).map(Some),
144 // Directly reading a static will always succeed
145 Static(ref static_) => {
146 let instance = ty::Instance::mono(self.tcx, static_.def_id);
147 let cid = GlobalId { instance, promoted: None };
148 Ok(Some(self.globals.get(&cid).expect("global not cached").value))
150 Projection(ref proj) => self.try_read_lvalue_projection(proj),
154 fn try_read_lvalue_projection(&mut self, proj: &mir::LvalueProjection<'tcx>) -> EvalResult<'tcx, Option<Value>> {
155 use rustc::mir::ProjectionElem::*;
156 let base = match self.try_read_lvalue(&proj.base)? {
158 None => return Ok(None),
160 let base_ty = self.lvalue_ty(&proj.base);
162 Field(field, _) => match (field.index(), base) {
163 // the only field of a struct
164 (0, Value::ByVal(val)) => Ok(Some(Value::ByVal(val))),
165 // split fat pointers, 2 element tuples, ...
166 (0...1, Value::ByValPair(a, b)) if self.get_field_count(base_ty)? == 2 => {
167 let val = [a, b][field.index()];
168 Ok(Some(Value::ByVal(val)))
170 // the only field of a struct is a fat pointer
171 (0, Value::ByValPair(..)) => Ok(Some(base)),
174 // The NullablePointer cases should work fine, need to take care for normal enums
177 // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
178 ConstantIndex { .. } | Index(_) |
179 // No way to optimize this projection any better than the normal lvalue path
184 /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
185 pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
186 // Shortcut for things like accessing a fat pointer's field,
187 // which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory
188 // and returning an `Lvalue::Ptr` to it
189 if let Some(val) = self.try_read_lvalue(lvalue)? {
192 let lvalue = self.eval_lvalue(lvalue)?;
193 self.read_lvalue(lvalue)
196 pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
198 Lvalue::Ptr { ptr, extra, aligned } => {
199 assert_eq!(extra, LvalueExtra::None);
200 Ok(Value::ByRef(ptr, aligned))
202 Lvalue::Local { frame, local } => {
203 self.stack[frame].get_local(local)
205 Lvalue::Global(cid) => {
206 Ok(self.globals.get(&cid).expect("global not cached").value)
211 pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
212 use rustc::mir::Lvalue::*;
213 let lvalue = match *mir_lvalue {
214 Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
215 Local(local) => Lvalue::Local { frame: self.cur_frame(), local },
217 Static(ref static_) => {
218 let instance = ty::Instance::mono(self.tcx, static_.def_id);
219 Lvalue::Global(GlobalId { instance, promoted: None })
222 Projection(ref proj) => {
223 let ty = self.lvalue_ty(&proj.base);
224 let lvalue = self.eval_lvalue(&proj.base)?;
225 return self.eval_lvalue_projection(lvalue, ty, &proj.elem);
229 if log_enabled!(::log::LogLevel::Trace) {
230 self.dump_local(lvalue);
242 ) -> EvalResult<'tcx, Lvalue<'tcx>> {
243 let base_layout = self.type_layout(base_ty)?;
244 use rustc::ty::layout::Layout::*;
245 let (offset, packed) = match *base_layout {
246 Univariant { ref variant, .. } => {
247 (variant.offsets[field_index], variant.packed)
250 General { ref variants, .. } => {
251 let (_, base_extra, _) = base.to_ptr_extra_aligned();
252 if let LvalueExtra::DowncastVariant(variant_idx) = base_extra {
253 // +1 for the discriminant, which is field 0
254 (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed)
256 bug!("field access on enum had no variant index");
260 RawNullablePointer { .. } => {
261 assert_eq!(field_index, 0);
265 StructWrappedNullablePointer { ref nonnull, .. } => {
266 (nonnull.offsets[field_index], nonnull.packed)
269 UntaggedUnion { .. } => return Ok(base),
271 Vector { element, count } => {
272 let field = field_index as u64;
273 assert!(field < count);
274 let elem_size = element.size(&self.tcx.data_layout).bytes();
275 (Size::from_bytes(field * elem_size), false)
278 // We treat arrays + fixed sized indexing like field accesses
280 let field = field_index as u64;
281 let elem_size = match base_ty.sty {
282 ty::TyArray(elem_ty, n) => {
283 assert!(field < n as u64);
284 self.type_size(elem_ty)?.expect("array elements are sized") as u64
286 _ => bug!("lvalue_field: got Array layout but non-array type {:?}", base_ty),
288 (Size::from_bytes(field * elem_size), false)
291 FatPointer { .. } => {
292 let bytes = field_index as u64 * self.memory.pointer_size();
293 let offset = Size::from_bytes(bytes);
297 _ => bug!("field access on non-product type: {:?}", base_layout),
300 // Do not allocate in trivial cases
301 let (base_ptr, base_extra, aligned) = match base {
302 Lvalue::Ptr { ptr, extra, aligned } => (ptr, extra, aligned),
303 Lvalue::Local { frame, local } => match self.stack[frame].get_local(local)? {
304 // in case the type has a single field, just return the value
305 Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
306 assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
310 Value::ByValPair(..) |
311 Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
313 Lvalue::Global(cid) => match self.globals.get(&cid).expect("uncached global").value {
314 // in case the type has a single field, just return the value
315 Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
316 assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
320 Value::ByValPair(..) |
321 Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
325 let offset = match base_extra {
326 LvalueExtra::Vtable(tab) => {
327 let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.to_value_with_vtable(tab))?;
328 offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
333 let ptr = base_ptr.offset(offset, &self)?;
335 let field_ty = self.monomorphize(field_ty, self.substs());
337 let extra = if self.type_is_sized(field_ty) {
341 LvalueExtra::None => bug!("expected fat pointer"),
342 LvalueExtra::DowncastVariant(..) =>
343 bug!("Rust doesn't support unsized fields in enum variants"),
344 LvalueExtra::Vtable(_) |
345 LvalueExtra::Length(_) => {},
350 Ok(Lvalue::Ptr { ptr, extra, aligned: aligned && !packed })
353 fn val_to_lvalue(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
354 Ok(match self.tcx.struct_tail(ty).sty {
355 ty::TyDynamic(..) => {
356 let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?;
357 Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true }
359 ty::TyStr | ty::TySlice(_) => {
360 let (ptr, len) = val.into_slice(&mut self.memory)?;
361 Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true }
363 _ => Lvalue::Ptr { ptr: val.into_ptr(&mut self.memory)?, extra: LvalueExtra::None, aligned: true },
367 fn lvalue_index(&mut self, base: Lvalue<'tcx>, outer_ty: Ty<'tcx>, n: u64) -> EvalResult<'tcx, Lvalue<'tcx>> {
368 // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
369 let base = self.force_allocation(base)?;
370 let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
372 let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
373 let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
374 assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
375 let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?;
376 Ok(Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned })
379 fn eval_lvalue_projection(
383 proj_elem: &mir::ProjectionElem<'tcx, mir::Operand<'tcx>>,
384 ) -> EvalResult<'tcx, Lvalue<'tcx>> {
385 use rustc::mir::ProjectionElem::*;
386 let (ptr, extra, aligned) = match *proj_elem {
387 Field(field, field_ty) => {
388 return self.lvalue_field(base, field.index(), base_ty, field_ty);
391 Downcast(_, variant) => {
392 let base_layout = self.type_layout(base_ty)?;
394 let base = self.force_allocation(base)?;
395 let (base_ptr, base_extra, aligned) = base.to_ptr_extra_aligned();
397 use rustc::ty::layout::Layout::*;
398 let extra = match *base_layout {
399 General { .. } => LvalueExtra::DowncastVariant(variant),
400 RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra,
401 _ => bug!("variant downcast on non-aggregate: {:?}", base_layout),
403 (base_ptr, extra, aligned)
407 let val = self.read_lvalue(base)?;
409 let pointee_type = match base_ty.sty {
410 ty::TyRawPtr(ref tam) |
411 ty::TyRef(_, ref tam) => tam.ty,
412 ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
413 _ => bug!("can only deref pointer types"),
416 trace!("deref to {} on {:?}", pointee_type, val);
418 return self.val_to_lvalue(val, pointee_type);
421 Index(ref operand) => {
423 let n_ptr = self.eval_operand(operand)?;
424 let usize = self.tcx.types.usize;
425 let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
426 return self.lvalue_index(base, base_ty, n);
429 ConstantIndex { offset, min_length, from_end } => {
431 let base = self.force_allocation(base)?;
432 let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
434 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
435 let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized");
436 assert!(n >= min_length as u64);
438 let index = if from_end {
439 n - u64::from(offset)
444 let ptr = base_ptr.offset(index * elem_size, &self)?;
445 (ptr, LvalueExtra::None, aligned)
448 Subslice { from, to } => {
450 let base = self.force_allocation(base)?;
451 let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
453 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
454 let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
455 assert!(u64::from(from) <= n - u64::from(to));
456 let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?;
457 let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from));
458 (ptr, extra, aligned)
462 Ok(Lvalue::Ptr { ptr, extra, aligned })
465 pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
466 self.monomorphize(lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx), self.substs())
471 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
472 pub(super) fn acquire_valid(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, outer_mutbl: TyMutability) -> EvalResult<'tcx> {
473 use rustc::ty::TypeVariants::*;
474 use rustc::ty::RegionKind::*;
475 use self::TyMutability::*;
477 trace!("Validating {:?} at type {}, outer mutability {:?}", lvalue, ty, outer_mutbl);
479 TyChar | TyInt(_) | TyUint(_) | TyRawPtr(_) => {
480 // TODO: Make sure these are not undef.
481 // We could do a bounds-check and other sanity checks on the lvalue, but it would be a bug in miri for this to ever fail.
484 TyBool | TyFloat(_) | TyStr => {
485 // TODO: Check if these are valid bool/float/UTF-8, respectively (and in particular, not undef).
488 TyRef(region, TypeAndMut { ty: pointee_ty, mutbl }) => {
490 let val = self.read_lvalue(lvalue)?;
491 let (len, _) = self.size_and_align_of_dst(pointee_ty, val)?;
492 let ptr = val.into_ptr(&mut self.memory)?.to_ptr()?;
493 let combined_mutbl = match outer_mutbl { MutMutable => mutbl, MutImmutable => MutImmutable };
494 let access = match combined_mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read };
495 let region = match *region {
496 ReScope(extent) => Some(extent),
499 self.memory.acquire_lock(ptr, len, region, access)?;
502 let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?;
503 self.acquire_valid(pointee_lvalue, pointee_ty, combined_mutbl)
505 TySlice(elem_ty) => {
506 let len = match lvalue {
507 Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
508 _ => bug!("acquire_valid of a TySlice given non-slice lvalue: {:?}", lvalue),
511 let inner_lvalue = self.lvalue_index(lvalue, ty, i)?;
512 self.acquire_valid(inner_lvalue, elem_ty, outer_mutbl)?;
517 // TODO: The function names here could need some improvement.
518 let ptr = self.read_lvalue(lvalue)?.into_ptr(&mut self.memory)?.to_ptr()?;
519 self.memory.get_fn(ptr)?;
520 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
523 _ => unimplemented!("Unimplemented type encountered when checking validity.")