]> git.lizzy.rs Git - rust.git/blob - src/validation.rs
7f0abb9ae0bad40f78f1e6993965644c7b30c129
[rust.git] / src / validation.rs
1 use rustc::hir::{self, Mutability};
2 use rustc::hir::Mutability::*;
3 use rustc::mir::{self, ValidationOp, ValidationOperand};
4 use rustc::mir::interpret::GlobalId;
5 use rustc::ty::{self, Ty, TypeFoldable, TyCtxt, Instance};
6 use rustc::ty::layout::{LayoutOf, PrimitiveExt};
7 use rustc::ty::subst::{Substs, Subst};
8 use rustc::traits::{self, TraitEngine};
9 use rustc::infer::InferCtxt;
10 use rustc::middle::region;
11 use rustc::mir::interpret::{ConstValue};
12 use rustc_data_structures::indexed_vec::Idx;
13 use rustc_mir::interpret::HasMemory;
14
15 use super::{EvalContext, Place, PlaceExtra, ValTy, ScalarExt};
16 use rustc::mir::interpret::{DynamicLifetime, AccessKind, EvalErrorKind, Value, EvalError, EvalResult};
17 use locks::MemoryExt;
18
19 pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsPlace<'tcx>, Place)>;
20
21 #[derive(Copy, Clone, Debug, PartialEq)]
22 pub(crate) enum ValidationMode {
23     Acquire,
24     /// Recover because the given region ended
25     Recover(region::Scope),
26     ReleaseUntil(Option<region::Scope>),
27 }
28
29 impl ValidationMode {
30     fn acquiring(self) -> bool {
31         use self::ValidationMode::*;
32         match self {
33             Acquire | Recover(_) => true,
34             ReleaseUntil(_) => false,
35         }
36     }
37 }
38
39 // Abstract places
40 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
41 pub enum AbsPlace<'tcx> {
42     Local(mir::Local),
43     Static(hir::def_id::DefId),
44     Projection(Box<AbsPlaceProjection<'tcx>>),
45 }
46
47 type AbsPlaceProjection<'tcx> = mir::Projection<'tcx, AbsPlace<'tcx>, u64, ()>;
48 type AbsPlaceElem<'tcx> = mir::ProjectionElem<'tcx, u64, ()>;
49
50 impl<'tcx> AbsPlace<'tcx> {
51     pub fn field(self, f: mir::Field) -> AbsPlace<'tcx> {
52         self.elem(mir::ProjectionElem::Field(f, ()))
53     }
54
55     pub fn deref(self) -> AbsPlace<'tcx> {
56         self.elem(mir::ProjectionElem::Deref)
57     }
58
59     pub fn downcast(self, adt_def: &'tcx ty::AdtDef, variant_index: usize) -> AbsPlace<'tcx> {
60         self.elem(mir::ProjectionElem::Downcast(adt_def, variant_index))
61     }
62
63     pub fn index(self, index: u64) -> AbsPlace<'tcx> {
64         self.elem(mir::ProjectionElem::Index(index))
65     }
66
67     fn elem(self, elem: AbsPlaceElem<'tcx>) -> AbsPlace<'tcx> {
68         AbsPlace::Projection(Box::new(AbsPlaceProjection {
69             base: self,
70             elem,
71         }))
72     }
73 }
74
75 pub(crate) trait EvalContextExt<'tcx> {
76     fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>>;
77     fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>>;
78     fn validation_op(
79         &mut self,
80         op: ValidationOp,
81         operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
82     ) -> EvalResult<'tcx>;
83     fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx>;
84     fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx>;
85     fn field_with_lifetimes(
86         &mut self,
87         base: Place,
88         layout: ty::layout::TyLayout<'tcx>,
89         i: usize,
90     ) -> EvalResult<'tcx, Ty<'tcx>>;
91     fn validate_fields(
92         &mut self,
93         query: ValidationQuery<'tcx>,
94         mode: ValidationMode,
95     ) -> EvalResult<'tcx>;
96     fn validate_ptr(
97         &mut self,
98         val: Value,
99         abs_place: AbsPlace<'tcx>,
100         pointee_ty: Ty<'tcx>,
101         re: Option<region::Scope>,
102         mutbl: Mutability,
103         mode: ValidationMode,
104     ) -> EvalResult<'tcx>;
105     fn validate(
106         &mut self,
107         query: ValidationQuery<'tcx>,
108         mode: ValidationMode,
109     ) -> EvalResult<'tcx>;
110 }
111
112 impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
113     fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>> {
114         use self::mir::ProjectionElem::*;
115
116         let elem = match proj.elem {
117             Deref => Deref,
118             Field(f, _) => Field(f, ()),
119             Index(v) => {
120                 let value = self.frame().locals[v].access()?;
121                 let ty = self.tcx.tcx.types.usize;
122                 let n = self.value_to_scalar(ValTy { value, ty })?.to_usize(self)?;
123                 Index(n)
124             },
125             ConstantIndex { offset, min_length, from_end } =>
126                 ConstantIndex { offset, min_length, from_end },
127             Subslice { from, to } =>
128                 Subslice { from, to },
129             Downcast(adt, sz) => Downcast(adt, sz),
130         };
131         Ok(AbsPlaceProjection {
132             base: self.abstract_place(&proj.base)?,
133             elem
134         })
135     }
136
137     fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>> {
138         Ok(match *place {
139             mir::Place::Local(l) => AbsPlace::Local(l),
140             mir::Place::Static(ref s) => AbsPlace::Static(s.def_id),
141             mir::Place::Projection(ref p) =>
142                 AbsPlace::Projection(Box::new(self.abstract_place_projection(&*p)?)),
143             _ => unimplemented!("validation is not currently maintained"),
144         })
145     }
146
147     // Validity checks
148     fn validation_op(
149         &mut self,
150         op: ValidationOp,
151         operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
152     ) -> EvalResult<'tcx> {
153         // If mir-emit-validate is set to 0 (i.e., disabled), we may still see validation commands
154         // because other crates may have been compiled with mir-emit-validate > 0.  Ignore those
155         // commands.  This makes mir-emit-validate also a flag to control whether miri will do
156         // validation or not.
157         if self.tcx.tcx.sess.opts.debugging_opts.mir_emit_validate == 0 {
158             return Ok(());
159         }
160         debug_assert!(self.memory.cur_frame == self.cur_frame());
161
162         // We need to monomorphize ty *without* erasing lifetimes
163         trace!("validation_op1: {:?}", operand.ty.sty);
164         let ty = operand.ty.subst(self.tcx.tcx, self.substs());
165         trace!("validation_op2: {:?}", operand.ty.sty);
166         let place = self.eval_place(&operand.place)?;
167         let abs_place = self.abstract_place(&operand.place)?;
168         let query = ValidationQuery {
169             place: (abs_place, place),
170             ty,
171             re: operand.re,
172             mutbl: operand.mutbl,
173         };
174
175         // Check the mode, and also perform mode-specific operations
176         let mode = match op {
177             ValidationOp::Acquire => ValidationMode::Acquire,
178             ValidationOp::Release => ValidationMode::ReleaseUntil(None),
179             ValidationOp::Suspend(scope) => {
180                 if query.mutbl == MutMutable {
181                     let lft = DynamicLifetime {
182                         frame: self.cur_frame(),
183                         region: Some(scope), // Notably, we only ever suspend things for given regions.
184                         // Suspending for the entire function does not make any sense.
185                     };
186                     trace!("Suspending {:?} until {:?}", query, scope);
187                     self.machine.suspended.entry(lft).or_insert_with(Vec::new).push(
188                         query.clone(),
189                     );
190                 }
191                 ValidationMode::ReleaseUntil(Some(scope))
192             }
193         };
194         self.validate(query, mode)
195     }
196
197     /// Release locks and executes suspensions of the given region (or the entire fn, in case of None).
198     fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx> {
199         debug_assert!(self.memory.cur_frame == self.cur_frame());
200         self.memory.locks_lifetime_ended(scope);
201         match scope {
202             Some(scope) => {
203                 // Recover suspended places
204                 let lft = DynamicLifetime {
205                     frame: self.cur_frame(),
206                     region: Some(scope),
207                 };
208                 if let Some(queries) = self.machine.suspended.remove(&lft) {
209                     for query in queries {
210                         trace!("Recovering {:?} from suspension", query);
211                         self.validate(query, ValidationMode::Recover(scope))?;
212                     }
213                 }
214             }
215             None => {
216                 // Clean suspension table of current frame
217                 let cur_frame = self.cur_frame();
218                 self.machine.suspended.retain(|lft, _| {
219                     lft.frame != cur_frame // keep only what is in the other (lower) frames
220                 });
221             }
222         }
223         Ok(())
224     }
225
226     fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
227         return normalize_associated_type(self.tcx.tcx, &ty);
228
229         use syntax::codemap::{Span, DUMMY_SP};
230
231         // We copy a bunch of stuff from rustc/infer/mod.rs to be able to tweak its behavior
232         fn normalize_projections_in<'a, 'gcx, 'tcx, T>(
233             self_: &InferCtxt<'a, 'gcx, 'tcx>,
234             param_env: ty::ParamEnv<'tcx>,
235             value: &T,
236         ) -> T::Lifted
237         where
238             T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
239         {
240             let mut selcx = traits::SelectionContext::new(self_);
241             let cause = traits::ObligationCause::dummy();
242             let traits::Normalized {
243                 value: result,
244                 obligations,
245             } = traits::normalize(&mut selcx, param_env, cause, value);
246
247             let mut fulfill_cx = traits::FulfillmentContext::new();
248
249             for obligation in obligations {
250                 fulfill_cx.register_predicate_obligation(self_, obligation);
251             }
252
253             drain_fulfillment_cx_or_panic(self_, DUMMY_SP, &mut fulfill_cx, &result)
254         }
255
256         fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
257             self_: &InferCtxt<'a, 'gcx, 'tcx>,
258             span: Span,
259             fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
260             result: &T,
261         ) -> T::Lifted
262         where
263             T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
264         {
265             // In principle, we only need to do this so long as `result`
266             // contains unbound type parameters. It could be a slight
267             // optimization to stop iterating early.
268             match fulfill_cx.select_all_or_error(self_) {
269                 Ok(()) => { }
270                 Err(errors) => {
271                     span_bug!(
272                         span,
273                         "Encountered errors `{:?}` resolving bounds after type-checking",
274                         errors
275                     );
276                 }
277             }
278
279             let result = self_.resolve_type_vars_if_possible(result);
280             let result = self_.tcx.fold_regions(
281                 &result,
282                 &mut false,
283                 |r, _| match *r {
284                     ty::ReVar(_) => self_.tcx.types.re_erased,
285                     _ => r,
286                 },
287             );
288
289             match self_.tcx.lift_to_global(&result) {
290                 Some(result) => result,
291                 None => {
292                     span_bug!(span, "Uninferred types/regions in `{:?}`", result);
293                 }
294             }
295         }
296
297         trait MyTransNormalize<'gcx>: TypeFoldable<'gcx> {
298             fn my_trans_normalize<'a, 'tcx>(
299                 &self,
300                 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
301                 param_env: ty::ParamEnv<'tcx>,
302             ) -> Self;
303         }
304
305         macro_rules! items { ($($item:item)+) => ($($item)+) }
306         macro_rules! impl_trans_normalize {
307             ($lt_gcx:tt, $($ty:ty),+) => {
308                 items!($(impl<$lt_gcx> MyTransNormalize<$lt_gcx> for $ty {
309                     fn my_trans_normalize<'a, 'tcx>(&self,
310                                                 infcx: &InferCtxt<'a, $lt_gcx, 'tcx>,
311                                                 param_env: ty::ParamEnv<'tcx>)
312                                                 -> Self {
313                         normalize_projections_in(infcx, param_env, self)
314                     }
315                 })+);
316             }
317         }
318
319         impl_trans_normalize!('gcx,
320             Ty<'gcx>,
321             &'gcx Substs<'gcx>,
322             ty::FnSig<'gcx>,
323             ty::PolyFnSig<'gcx>,
324             ty::ClosureSubsts<'gcx>,
325             ty::PolyTraitRef<'gcx>,
326             ty::ExistentialTraitRef<'gcx>
327         );
328
329         fn normalize_associated_type<'a, 'tcx, T>(self_: TyCtxt<'a, 'tcx, 'tcx>, value: &T) -> T
330         where
331             T: MyTransNormalize<'tcx>,
332         {
333             let param_env = ty::ParamEnv::reveal_all();
334
335             if !value.has_projections() {
336                 return value.clone();
337             }
338
339             self_.infer_ctxt().enter(|infcx| {
340                 value.my_trans_normalize(&infcx, param_env)
341             })
342         }
343     }
344
345     // This is a copy of `Layout::field`
346     //
347     // FIXME: remove once validation does not depend on lifetimes
348     fn field_with_lifetimes(
349         &mut self,
350         base: Place,
351         mut layout: ty::layout::TyLayout<'tcx>,
352         i: usize,
353     ) -> EvalResult<'tcx, Ty<'tcx>> {
354         if let Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } = base {
355             layout = layout.for_variant(&self, variant_index);
356         }
357         let tcx = self.tcx.tcx;
358         Ok(match layout.ty.sty {
359             ty::TyBool |
360             ty::TyChar |
361             ty::TyInt(_) |
362             ty::TyUint(_) |
363             ty::TyFloat(_) |
364             ty::TyFnPtr(_) |
365             ty::TyNever |
366             ty::TyFnDef(..) |
367             ty::TyGeneratorWitness(..) |
368             ty::TyDynamic(..) |
369             ty::TyForeign(..) => {
370                 bug!("TyLayout::field_type({:?}): not applicable", layout)
371             }
372
373             // Potentially-fat pointers.
374             ty::TyRef(_, pointee, _) |
375             ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
376                 assert!(i < 2);
377
378                 // Reuse the fat *T type as its own thin pointer data field.
379                 // This provides information about e.g. DST struct pointees
380                 // (which may have no non-DST form), and will work as long
381                 // as the `Abi` or `FieldPlacement` is checked by users.
382                 if i == 0 {
383                     return Ok(layout.ty);
384                 }
385
386                 match tcx.struct_tail(pointee).sty {
387                     ty::TySlice(_) |
388                     ty::TyStr => tcx.types.usize,
389                     ty::TyDynamic(..) => {
390                         // FIXME(eddyb) use an usize/fn() array with
391                         // the correct number of vtables slots.
392                         tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil())
393                     }
394                     _ => bug!("TyLayout::field_type({:?}): not applicable", layout)
395                 }
396             }
397
398             // Arrays and slices.
399             ty::TyArray(element, _) |
400             ty::TySlice(element) => element,
401             ty::TyStr => tcx.types.u8,
402
403             // Tuples, generators and closures.
404             ty::TyClosure(def_id, ref substs) => {
405                 substs.upvar_tys(def_id, tcx).nth(i).unwrap()
406             }
407
408             ty::TyGenerator(def_id, ref substs, _) => {
409                 substs.field_tys(def_id, tcx).nth(i).unwrap()
410             }
411
412             ty::TyTuple(tys) => tys[i],
413
414             // SIMD vector types.
415             ty::TyAdt(def, ..) if def.repr.simd() => {
416                 layout.ty.simd_type(tcx)
417             }
418
419             // ADTs.
420             ty::TyAdt(def, substs) => {
421                 use rustc::ty::layout::Variants;
422                 match layout.variants {
423                     Variants::Single { index } => {
424                         def.variants[index].fields[i].ty(tcx, substs)
425                     }
426
427                     // Discriminant field for enums (where applicable).
428                     Variants::Tagged { tag: ref discr, .. } |
429                     Variants::NicheFilling { niche: ref discr, .. } => {
430                         assert_eq!(i, 0);
431                         return Ok(discr.value.to_ty(tcx))
432                     }
433                 }
434             }
435
436             ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
437             ty::TyInfer(_) | ty::TyError => {
438                 bug!("TyLayout::field_type: unexpected type `{}`", layout.ty)
439             }
440         })
441     }
442
443     fn validate_fields(
444         &mut self,
445         query: ValidationQuery<'tcx>,
446         mode: ValidationMode,
447     ) -> EvalResult<'tcx> {
448         let mut layout = self.layout_of(query.ty)?;
449         layout.ty = query.ty;
450
451         // TODO: Maybe take visibility/privacy into account.
452         for idx in 0..layout.fields.count() {
453             let field = mir::Field::new(idx);
454             let (field_place, field_layout) =
455                 self.place_field(query.place.1, field, layout)?;
456             // layout stuff erases lifetimes, get the field ourselves
457             let field_ty = self.field_with_lifetimes(query.place.1, layout, idx)?;
458             trace!("assuming \n{:?}\n == \n{:?}\n except for lifetimes", field_layout.ty, field_ty);
459             self.validate(
460                 ValidationQuery {
461                     place: (query.place.0.clone().field(field), field_place),
462                     ty: field_ty,
463                     ..query
464                 },
465                 mode,
466             )?;
467         }
468
469         Ok(())
470     }
471
472     fn validate_ptr(
473         &mut self,
474         val: Value,
475         abs_place: AbsPlace<'tcx>,
476         pointee_ty: Ty<'tcx>,
477         re: Option<region::Scope>,
478         mutbl: Mutability,
479         mode: ValidationMode,
480     ) -> EvalResult<'tcx> {
481         // Check alignment and non-NULLness
482         let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
483         let ptr = self.into_ptr(val)?.unwrap_or_err()?;
484         self.memory.check_align(ptr, align)?;
485
486         // Recurse
487         let pointee_place = self.val_to_place(val, pointee_ty)?;
488         self.validate(
489             ValidationQuery {
490                 place: (abs_place.deref(), pointee_place),
491                 ty: pointee_ty,
492                 re,
493                 mutbl,
494             },
495             mode,
496         )
497     }
498
499     /// Validate the place at the given type. If `acquire` is false, just do a release of all write locks
500     fn validate(
501         &mut self,
502         mut query: ValidationQuery<'tcx>,
503         mode: ValidationMode,
504     ) -> EvalResult<'tcx> {
505         use rustc::ty::TypeVariants::*;
506         use rustc::ty::RegionKind::*;
507         use rustc::ty::AdtKind;
508
509         // No point releasing shared stuff.
510         if !mode.acquiring() && query.mutbl == MutImmutable {
511             return Ok(());
512         }
513         // When we recover, we may see data whose validity *just* ended.  Do not acquire it.
514         if let ValidationMode::Recover(ending_ce) = mode {
515             if query.re == Some(ending_ce) {
516                 return Ok(());
517             }
518         }
519
520         query.ty = self.normalize_type_unerased(&query.ty);
521         trace!("{:?} on {:#?}", mode, query);
522         trace!("{:#?}", query.ty.sty);
523
524         // Decide whether this type *owns* the memory it covers (like integers), or whether it
525         // just assembles pieces (that each own their memory) together to a larger whole.
526         // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
527         let is_owning = match query.ty.sty {
528             TyInt(_) | TyUint(_) | TyRawPtr(_) | TyBool | TyFloat(_) | TyChar | TyStr |
529             TyRef(..) | TyFnPtr(..) | TyFnDef(..) | TyNever => true,
530             TyAdt(adt, _) if adt.is_box() => true,
531             TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) |
532             TyDynamic(..) | TyGenerator(..) | TyForeign(_) => false,
533             TyGeneratorWitness(..) => unreachable!("TyGeneratorWitness in validate"),
534             TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => {
535                 bug!("I got an incomplete/unnormalized type for validation")
536             }
537         };
538         if is_owning {
539             // We need to lock.  So we need memory.  So we have to force_acquire.
540             // Tracking the same state for locals not backed by memory would just duplicate too
541             // much machinery.
542             // FIXME: We ignore alignment.
543             let (ptr, _, extra) = self.force_allocation(query.place.1)?.to_ptr_align_extra();
544             // Determine the size
545             // FIXME: Can we reuse size_and_align_of_dst for Places?
546             let layout = self.layout_of(query.ty)?;
547             let len = if !layout.is_unsized() {
548                 assert_eq!(extra, PlaceExtra::None, "Got a fat ptr to a sized type");
549                 layout.size.bytes()
550             } else {
551                 // The only unsized typ we concider "owning" is TyStr.
552                 assert_eq!(
553                     query.ty.sty,
554                     TyStr,
555                     "Found a surprising unsized owning type"
556                 );
557                 // The extra must be the length, in bytes.
558                 match extra {
559                     PlaceExtra::Length(len) => len,
560                     _ => bug!("TyStr must have a length as extra"),
561                 }
562             };
563             // Handle locking
564             if len > 0 {
565                 let ptr = ptr.unwrap_or_err()?.to_ptr()?;
566                 match query.mutbl {
567                     MutImmutable => {
568                         if mode.acquiring() {
569                             self.memory.acquire_lock(
570                                 ptr,
571                                 len,
572                                 query.re,
573                                 AccessKind::Read,
574                             )?;
575                         }
576                     }
577                     // No releasing of read locks, ever.
578                     MutMutable => {
579                         match mode {
580                             ValidationMode::Acquire => {
581                                 self.memory.acquire_lock(
582                                     ptr,
583                                     len,
584                                     query.re,
585                                     AccessKind::Write,
586                                 )?
587                             }
588                             ValidationMode::Recover(ending_ce) => {
589                                 self.memory.recover_write_lock(
590                                     ptr,
591                                     len,
592                                     &query.place.0,
593                                     query.re,
594                                     ending_ce,
595                                 )?
596                             }
597                             ValidationMode::ReleaseUntil(suspended_ce) => {
598                                 self.memory.suspend_write_lock(
599                                     ptr,
600                                     len,
601                                     &query.place.0,
602                                     suspended_ce,
603                                 )?
604                             }
605                         }
606                     }
607                 }
608             }
609         }
610
611         let res: EvalResult<'tcx> = do catch {
612             match query.ty.sty {
613                 TyInt(_) | TyUint(_) | TyRawPtr(_) => {
614                     if mode.acquiring() {
615                         // Make sure we can read this.
616                         let val = self.read_place(query.place.1)?;
617                         self.follow_by_ref_value(val, query.ty)?;
618                         // FIXME: It would be great to rule out Undef here, but that doesn't actually work.
619                         // Passing around undef data is a thing that e.g. Vec::extend_with does.
620                     }
621                 }
622                 TyBool | TyFloat(_) | TyChar => {
623                     if mode.acquiring() {
624                         let val = self.read_place(query.place.1)?;
625                         let val = self.value_to_scalar(ValTy { value: val, ty: query.ty })?;
626                         val.to_bytes()?;
627                         // TODO: Check if these are valid bool/float/codepoint/UTF-8
628                     }
629                 }
630                 TyNever => return err!(ValidationFailure(format!("The empty type is never valid."))),
631                 TyRef(region, pointee_ty, mutbl) => {
632                     let val = self.read_place(query.place.1)?;
633                     // Sharing restricts our context
634                     if mutbl == MutImmutable {
635                         query.mutbl = MutImmutable;
636                     }
637                     // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
638                     // we record the region of this borrow to the context.
639                     if query.re == None {
640                         if let ReScope(scope) = *region {
641                             query.re = Some(scope);
642                         }
643                         // It is possible for us to encounter erased lifetimes here because the lifetimes in
644                         // this functions' Subst will be erased.
645                     }
646                     self.validate_ptr(val, query.place.0, pointee_ty, query.re, query.mutbl, mode)?;
647                 }
648                 TyAdt(adt, _) if adt.is_box() => {
649                     let val = self.read_place(query.place.1)?;
650                     self.validate_ptr(val, query.place.0, query.ty.boxed_ty(), query.re, query.mutbl, mode)?;
651                 }
652                 TyFnPtr(_sig) => {
653                     let ptr = self.read_place(query.place.1)?;
654                     let ptr = self.into_ptr(ptr)?.unwrap_or_err()?.to_ptr()?;
655                     self.memory.get_fn(ptr)?;
656                     // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
657                 }
658                 TyFnDef(..) => {
659                     // This is a zero-sized type with all relevant data sitting in the type.
660                     // There is nothing to validate.
661                 }
662
663                 // Compound types
664                 TyStr => {
665                     // TODO: Validate strings
666                 }
667                 TySlice(elem_ty) => {
668                     let len = match query.place.1 {
669                         Place::Ptr { extra: PlaceExtra::Length(len), .. } => len,
670                         _ => {
671                             bug!(
672                                 "acquire_valid of a TySlice given non-slice place: {:?}",
673                                 query.place
674                             )
675                         }
676                     };
677                     for i in 0..len {
678                         let inner_place = self.place_index(query.place.1, query.ty, i)?;
679                         self.validate(
680                             ValidationQuery {
681                                 place: (query.place.0.clone().index(i), inner_place),
682                                 ty: elem_ty,
683                                 ..query
684                             },
685                             mode,
686                         )?;
687                     }
688                 }
689                 TyArray(elem_ty, len) => {
690                     let len = match len.val {
691                         ConstValue::Unevaluated(def_id, substs) => {
692                             self.tcx.const_eval(self.tcx.param_env(def_id).and(GlobalId {
693                                 instance: Instance::new(def_id, substs),
694                                 promoted: None,
695                             }))
696                                 .map_err(|_err|EvalErrorKind::MachineError("<already reported>".to_string()))?
697                         }
698                         _ => len,
699                     };
700                     let len = len.unwrap_usize(self.tcx.tcx);
701                     for i in 0..len {
702                         let inner_place = self.place_index(query.place.1, query.ty, i as u64)?;
703                         self.validate(
704                             ValidationQuery {
705                                 place: (query.place.0.clone().index(i as u64), inner_place),
706                                 ty: elem_ty,
707                                 ..query
708                             },
709                             mode,
710                         )?;
711                     }
712                 }
713                 TyDynamic(_data, _region) => {
714                     // Check that this is a valid vtable
715                     let vtable = match query.place.1 {
716                         Place::Ptr { extra: PlaceExtra::Vtable(vtable), .. } => vtable,
717                         _ => {
718                             bug!(
719                                 "acquire_valid of a TyDynamic given non-trait-object place: {:?}",
720                                 query.place
721                             )
722                         }
723                     };
724                     self.read_size_and_align_from_vtable(vtable)?;
725                     // TODO: Check that the vtable contains all the function pointers we expect it to have.
726                     // Trait objects cannot have any operations performed
727                     // on them directly.  We cannot, in general, even acquire any locks as the trait object *could*
728                     // contain an UnsafeCell.  If we call functions to get access to data, we will validate
729                     // their return values.  So, it doesn't seem like there's anything else to do.
730                 }
731                 TyAdt(adt, _) => {
732                     if Some(adt.did) == self.tcx.tcx.lang_items().unsafe_cell_type() &&
733                         query.mutbl == MutImmutable
734                     {
735                         // No locks for shared unsafe cells.  Also no other validation, the only field is private anyway.
736                         return Ok(());
737                     }
738
739                     match adt.adt_kind() {
740                         AdtKind::Enum => {
741                             let layout = self.layout_of(query.ty)?;
742                             let variant_idx = self.read_discriminant_as_variant_index(query.place.1, layout)?;
743                             let variant = &adt.variants[variant_idx];
744
745                             if !variant.fields.is_empty() {
746                                 // Downcast to this variant, if needed
747                                 let place = if adt.is_enum() {
748                                     (
749                                         query.place.0.downcast(adt, variant_idx),
750                                         self.eval_place_projection(
751                                             query.place.1,
752                                             query.ty,
753                                             &mir::ProjectionElem::Downcast(adt, variant_idx),
754                                         )?,
755                                     )
756                                 } else {
757                                     query.place
758                                 };
759
760                                 // Recursively validate the fields
761                                 self.validate_fields(
762                                     ValidationQuery { place, ..query },
763                                     mode,
764                                 )?;
765                             } else {
766                                 // No fields, nothing left to check.  Downcasting may fail, e.g. in case of a CEnum.
767                             }
768                         }
769                         AdtKind::Struct => {
770                             self.validate_fields(query, mode)?;
771                         }
772                         AdtKind::Union => {
773                             // No guarantees are provided for union types.
774                             // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
775                         }
776                     }
777                 }
778                 TyTuple(..) |
779                 TyClosure(..) => {
780                     // TODO: Check if the signature matches for `TyClosure`
781                     // (should be the same check as what terminator/mod.rs already does on call?).
782                     // Is there other things we can/should check?  Like vtable pointers?
783                     self.validate_fields(query, mode)?;
784                 }
785                 // FIXME: generators aren't validated right now
786                 TyGenerator(..) => {},
787                 _ => bug!("We already established that this is a type we support. ({})", query.ty),
788             }
789         };
790         match res {
791             // ReleaseUntil(None) of an uninitalized variable is a NOP.  This is needed because
792             // we have to release the return value of a function; due to destination-passing-style
793             // the callee may directly write there.
794             // TODO: Ideally we would know whether the destination is already initialized, and only
795             // release if it is.  But of course that can't even always be statically determined.
796             Err(EvalError { kind: EvalErrorKind::ReadUndefBytes, .. })
797                 if mode == ValidationMode::ReleaseUntil(None) => {
798                 Ok(())
799             }
800             res => res,
801         }
802     }
803 }