]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/lvalue.rs
fn ptrs and never were accidentally disabled (55)
[rust.git] / src / librustc_mir / interpret / lvalue.rs
1 use rustc::hir::Mutability as TyMutability;
2 use rustc::mir::{self, ValidationOp};
3 use rustc::ty::layout::{Size, Align};
4 use rustc::ty::{self, Ty};
5 use rustc::middle::region::CodeExtent;
6 use rustc_data_structures::indexed_vec::Idx;
7 use syntax::ast::Mutability;
8
9 use error::{EvalError, EvalResult};
10 use eval_context::EvalContext;
11 use memory::{MemoryPointer, AccessKind};
12 use value::{PrimVal, Pointer, Value};
13
14 #[derive(Copy, Clone, Debug)]
15 pub enum Lvalue<'tcx> {
16     /// An lvalue referring to a value allocated in the `Memory` system.
17     Ptr {
18         /// An lvalue may have an invalid (integral or undef) pointer,
19         /// since it might be turned back into a reference
20         /// before ever being dereferenced.
21         ptr: Pointer,
22         extra: LvalueExtra,
23         /// Remember whether this lvalue is *supposed* to be aligned.
24         aligned: bool,
25     },
26
27     /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with
28     /// a Mir local index.
29     Local {
30         frame: usize,
31         local: mir::Local,
32     },
33
34     /// An lvalue referring to a global
35     Global(GlobalId<'tcx>),
36 }
37
38 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
39 pub enum LvalueExtra {
40     None,
41     Length(u64),
42     Vtable(MemoryPointer),
43     DowncastVariant(usize),
44 }
45
46 /// Uniquely identifies a specific constant or static.
47 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
48 pub struct GlobalId<'tcx> {
49     /// For a constant or static, the `Instance` of the item itself.
50     /// For a promoted global, the `Instance` of the function they belong to.
51     pub(super) instance: ty::Instance<'tcx>,
52
53     /// The index for promoted globals within their function's `Mir`.
54     pub(super) promoted: Option<mir::Promoted>,
55 }
56
57 #[derive(Clone, Debug)]
58 pub struct Global<'tcx> {
59     pub(super) value: Value,
60     /// Only used in `force_allocation` to ensure we don't mark the memory
61     /// before the static is initialized. It is possible to convert a
62     /// global which initially is `Value::ByVal(PrimVal::Undef)` and gets
63     /// lifted to an allocation before the static is fully initialized
64     pub(super) initialized: bool,
65     pub(super) mutable: Mutability,
66     pub(super) ty: Ty<'tcx>,
67 }
68
69 impl<'tcx> Lvalue<'tcx> {
70     /// Produces an Lvalue that will error if attempted to be read from
71     pub fn undef() -> Self {
72         Self::from_primval_ptr(PrimVal::Undef.into())
73     }
74
75     pub(crate) fn from_primval_ptr(ptr: Pointer) -> Self {
76         Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true }
77     }
78
79     pub(crate) fn from_ptr(ptr: MemoryPointer) -> Self {
80         Self::from_primval_ptr(ptr.into())
81     }
82
83     pub(super) fn to_ptr_extra_aligned(self) -> (Pointer, LvalueExtra, bool) {
84         match self {
85             Lvalue::Ptr { ptr, extra, aligned } => (ptr, extra, aligned),
86             _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self),
87
88         }
89     }
90
91     pub(super) fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> {
92         let (ptr, extra, _aligned) = self.to_ptr_extra_aligned();
93         // At this point, we forget about the alignment information -- the lvalue has been turned into a reference,
94         // and no matter where it came from, it now must be aligned.
95         assert_eq!(extra, LvalueExtra::None);
96         ptr.to_ptr()
97     }
98
99     pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
100         match ty.sty {
101             ty::TyArray(elem, n) => (elem, n as u64),
102
103             ty::TySlice(elem) => {
104                 match self {
105                     Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len),
106                     _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self),
107                 }
108             }
109
110             _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
111         }
112     }
113 }
114
115 impl<'tcx> Global<'tcx> {
116     pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self {
117         Global {
118             value: Value::ByVal(PrimVal::Undef),
119             mutable: Mutability::Mutable,
120             ty,
121             initialized: false,
122         }
123     }
124
125     pub(super) fn initialized(ty: Ty<'tcx>, value: Value, mutable: Mutability) -> Self {
126         Global {
127             value,
128             mutable,
129             ty,
130             initialized: true,
131         }
132     }
133 }
134
135 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
136     /// Reads a value from the lvalue without going through the intermediate step of obtaining
137     /// a `miri::Lvalue`
138     pub fn try_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Option<Value>> {
139         use rustc::mir::Lvalue::*;
140         match *lvalue {
141             // Might allow this in the future, right now there's no way to do this from Rust code anyway
142             Local(mir::RETURN_POINTER) => Err(EvalError::ReadFromReturnPointer),
143             // Directly reading a local will always succeed
144             Local(local) => self.frame().get_local(local).map(Some),
145             // Directly reading a static will always succeed
146             Static(ref static_) => {
147                 let instance = ty::Instance::mono(self.tcx, static_.def_id);
148                 let cid = GlobalId { instance, promoted: None };
149                 Ok(Some(self.globals.get(&cid).expect("global not cached").value))
150             },
151             Projection(ref proj) => self.try_read_lvalue_projection(proj),
152         }
153     }
154
155     fn try_read_lvalue_projection(&mut self, proj: &mir::LvalueProjection<'tcx>) -> EvalResult<'tcx, Option<Value>> {
156         use rustc::mir::ProjectionElem::*;
157         let base = match self.try_read_lvalue(&proj.base)? {
158             Some(base) => base,
159             None => return Ok(None),
160         };
161         let base_ty = self.lvalue_ty(&proj.base);
162         match proj.elem {
163             Field(field, _) => match (field.index(), base) {
164                 // the only field of a struct
165                 (0, Value::ByVal(val)) => Ok(Some(Value::ByVal(val))),
166                 // split fat pointers, 2 element tuples, ...
167                 (0...1, Value::ByValPair(a, b)) if self.get_field_count(base_ty)? == 2 => {
168                     let val = [a, b][field.index()];
169                     Ok(Some(Value::ByVal(val)))
170                 },
171                 // the only field of a struct is a fat pointer
172                 (0, Value::ByValPair(..)) => Ok(Some(base)),
173                 _ => Ok(None),
174             },
175             // The NullablePointer cases should work fine, need to take care for normal enums
176             Downcast(..) |
177             Subslice { .. } |
178             // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
179             ConstantIndex { .. } | Index(_) |
180             // No way to optimize this projection any better than the normal lvalue path
181             Deref => Ok(None),
182         }
183     }
184
185     /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
186     pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
187         // Shortcut for things like accessing a fat pointer's field,
188         // which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory
189         // and returning an `Lvalue::Ptr` to it
190         if let Some(val) = self.try_read_lvalue(lvalue)? {
191             return Ok(val);
192         }
193         let lvalue = self.eval_lvalue(lvalue)?;
194         self.read_lvalue(lvalue)
195     }
196
197     pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
198         match lvalue {
199             Lvalue::Ptr { ptr, extra, aligned } => {
200                 assert_eq!(extra, LvalueExtra::None);
201                 Ok(Value::ByRef(ptr, aligned))
202             }
203             Lvalue::Local { frame, local } => {
204                 self.stack[frame].get_local(local)
205             }
206             Lvalue::Global(cid) => {
207                 Ok(self.globals.get(&cid).expect("global not cached").value)
208             }
209         }
210     }
211
212     pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
213         use rustc::mir::Lvalue::*;
214         let lvalue = match *mir_lvalue {
215             Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
216             Local(local) => Lvalue::Local { frame: self.cur_frame(), local },
217
218             Static(ref static_) => {
219                 let instance = ty::Instance::mono(self.tcx, static_.def_id);
220                 Lvalue::Global(GlobalId { instance, promoted: None })
221             }
222
223             Projection(ref proj) => {
224                 let ty = self.lvalue_ty(&proj.base);
225                 let lvalue = self.eval_lvalue(&proj.base)?;
226                 return self.eval_lvalue_projection(lvalue, ty, &proj.elem);
227             }
228         };
229
230         if log_enabled!(::log::LogLevel::Trace) {
231             self.dump_local(lvalue);
232         }
233
234         Ok(lvalue)
235     }
236
237     pub fn lvalue_field(
238         &mut self,
239         base: Lvalue<'tcx>,
240         field_index: usize,
241         base_ty: Ty<'tcx>,
242         field_ty: Ty<'tcx>,
243     ) -> EvalResult<'tcx, Lvalue<'tcx>> {
244         let base_layout = self.type_layout(base_ty)?;
245         use rustc::ty::layout::Layout::*;
246         let (offset, packed) = match *base_layout {
247             Univariant { ref variant, .. } => {
248                 (variant.offsets[field_index], variant.packed)
249             },
250
251             General { ref variants, .. } => {
252                 let (_, base_extra, _) = base.to_ptr_extra_aligned();
253                 if let LvalueExtra::DowncastVariant(variant_idx) = base_extra {
254                     // +1 for the discriminant, which is field 0
255                     (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed)
256                 } else {
257                     bug!("field access on enum had no variant index");
258                 }
259             }
260
261             RawNullablePointer { .. } => {
262                 assert_eq!(field_index, 0);
263                 return Ok(base);
264             }
265
266             StructWrappedNullablePointer { ref nonnull, .. } => {
267                 (nonnull.offsets[field_index], nonnull.packed)
268             }
269
270             UntaggedUnion { .. } => return Ok(base),
271
272             Vector { element, count } => {
273                 let field = field_index as u64;
274                 assert!(field < count);
275                 let elem_size = element.size(&self.tcx.data_layout).bytes();
276                 (Size::from_bytes(field * elem_size), false)
277             }
278
279             // We treat arrays + fixed sized indexing like field accesses
280             Array { .. } => {
281                 let field = field_index as u64;
282                 let elem_size = match base_ty.sty {
283                     ty::TyArray(elem_ty, n) => {
284                         assert!(field < n as u64);
285                         self.type_size(elem_ty)?.expect("array elements are sized") as u64
286                     },
287                     _ => bug!("lvalue_field: got Array layout but non-array type {:?}", base_ty),
288                 };
289                 (Size::from_bytes(field * elem_size), false)
290             }
291
292             FatPointer { .. } => {
293                 let bytes = field_index as u64 * self.memory.pointer_size();
294                 let offset = Size::from_bytes(bytes);
295                 (offset, false)
296             }
297
298             _ => bug!("field access on non-product type: {:?}", base_layout),
299         };
300
301         // Do not allocate in trivial cases
302         let (base_ptr, base_extra, aligned) = match base {
303             Lvalue::Ptr { ptr, extra, aligned } => (ptr, extra, aligned),
304             Lvalue::Local { frame, local } => match self.stack[frame].get_local(local)? {
305                 // in case the type has a single field, just return the value
306                 Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
307                     assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
308                     return Ok(base);
309                 },
310                 Value::ByRef(..) |
311                 Value::ByValPair(..) |
312                 Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
313             },
314             Lvalue::Global(cid) => match self.globals.get(&cid).expect("uncached global").value {
315                 // in case the type has a single field, just return the value
316                 Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
317                     assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
318                     return Ok(base);
319                 },
320                 Value::ByRef(..) |
321                 Value::ByValPair(..) |
322                 Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
323             },
324         };
325
326         let offset = match base_extra {
327             LvalueExtra::Vtable(tab) => {
328                 let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.to_value_with_vtable(tab))?;
329                 offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
330             }
331             _ => offset.bytes(),
332         };
333
334         let ptr = base_ptr.offset(offset, &self)?;
335
336         let field_ty = self.monomorphize(field_ty, self.substs());
337
338         let extra = if self.type_is_sized(field_ty) {
339             LvalueExtra::None
340         } else {
341             match base_extra {
342                 LvalueExtra::None => bug!("expected fat pointer"),
343                 LvalueExtra::DowncastVariant(..) =>
344                     bug!("Rust doesn't support unsized fields in enum variants"),
345                 LvalueExtra::Vtable(_) |
346                 LvalueExtra::Length(_) => {},
347             }
348             base_extra
349         };
350
351         Ok(Lvalue::Ptr { ptr, extra, aligned: aligned && !packed })
352     }
353
354     fn val_to_lvalue(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
355         Ok(match self.tcx.struct_tail(ty).sty {
356             ty::TyDynamic(..) => {
357                 let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?;
358                 Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true }
359             },
360             ty::TyStr | ty::TySlice(_) => {
361                 let (ptr, len) = val.into_slice(&mut self.memory)?;
362                 Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true }
363             },
364             _ => Lvalue::Ptr { ptr: val.into_ptr(&mut self.memory)?, extra: LvalueExtra::None, aligned: true },
365         })
366     }
367
368     fn lvalue_index(&mut self, base: Lvalue<'tcx>, outer_ty: Ty<'tcx>, n: u64) -> EvalResult<'tcx, Lvalue<'tcx>> {
369         // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
370         let base = self.force_allocation(base)?;
371         let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
372
373         let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
374         let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
375         assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
376         let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?;
377         Ok(Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned })
378     }
379
380     fn eval_lvalue_projection(
381         &mut self,
382         base: Lvalue<'tcx>,
383         base_ty: Ty<'tcx>,
384         proj_elem: &mir::ProjectionElem<'tcx, mir::Operand<'tcx>>,
385     ) -> EvalResult<'tcx, Lvalue<'tcx>> {
386         use rustc::mir::ProjectionElem::*;
387         let (ptr, extra, aligned) = match *proj_elem {
388             Field(field, field_ty) => {
389                 return self.lvalue_field(base, field.index(), base_ty, field_ty);
390             }
391
392             Downcast(_, variant) => {
393                 let base_layout = self.type_layout(base_ty)?;
394                 // FIXME(solson)
395                 let base = self.force_allocation(base)?;
396                 let (base_ptr, base_extra, aligned) = base.to_ptr_extra_aligned();
397
398                 use rustc::ty::layout::Layout::*;
399                 let extra = match *base_layout {
400                     General { .. } => LvalueExtra::DowncastVariant(variant),
401                     RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra,
402                     _ => bug!("variant downcast on non-aggregate: {:?}", base_layout),
403                 };
404                 (base_ptr, extra, aligned)
405             }
406
407             Deref => {
408                 let val = self.read_lvalue(base)?;
409
410                 let pointee_type = match base_ty.sty {
411                     ty::TyRawPtr(ref tam) |
412                     ty::TyRef(_, ref tam) => tam.ty,
413                     ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
414                     _ => bug!("can only deref pointer types"),
415                 };
416
417                 trace!("deref to {} on {:?}", pointee_type, val);
418
419                 return self.val_to_lvalue(val, pointee_type);
420             }
421
422             Index(ref operand) => {
423                 // FIXME(solson)
424                 let n_ptr = self.eval_operand(operand)?;
425                 let usize = self.tcx.types.usize;
426                 let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
427                 return self.lvalue_index(base, base_ty, n);
428             }
429
430             ConstantIndex { offset, min_length, from_end } => {
431                 // FIXME(solson)
432                 let base = self.force_allocation(base)?;
433                 let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
434
435                 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
436                 let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized");
437                 assert!(n >= min_length as u64);
438
439                 let index = if from_end {
440                     n - u64::from(offset)
441                 } else {
442                     u64::from(offset)
443                 };
444
445                 let ptr = base_ptr.offset(index * elem_size, &self)?;
446                 (ptr, LvalueExtra::None, aligned)
447             }
448
449             Subslice { from, to } => {
450                 // FIXME(solson)
451                 let base = self.force_allocation(base)?;
452                 let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
453
454                 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
455                 let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
456                 assert!(u64::from(from) <= n - u64::from(to));
457                 let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?;
458                 let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from));
459                 (ptr, extra, aligned)
460             }
461         };
462
463         Ok(Lvalue::Ptr { ptr, extra, aligned })
464     }
465
466     pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
467         self.monomorphize(lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx), self.substs())
468     }
469 }
470
471 // Validity checks
472 #[derive(Copy, Clone, Debug)]
473 pub struct ValidationCtx {
474     op: ValidationOp,
475     region: Option<CodeExtent>,
476     mutbl: TyMutability,
477 }
478
479 impl ValidationCtx {
480     pub fn new(op: ValidationOp) -> Self {
481         ValidationCtx {
482             op, region: None, mutbl: TyMutability::MutMutable,
483         }
484     }
485 }
486
487 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
488     fn validate_variant(
489         &mut self,
490         lvalue: Lvalue<'tcx>,
491         ty: Ty<'tcx>,
492         variant: &ty::VariantDef,
493         subst: &ty::subst::Substs<'tcx>,
494         vctx: ValidationCtx,
495     ) -> EvalResult<'tcx> {
496         // TODO: Take visibility/privacy into account.
497         for (idx, field) in variant.fields.iter().enumerate() {
498             let field_ty = field.ty(self.tcx, subst);
499             let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
500             self.validate(field_lvalue, field_ty, vctx)?;
501         }
502         Ok(())
503     }
504
505     fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, vctx: ValidationCtx) -> EvalResult<'tcx> {
506         // Check alignment and non-NULLness
507         let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
508         let ptr = val.into_ptr(&mut self.memory)?;
509         self.memory.check_align(ptr, align)?;
510
511         // Recurse
512         let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?;
513         self.validate(pointee_lvalue, pointee_ty, vctx)
514     }
515
516     /// Validate the lvalue at the given type. If `release` is true, just do a release of all write locks
517     pub(super) fn validate(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, mut vctx: ValidationCtx) -> EvalResult<'tcx>
518     {
519         use rustc::ty::TypeVariants::*;
520         use rustc::ty::RegionKind::*;
521         use rustc::ty::AdtKind;
522         use self::TyMutability::*;
523
524         trace!("Validating {:?} at type {}, context {:?}", lvalue, ty, vctx);
525
526         // Decide whether this type *owns* the memory it covers (like integers), or whether it
527         // just assembles pieces (that each own their memory) together to a larger whole.
528         // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
529         let is_owning = match ty.sty {
530             TyInt(_) | TyUint(_) | TyRawPtr(_) |
531             TyBool | TyFloat(_) | TyChar | TyStr |
532             TyRef(..) | TyFnPtr(..) | TyNever => true,
533             TyAdt(adt, _) if adt.is_box() => true,
534             TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) => false,
535             TyParam(_) | TyInfer(_) => bug!("I got an incomplete type for validation"),
536             _ => return Err(EvalError::Unimplemented(format!("Unimplemented type encountered when checking validity."))),
537         };
538         if is_owning {
539             match lvalue {
540                 Lvalue::Ptr { ptr, extra, aligned: _ } => {
541                     // Determine the size
542                     // FIXME: Can we reuse size_and_align_of_dst for Lvalues?
543                     let len = match self.type_size(ty)? {
544                         Some(size) => {
545                             assert_eq!(extra, LvalueExtra::None, "Got a fat ptr to a sized type");
546                             size
547                         }
548                         None => {
549                             // The only unsized typ we concider "owning" is TyStr.
550                             assert_eq!(ty.sty, TyStr, "Found a surprising unsized owning type");
551                             // The extra must be the length, in bytes.
552                             match extra {
553                                 LvalueExtra::Length(len) => len,
554                                 _ => bug!("TyStr must have a length as extra"),
555                             }
556                         }
557                     };
558                     // Handle locking
559                     if len > 0 {
560                         let ptr = ptr.to_ptr()?;
561                         let access = match vctx.mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read };
562                         match vctx.op {
563                             ValidationOp::Acquire => self.memory.acquire_lock(ptr, len, vctx.region, access)?,
564                             ValidationOp::Release => self.memory.release_write_lock_until(ptr, len, None)?,
565                             ValidationOp::Suspend(region) => self.memory.release_write_lock_until(ptr, len, Some(region))?,
566                         }
567                     }
568                 }
569                 Lvalue::Local { ..} | Lvalue::Global(..) => {
570                     // These are not backed by memory, so we have nothing to do.
571                 }
572             }
573         }
574
575         match ty.sty {
576             TyInt(_) | TyUint(_) | TyRawPtr(_) => {
577                 // TODO: Make sure these are not undef.
578                 // 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.
579                 Ok(())
580             }
581             TyBool | TyFloat(_) | TyChar | TyStr => {
582                 // TODO: Check if these are valid bool/float/codepoint/UTF-8, respectively (and in particular, not undef).
583                 Ok(())
584             }
585             TyNever => {
586                 Err(EvalError::ValidationFailure(format!("The empty type is never valid.")))
587             }
588             TyRef(region, ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
589                 let val = self.read_lvalue(lvalue)?;
590                 // Sharing restricts our context
591                 if mutbl == MutImmutable {
592                     // Actually, in case of releasing-validation, this means we are done.
593                     if vctx.op != ValidationOp::Acquire {
594                         return Ok(());
595                     }
596                     vctx.mutbl = MutImmutable;
597                 }
598                 // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
599                 // we record the region of this borrow to the context.
600                 if vctx.region == None {
601                     match *region {
602                         ReScope(ce) => vctx.region = Some(ce),
603                         // It is possible for us to encode erased lifetimes here because the lifetimes in
604                         // this functions' Subst will be erased.
605                         _ => {},
606                     }
607                 }
608                 self.validate_ptr(val, pointee_ty, vctx)
609             }
610             TyAdt(adt, _) if adt.is_box() => {
611                 let val = self.read_lvalue(lvalue)?;
612                 self.validate_ptr(val, ty.boxed_ty(), vctx)
613             }
614             TyFnPtr(_sig) => {
615                 // TODO: The function names here could need some improvement.
616                 let ptr = self.read_lvalue(lvalue)?.into_ptr(&mut self.memory)?.to_ptr()?;
617                 self.memory.get_fn(ptr)?;
618                 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
619                 Ok(())
620             }
621
622             // Compound types
623             TySlice(elem_ty) => {
624                 let len = match lvalue {
625                     Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
626                     _ => bug!("acquire_valid of a TySlice given non-slice lvalue: {:?}", lvalue),
627                 };
628                 for i in 0..len {
629                     let inner_lvalue = self.lvalue_index(lvalue, ty, i)?;
630                     self.validate(inner_lvalue, elem_ty, vctx)?;
631                 }
632                 Ok(())
633             }
634             TyArray(elem_ty, len) => {
635                 for i in 0..len {
636                     let inner_lvalue = self.lvalue_index(lvalue, ty, i as u64)?;
637                     self.validate(inner_lvalue, elem_ty, vctx)?;
638                 }
639                 Ok(())
640             }
641             TyAdt(adt, subst) => {
642                 match adt.adt_kind() {
643                     AdtKind::Enum => {
644                         // TODO: Can we get the discriminant without forcing an allocation?
645                         let ptr = self.force_allocation(lvalue)?.to_ptr()?;
646                         let discr = self.read_discriminant_value(ptr, ty)?;
647
648                         // Get variant index for discriminant
649                         let variant_idx = adt.discriminants(self.tcx)
650                             .position(|variant_discr| variant_discr.to_u128_unchecked() == discr)
651                             .ok_or(EvalError::InvalidDiscriminant)?;
652                         let variant = &adt.variants[variant_idx];
653
654                         if variant.fields.len() > 0 {
655                             // Downcast to this variant
656                             let lvalue = self.eval_lvalue_projection(lvalue, ty, &mir::ProjectionElem::Downcast(adt, variant_idx))?;
657
658                             // Recursively validate the fields
659                             self.validate_variant(lvalue, ty, variant, subst, vctx)
660                         } else {
661                             // No fields, nothing left to check.  Downcasting may fail, e.g. in case of a CEnum.
662                             Ok(())
663                         }
664                     }
665                     AdtKind::Struct => {
666                         self.validate_variant(lvalue, ty, adt.struct_variant(), subst, vctx)
667                     }
668                     AdtKind::Union => {
669                         // No guarantees are provided for union types.
670                         // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
671                         Ok(())
672                     }
673                 }
674             }
675             TyTuple(ref types, _) => {
676                 for (idx, field_ty) in types.iter().enumerate() {
677                     let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
678                     self.validate(field_lvalue, field_ty, vctx)?;
679                 }
680                 Ok(())
681             }
682             TyClosure(def_id, ref closure_substs) => {
683                 for (idx, field_ty) in closure_substs.upvar_tys(def_id, self.tcx).enumerate() {
684                     let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
685                     self.validate(field_lvalue, field_ty, vctx)?;
686                 }
687                 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
688                 // Is there other things we can/should check?  Like vtable pointers?
689                 Ok(())
690             }
691             _ => bug!("We already establishd that this is a type we support.")
692         }
693     }
694 }