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;
15 use super::{EvalContext, Place, PlaceExtra, ValTy, ScalarExt};
16 use rustc::mir::interpret::{DynamicLifetime, AccessKind, EvalErrorKind, Value, EvalError, EvalResult};
19 pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsPlace<'tcx>, Place)>;
21 #[derive(Copy, Clone, Debug, PartialEq)]
22 pub(crate) enum ValidationMode {
24 /// Recover because the given region ended
25 Recover(region::Scope),
26 ReleaseUntil(Option<region::Scope>),
30 fn acquiring(self) -> bool {
31 use self::ValidationMode::*;
33 Acquire | Recover(_) => true,
34 ReleaseUntil(_) => false,
40 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
41 pub enum AbsPlace<'tcx> {
43 Static(hir::def_id::DefId),
44 Projection(Box<AbsPlaceProjection<'tcx>>),
47 type AbsPlaceProjection<'tcx> = mir::Projection<'tcx, AbsPlace<'tcx>, u64, ()>;
48 type AbsPlaceElem<'tcx> = mir::ProjectionElem<'tcx, u64, ()>;
50 impl<'tcx> AbsPlace<'tcx> {
51 pub fn field(self, f: mir::Field) -> AbsPlace<'tcx> {
52 self.elem(mir::ProjectionElem::Field(f, ()))
55 pub fn deref(self) -> AbsPlace<'tcx> {
56 self.elem(mir::ProjectionElem::Deref)
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))
63 pub fn index(self, index: u64) -> AbsPlace<'tcx> {
64 self.elem(mir::ProjectionElem::Index(index))
67 fn elem(self, elem: AbsPlaceElem<'tcx>) -> AbsPlace<'tcx> {
68 AbsPlace::Projection(Box::new(AbsPlaceProjection {
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>>;
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(
88 layout: ty::layout::TyLayout<'tcx>,
90 ) -> EvalResult<'tcx, Ty<'tcx>>;
93 query: ValidationQuery<'tcx>,
95 ) -> EvalResult<'tcx>;
99 abs_place: AbsPlace<'tcx>,
100 pointee_ty: Ty<'tcx>,
101 re: Option<region::Scope>,
103 mode: ValidationMode,
104 ) -> EvalResult<'tcx>;
107 query: ValidationQuery<'tcx>,
108 mode: ValidationMode,
109 ) -> EvalResult<'tcx>;
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::*;
116 let elem = match proj.elem {
118 Field(f, _) => Field(f, ()),
120 let value = self.frame().get_local(v)?;
121 let ty = self.tcx.tcx.types.usize;
122 let n = self.value_to_scalar(ValTy { value, ty })?.to_usize(self)?;
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),
131 Ok(AbsPlaceProjection {
132 base: self.abstract_place(&proj.base)?,
137 fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>> {
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)?)),
150 operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
151 ) -> EvalResult<'tcx> {
152 // If mir-emit-validate is set to 0 (i.e., disabled), we may still see validation commands
153 // because other crates may have been compiled with mir-emit-validate > 0. Ignore those
154 // commands. This makes mir-emit-validate also a flag to control whether miri will do
155 // validation or not.
156 if self.tcx.tcx.sess.opts.debugging_opts.mir_emit_validate == 0 {
159 debug_assert!(self.memory.cur_frame == self.cur_frame());
161 // HACK: Determine if this method is whitelisted and hence we do not perform any validation.
162 // We currently insta-UB on anything passing around uninitialized memory, so we have to whitelist
163 // the places that are allowed to do that.
164 // The second group is stuff libstd does that is forbidden even under relaxed validation.
166 // The regexp we use for filtering
169 static ref RE: Regex = Regex::new("^(\
170 (std|alloc::heap::__core)::mem::(uninitialized|forget)::|\
171 <(std|alloc)::heap::Heap as (std::heap|alloc::allocator)::Alloc>::|\
172 <(std|alloc::heap::__core)::mem::ManuallyDrop<T>><.*>::new$|\
173 <(std|alloc::heap::__core)::mem::ManuallyDrop<T> as std::ops::DerefMut><.*>::deref_mut$|\
174 (std|alloc::heap::__core)::ptr::read::|\
176 <std::sync::Arc<T>><.*>::inner$|\
177 <std::sync::Arc<T>><.*>::drop_slow$|\
178 (std::heap|alloc::allocator)::Layout::for_value::|\
179 (std|alloc::heap::__core)::mem::(size|align)_of_val::\
183 let name = self.frame().instance.to_string();
184 if RE.is_match(&name) {
189 // We need to monomorphize ty *without* erasing lifetimes
190 trace!("validation_op1: {:?}", operand.ty.sty);
191 let ty = operand.ty.subst(self.tcx.tcx, self.substs());
192 trace!("validation_op2: {:?}", operand.ty.sty);
193 let place = self.eval_place(&operand.place)?;
194 let abs_place = self.abstract_place(&operand.place)?;
195 let query = ValidationQuery {
196 place: (abs_place, place),
199 mutbl: operand.mutbl,
202 // Check the mode, and also perform mode-specific operations
203 let mode = match op {
204 ValidationOp::Acquire => ValidationMode::Acquire,
205 ValidationOp::Release => ValidationMode::ReleaseUntil(None),
206 ValidationOp::Suspend(scope) => {
207 if query.mutbl == MutMutable {
208 let lft = DynamicLifetime {
209 frame: self.cur_frame(),
210 region: Some(scope), // Notably, we only ever suspend things for given regions.
211 // Suspending for the entire function does not make any sense.
213 trace!("Suspending {:?} until {:?}", query, scope);
214 self.machine.suspended.entry(lft).or_insert_with(Vec::new).push(
218 ValidationMode::ReleaseUntil(Some(scope))
221 self.validate(query, mode)
224 /// Release locks and executes suspensions of the given region (or the entire fn, in case of None).
225 fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx> {
226 debug_assert!(self.memory.cur_frame == self.cur_frame());
227 self.memory.locks_lifetime_ended(scope);
230 // Recover suspended places
231 let lft = DynamicLifetime {
232 frame: self.cur_frame(),
235 if let Some(queries) = self.machine.suspended.remove(&lft) {
236 for query in queries {
237 trace!("Recovering {:?} from suspension", query);
238 self.validate(query, ValidationMode::Recover(scope))?;
243 // Clean suspension table of current frame
244 let cur_frame = self.cur_frame();
245 self.machine.suspended.retain(|lft, _| {
246 lft.frame != cur_frame // keep only what is in the other (lower) frames
253 fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
254 return normalize_associated_type(self.tcx.tcx, &ty);
256 use syntax::codemap::{Span, DUMMY_SP};
258 // We copy a bunch of stuff from rustc/infer/mod.rs to be able to tweak its behavior
259 fn normalize_projections_in<'a, 'gcx, 'tcx, T>(
260 self_: &InferCtxt<'a, 'gcx, 'tcx>,
261 param_env: ty::ParamEnv<'tcx>,
265 T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
267 let mut selcx = traits::SelectionContext::new(self_);
268 let cause = traits::ObligationCause::dummy();
269 let traits::Normalized {
272 } = traits::normalize(&mut selcx, param_env, cause, value);
274 let mut fulfill_cx = traits::FulfillmentContext::new();
276 for obligation in obligations {
277 fulfill_cx.register_predicate_obligation(self_, obligation);
280 drain_fulfillment_cx_or_panic(self_, DUMMY_SP, &mut fulfill_cx, &result)
283 fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
284 self_: &InferCtxt<'a, 'gcx, 'tcx>,
286 fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
290 T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
292 // In principle, we only need to do this so long as `result`
293 // contains unbound type parameters. It could be a slight
294 // optimization to stop iterating early.
295 match fulfill_cx.select_all_or_error(self_) {
300 "Encountered errors `{:?}` resolving bounds after type-checking",
306 let result = self_.resolve_type_vars_if_possible(result);
307 let result = self_.tcx.fold_regions(
311 ty::ReVar(_) => self_.tcx.types.re_erased,
316 match self_.tcx.lift_to_global(&result) {
317 Some(result) => result,
319 span_bug!(span, "Uninferred types/regions in `{:?}`", result);
324 trait MyTransNormalize<'gcx>: TypeFoldable<'gcx> {
325 fn my_trans_normalize<'a, 'tcx>(
327 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
328 param_env: ty::ParamEnv<'tcx>,
332 macro_rules! items { ($($item:item)+) => ($($item)+) }
333 macro_rules! impl_trans_normalize {
334 ($lt_gcx:tt, $($ty:ty),+) => {
335 items!($(impl<$lt_gcx> MyTransNormalize<$lt_gcx> for $ty {
336 fn my_trans_normalize<'a, 'tcx>(&self,
337 infcx: &InferCtxt<'a, $lt_gcx, 'tcx>,
338 param_env: ty::ParamEnv<'tcx>)
340 normalize_projections_in(infcx, param_env, self)
346 impl_trans_normalize!('gcx,
351 ty::ClosureSubsts<'gcx>,
352 ty::PolyTraitRef<'gcx>,
353 ty::ExistentialTraitRef<'gcx>
356 fn normalize_associated_type<'a, 'tcx, T>(self_: TyCtxt<'a, 'tcx, 'tcx>, value: &T) -> T
358 T: MyTransNormalize<'tcx>,
360 let param_env = ty::ParamEnv::reveal_all();
362 if !value.has_projections() {
363 return value.clone();
366 self_.infer_ctxt().enter(|infcx| {
367 value.my_trans_normalize(&infcx, param_env)
372 // This is a copy of `Layout::field`
374 // FIXME: remove once validation does not depend on lifetimes
375 fn field_with_lifetimes(
378 mut layout: ty::layout::TyLayout<'tcx>,
380 ) -> EvalResult<'tcx, Ty<'tcx>> {
381 if let Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } = base {
382 layout = layout.for_variant(&self, variant_index);
384 let tcx = self.tcx.tcx;
385 Ok(match layout.ty.sty {
394 ty::TyGeneratorWitness(..) |
396 ty::TyForeign(..) => {
397 bug!("TyLayout::field_type({:?}): not applicable", layout)
400 // Potentially-fat pointers.
401 ty::TyRef(_, pointee, _) |
402 ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
405 // Reuse the fat *T type as its own thin pointer data field.
406 // This provides information about e.g. DST struct pointees
407 // (which may have no non-DST form), and will work as long
408 // as the `Abi` or `FieldPlacement` is checked by users.
410 return Ok(layout.ty);
413 match tcx.struct_tail(pointee).sty {
415 ty::TyStr => tcx.types.usize,
416 ty::TyDynamic(..) => {
417 // FIXME(eddyb) use an usize/fn() array with
418 // the correct number of vtables slots.
419 tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil())
421 _ => bug!("TyLayout::field_type({:?}): not applicable", layout)
425 // Arrays and slices.
426 ty::TyArray(element, _) |
427 ty::TySlice(element) => element,
428 ty::TyStr => tcx.types.u8,
430 // Tuples, generators and closures.
431 ty::TyClosure(def_id, ref substs) => {
432 substs.upvar_tys(def_id, tcx).nth(i).unwrap()
435 ty::TyGenerator(def_id, ref substs, _) => {
436 substs.field_tys(def_id, tcx).nth(i).unwrap()
439 ty::TyTuple(tys) => tys[i],
441 // SIMD vector types.
442 ty::TyAdt(def, ..) if def.repr.simd() => {
443 layout.ty.simd_type(tcx)
447 ty::TyAdt(def, substs) => {
448 use rustc::ty::layout::Variants;
449 match layout.variants {
450 Variants::Single { index } => {
451 def.variants[index].fields[i].ty(tcx, substs)
454 // Discriminant field for enums (where applicable).
455 Variants::Tagged { tag: ref discr, .. } |
456 Variants::NicheFilling { niche: ref discr, .. } => {
458 return Ok(discr.value.to_ty(tcx))
463 ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
464 ty::TyInfer(_) | ty::TyError => {
465 bug!("TyLayout::field_type: unexpected type `{}`", layout.ty)
472 query: ValidationQuery<'tcx>,
473 mode: ValidationMode,
474 ) -> EvalResult<'tcx> {
475 let mut layout = self.layout_of(query.ty)?;
476 layout.ty = query.ty;
478 // TODO: Maybe take visibility/privacy into account.
479 for idx in 0..layout.fields.count() {
480 let field = mir::Field::new(idx);
481 let (field_place, field_layout) =
482 self.place_field(query.place.1, field, layout)?;
483 // layout stuff erases lifetimes, get the field ourselves
484 let field_ty = self.field_with_lifetimes(query.place.1, layout, idx)?;
485 trace!("assuming \n{:?}\n == \n{:?}\n except for lifetimes", field_layout.ty, field_ty);
488 place: (query.place.0.clone().field(field), field_place),
502 abs_place: AbsPlace<'tcx>,
503 pointee_ty: Ty<'tcx>,
504 re: Option<region::Scope>,
506 mode: ValidationMode,
507 ) -> EvalResult<'tcx> {
508 // Check alignment and non-NULLness
509 let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
510 let ptr = self.into_ptr(val)?;
511 self.memory.check_align(ptr, align)?;
514 let pointee_place = self.val_to_place(val, pointee_ty)?;
517 place: (abs_place.deref(), pointee_place),
526 /// Validate the place at the given type. If `acquire` is false, just do a release of all write locks
529 mut query: ValidationQuery<'tcx>,
530 mode: ValidationMode,
531 ) -> EvalResult<'tcx> {
532 use rustc::ty::TypeVariants::*;
533 use rustc::ty::RegionKind::*;
534 use rustc::ty::AdtKind;
536 // No point releasing shared stuff.
537 if !mode.acquiring() && query.mutbl == MutImmutable {
540 // When we recover, we may see data whose validity *just* ended. Do not acquire it.
541 if let ValidationMode::Recover(ending_ce) = mode {
542 if query.re == Some(ending_ce) {
547 query.ty = self.normalize_type_unerased(&query.ty);
548 trace!("{:?} on {:#?}", mode, query);
549 trace!("{:#?}", query.ty.sty);
551 // Decide whether this type *owns* the memory it covers (like integers), or whether it
552 // just assembles pieces (that each own their memory) together to a larger whole.
553 // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
554 let is_owning = match query.ty.sty {
555 TyInt(_) | TyUint(_) | TyRawPtr(_) | TyBool | TyFloat(_) | TyChar | TyStr |
556 TyRef(..) | TyFnPtr(..) | TyFnDef(..) | TyNever => true,
557 TyAdt(adt, _) if adt.is_box() => true,
558 TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) |
559 TyDynamic(..) | TyGenerator(..) | TyForeign(_) => false,
560 TyGeneratorWitness(..) => unreachable!("TyGeneratorWitness in validate"),
561 TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => {
562 bug!("I got an incomplete/unnormalized type for validation")
566 // We need to lock. So we need memory. So we have to force_acquire.
567 // Tracking the same state for locals not backed by memory would just duplicate too
569 // FIXME: We ignore alignment.
570 let (ptr, _, extra) = self.force_allocation(query.place.1)?.to_ptr_align_extra();
571 // Determine the size
572 // FIXME: Can we reuse size_and_align_of_dst for Places?
573 let layout = self.layout_of(query.ty)?;
574 let len = if !layout.is_unsized() {
575 assert_eq!(extra, PlaceExtra::None, "Got a fat ptr to a sized type");
578 // The only unsized typ we concider "owning" is TyStr.
582 "Found a surprising unsized owning type"
584 // The extra must be the length, in bytes.
586 PlaceExtra::Length(len) => len,
587 _ => bug!("TyStr must have a length as extra"),
592 let ptr = ptr.to_ptr()?;
595 if mode.acquiring() {
596 self.memory.acquire_lock(
604 // No releasing of read locks, ever.
607 ValidationMode::Acquire => {
608 self.memory.acquire_lock(
615 ValidationMode::Recover(ending_ce) => {
616 self.memory.recover_write_lock(
624 ValidationMode::ReleaseUntil(suspended_ce) => {
625 self.memory.suspend_write_lock(
638 let res: EvalResult<'tcx> = do catch {
640 TyInt(_) | TyUint(_) | TyRawPtr(_) => {
641 if mode.acquiring() {
642 // Make sure we can read this.
643 let val = self.read_place(query.place.1)?;
644 self.follow_by_ref_value(val, query.ty)?;
645 // FIXME: It would be great to rule out Undef here, but that doesn't actually work.
646 // Passing around undef data is a thing that e.g. Vec::extend_with does.
649 TyBool | TyFloat(_) | TyChar => {
650 if mode.acquiring() {
651 let val = self.read_place(query.place.1)?;
652 let val = self.value_to_scalar(ValTy { value: val, ty: query.ty })?;
654 // TODO: Check if these are valid bool/float/codepoint/UTF-8
657 TyNever => return err!(ValidationFailure(format!("The empty type is never valid."))),
658 TyRef(region, pointee_ty, mutbl) => {
659 let val = self.read_place(query.place.1)?;
660 // Sharing restricts our context
661 if mutbl == MutImmutable {
662 query.mutbl = MutImmutable;
664 // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
665 // we record the region of this borrow to the context.
666 if query.re == None {
667 if let ReScope(scope) = *region {
668 query.re = Some(scope);
670 // It is possible for us to encounter erased lifetimes here because the lifetimes in
671 // this functions' Subst will be erased.
673 self.validate_ptr(val, query.place.0, pointee_ty, query.re, query.mutbl, mode)?;
675 TyAdt(adt, _) if adt.is_box() => {
676 let val = self.read_place(query.place.1)?;
677 self.validate_ptr(val, query.place.0, query.ty.boxed_ty(), query.re, query.mutbl, mode)?;
680 let ptr = self.read_place(query.place.1)?;
681 let ptr = self.into_ptr(ptr)?.to_ptr()?;
682 self.memory.get_fn(ptr)?;
683 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
686 // This is a zero-sized type with all relevant data sitting in the type.
687 // There is nothing to validate.
692 // TODO: Validate strings
694 TySlice(elem_ty) => {
695 let len = match query.place.1 {
696 Place::Ptr { extra: PlaceExtra::Length(len), .. } => len,
699 "acquire_valid of a TySlice given non-slice place: {:?}",
705 let inner_place = self.place_index(query.place.1, query.ty, i)?;
708 place: (query.place.0.clone().index(i), inner_place),
716 TyArray(elem_ty, len) => {
717 let len = match len.val {
718 ConstValue::Unevaluated(def_id, substs) => {
719 self.tcx.const_eval(self.tcx.param_env(def_id).and(GlobalId {
720 instance: Instance::new(def_id, substs),
723 .map_err(|_err|EvalErrorKind::MachineError("<already reported>".to_string()))?
727 let len = len.unwrap_usize(self.tcx.tcx);
729 let inner_place = self.place_index(query.place.1, query.ty, i as u64)?;
732 place: (query.place.0.clone().index(i as u64), inner_place),
740 TyDynamic(_data, _region) => {
741 // Check that this is a valid vtable
742 let vtable = match query.place.1 {
743 Place::Ptr { extra: PlaceExtra::Vtable(vtable), .. } => vtable,
746 "acquire_valid of a TyDynamic given non-trait-object place: {:?}",
751 self.read_size_and_align_from_vtable(vtable)?;
752 // TODO: Check that the vtable contains all the function pointers we expect it to have.
753 // Trait objects cannot have any operations performed
754 // on them directly. We cannot, in general, even acquire any locks as the trait object *could*
755 // contain an UnsafeCell. If we call functions to get access to data, we will validate
756 // their return values. So, it doesn't seem like there's anything else to do.
759 if Some(adt.did) == self.tcx.tcx.lang_items().unsafe_cell_type() &&
760 query.mutbl == MutImmutable
762 // No locks for shared unsafe cells. Also no other validation, the only field is private anyway.
766 match adt.adt_kind() {
768 let variant_idx = self.read_discriminant_as_variant_index(query.place.1, query.ty)?;
769 let variant = &adt.variants[variant_idx];
771 if !variant.fields.is_empty() {
772 // Downcast to this variant, if needed
773 let place = if adt.is_enum() {
775 query.place.0.downcast(adt, variant_idx),
776 self.eval_place_projection(
779 &mir::ProjectionElem::Downcast(adt, variant_idx),
786 // Recursively validate the fields
787 self.validate_fields(
788 ValidationQuery { place, ..query },
792 // No fields, nothing left to check. Downcasting may fail, e.g. in case of a CEnum.
796 self.validate_fields(query, mode)?;
799 // No guarantees are provided for union types.
800 // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
806 // TODO: Check if the signature matches for `TyClosure`
807 // (should be the same check as what terminator/mod.rs already does on call?).
808 // Is there other things we can/should check? Like vtable pointers?
809 self.validate_fields(query, mode)?;
811 // FIXME: generators aren't validated right now
812 TyGenerator(..) => {},
813 _ => bug!("We already established that this is a type we support. ({})", query.ty),
817 // ReleaseUntil(None) of an uninitalized variable is a NOP. This is needed because
818 // we have to release the return value of a function; due to destination-passing-style
819 // the callee may directly write there.
820 // TODO: Ideally we would know whether the destination is already initialized, and only
821 // release if it is. But of course that can't even always be statically determined.
822 Err(EvalError { kind: EvalErrorKind::ReadUndefBytes, .. })
823 if mode == ValidationMode::ReleaseUntil(None) => {