-use rustc::hir::{self, Mutability};
-use rustc::hir::Mutability::*;
-use rustc::mir::{self, ValidationOp, ValidationOperand};
-use rustc::mir::interpret::GlobalId;
-use rustc::ty::{self, Ty, TypeFoldable, TyCtxt, Instance};
-use rustc::ty::layout::{LayoutOf, PrimitiveExt};
-use rustc::ty::subst::{Substs, Subst};
-use rustc::traits::{self, TraitEngine};
-use rustc::infer::InferCtxt;
-use rustc::middle::region;
-use rustc::mir::interpret::{ConstValue};
-use rustc_data_structures::indexed_vec::Idx;
-use rustc_mir::interpret::HasMemory;
-
-use super::{EvalContext, Place, PlaceExtra, ValTy, ScalarExt};
-use rustc::mir::interpret::{DynamicLifetime, AccessKind, EvalErrorKind, Value, EvalError, EvalResult};
-use locks::MemoryExt;
-
-pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsPlace<'tcx>, Place)>;
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub(crate) enum ValidationMode {
- Acquire,
- /// Recover because the given region ended
- Recover(region::Scope),
- ReleaseUntil(Option<region::Scope>),
-}
-
-impl ValidationMode {
- fn acquiring(self) -> bool {
- use self::ValidationMode::*;
- match self {
- Acquire | Recover(_) => true,
- ReleaseUntil(_) => false,
- }
- }
-}
-
-// Abstract places
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub enum AbsPlace<'tcx> {
- Local(mir::Local),
- Static(hir::def_id::DefId),
- Projection(Box<AbsPlaceProjection<'tcx>>),
-}
-
-type AbsPlaceProjection<'tcx> = mir::Projection<'tcx, AbsPlace<'tcx>, u64, ()>;
-type AbsPlaceElem<'tcx> = mir::ProjectionElem<'tcx, u64, ()>;
-
-impl<'tcx> AbsPlace<'tcx> {
- pub fn field(self, f: mir::Field) -> AbsPlace<'tcx> {
- self.elem(mir::ProjectionElem::Field(f, ()))
- }
-
- pub fn deref(self) -> AbsPlace<'tcx> {
- self.elem(mir::ProjectionElem::Deref)
- }
-
- pub fn downcast(self, adt_def: &'tcx ty::AdtDef, variant_index: usize) -> AbsPlace<'tcx> {
- self.elem(mir::ProjectionElem::Downcast(adt_def, variant_index))
- }
-
- pub fn index(self, index: u64) -> AbsPlace<'tcx> {
- self.elem(mir::ProjectionElem::Index(index))
- }
-
- fn elem(self, elem: AbsPlaceElem<'tcx>) -> AbsPlace<'tcx> {
- AbsPlace::Projection(Box::new(AbsPlaceProjection {
- base: self,
- elem,
- }))
- }
-}
-
-pub(crate) trait EvalContextExt<'tcx> {
- fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>>;
- fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>>;
- fn validation_op(
- &mut self,
- op: ValidationOp,
- operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
- ) -> EvalResult<'tcx>;
- fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx>;
- fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx>;
- fn field_with_lifetimes(
- &mut self,
- base: Place,
- layout: ty::layout::TyLayout<'tcx>,
- i: usize,
- ) -> EvalResult<'tcx, Ty<'tcx>>;
- fn validate_fields(
- &mut self,
- query: ValidationQuery<'tcx>,
- mode: ValidationMode,
- ) -> EvalResult<'tcx>;
- fn validate_ptr(
- &mut self,
- val: Value,
- abs_place: AbsPlace<'tcx>,
- pointee_ty: Ty<'tcx>,
- re: Option<region::Scope>,
- mutbl: Mutability,
- mode: ValidationMode,
- ) -> EvalResult<'tcx>;
- fn validate(
- &mut self,
- query: ValidationQuery<'tcx>,
- mode: ValidationMode,
- ) -> EvalResult<'tcx>;
-}
-
-impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
- fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>> {
- use self::mir::ProjectionElem::*;
-
- let elem = match proj.elem {
- Deref => Deref,
- Field(f, _) => Field(f, ()),
- Index(v) => {
- let value = self.frame().locals[v].access()?;
- let ty = self.tcx.tcx.types.usize;
- let n = self.value_to_scalar(ValTy { value, ty })?.to_usize(self)?;
- Index(n)
- },
- ConstantIndex { offset, min_length, from_end } =>
- ConstantIndex { offset, min_length, from_end },
- Subslice { from, to } =>
- Subslice { from, to },
- Downcast(adt, sz) => Downcast(adt, sz),
- };
- Ok(AbsPlaceProjection {
- base: self.abstract_place(&proj.base)?,
- elem
- })
- }
-
- fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>> {
- Ok(match *place {
- mir::Place::Local(l) => AbsPlace::Local(l),
- mir::Place::Static(ref s) => AbsPlace::Static(s.def_id),
- mir::Place::Projection(ref p) =>
- AbsPlace::Projection(Box::new(self.abstract_place_projection(&*p)?)),
- _ => unimplemented!("validation is not currently maintained"),
- })
- }
-
- // Validity checks
- fn validation_op(
- &mut self,
- op: ValidationOp,
- operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
- ) -> EvalResult<'tcx> {
- // If mir-emit-validate is set to 0 (i.e., disabled), we may still see validation commands
- // because other crates may have been compiled with mir-emit-validate > 0. Ignore those
- // commands. This makes mir-emit-validate also a flag to control whether miri will do
- // validation or not.
- if self.tcx.tcx.sess.opts.debugging_opts.mir_emit_validate == 0 {
- return Ok(());
- }
- debug_assert!(self.memory.cur_frame == self.cur_frame());
-
- // We need to monomorphize ty *without* erasing lifetimes
- trace!("validation_op1: {:?}", operand.ty.sty);
- let ty = operand.ty.subst(self.tcx.tcx, self.substs());
- trace!("validation_op2: {:?}", operand.ty.sty);
- let place = self.eval_place(&operand.place)?;
- let abs_place = self.abstract_place(&operand.place)?;
- let query = ValidationQuery {
- place: (abs_place, place),
- ty,
- re: operand.re,
- mutbl: operand.mutbl,
- };
-
- // Check the mode, and also perform mode-specific operations
- let mode = match op {
- ValidationOp::Acquire => ValidationMode::Acquire,
- ValidationOp::Release => ValidationMode::ReleaseUntil(None),
- ValidationOp::Suspend(scope) => {
- if query.mutbl == MutMutable {
- let lft = DynamicLifetime {
- frame: self.cur_frame(),
- region: Some(scope), // Notably, we only ever suspend things for given regions.
- // Suspending for the entire function does not make any sense.
- };
- trace!("Suspending {:?} until {:?}", query, scope);
- self.machine.suspended.entry(lft).or_insert_with(Vec::new).push(
- query.clone(),
- );
- }
- ValidationMode::ReleaseUntil(Some(scope))
- }
- };
- self.validate(query, mode)
- }
-
- /// Release locks and executes suspensions of the given region (or the entire fn, in case of None).
- fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx> {
- debug_assert!(self.memory.cur_frame == self.cur_frame());
- self.memory.locks_lifetime_ended(scope);
- match scope {
- Some(scope) => {
- // Recover suspended places
- let lft = DynamicLifetime {
- frame: self.cur_frame(),
- region: Some(scope),
- };
- if let Some(queries) = self.machine.suspended.remove(&lft) {
- for query in queries {
- trace!("Recovering {:?} from suspension", query);
- self.validate(query, ValidationMode::Recover(scope))?;
- }
- }
- }
- None => {
- // Clean suspension table of current frame
- let cur_frame = self.cur_frame();
- self.machine.suspended.retain(|lft, _| {
- lft.frame != cur_frame // keep only what is in the other (lower) frames
- });
- }
- }
- Ok(())
- }
-
- fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
- return normalize_associated_type(self.tcx.tcx, &ty);
-
- use syntax::codemap::{Span, DUMMY_SP};
-
- // We copy a bunch of stuff from rustc/infer/mod.rs to be able to tweak its behavior
- fn normalize_projections_in<'a, 'gcx, 'tcx, T>(
- self_: &InferCtxt<'a, 'gcx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- value: &T,
- ) -> T::Lifted
- where
- T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
- {
- let mut selcx = traits::SelectionContext::new(self_);
- let cause = traits::ObligationCause::dummy();
- let traits::Normalized {
- value: result,
- obligations,
- } = traits::normalize(&mut selcx, param_env, cause, value);
-
- let mut fulfill_cx = traits::FulfillmentContext::new();
-
- for obligation in obligations {
- fulfill_cx.register_predicate_obligation(self_, obligation);
- }
-
- drain_fulfillment_cx_or_panic(self_, DUMMY_SP, &mut fulfill_cx, &result)
- }
-
- fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
- self_: &InferCtxt<'a, 'gcx, 'tcx>,
- span: Span,
- fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
- result: &T,
- ) -> T::Lifted
- where
- T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
- {
- // In principle, we only need to do this so long as `result`
- // contains unbound type parameters. It could be a slight
- // optimization to stop iterating early.
- match fulfill_cx.select_all_or_error(self_) {
- Ok(()) => { }
- Err(errors) => {
- span_bug!(
- span,
- "Encountered errors `{:?}` resolving bounds after type-checking",
- errors
- );
- }
- }
-
- let result = self_.resolve_type_vars_if_possible(result);
- let result = self_.tcx.fold_regions(
- &result,
- &mut false,
- |r, _| match *r {
- ty::ReVar(_) => self_.tcx.types.re_erased,
- _ => r,
- },
- );
-
- match self_.tcx.lift_to_global(&result) {
- Some(result) => result,
- None => {
- span_bug!(span, "Uninferred types/regions in `{:?}`", result);
- }
- }
- }
-
- trait MyTransNormalize<'gcx>: TypeFoldable<'gcx> {
- fn my_trans_normalize<'a, 'tcx>(
- &self,
- infcx: &InferCtxt<'a, 'gcx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Self;
- }
-
- macro_rules! items { ($($item:item)+) => ($($item)+) }
- macro_rules! impl_trans_normalize {
- ($lt_gcx:tt, $($ty:ty),+) => {
- items!($(impl<$lt_gcx> MyTransNormalize<$lt_gcx> for $ty {
- fn my_trans_normalize<'a, 'tcx>(&self,
- infcx: &InferCtxt<'a, $lt_gcx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>)
- -> Self {
- normalize_projections_in(infcx, param_env, self)
- }
- })+);
- }
- }
-
- impl_trans_normalize!('gcx,
- Ty<'gcx>,
- &'gcx Substs<'gcx>,
- ty::FnSig<'gcx>,
- ty::PolyFnSig<'gcx>,
- ty::ClosureSubsts<'gcx>,
- ty::PolyTraitRef<'gcx>,
- ty::ExistentialTraitRef<'gcx>
- );
-
- fn normalize_associated_type<'a, 'tcx, T>(self_: TyCtxt<'a, 'tcx, 'tcx>, value: &T) -> T
- where
- T: MyTransNormalize<'tcx>,
- {
- let param_env = ty::ParamEnv::reveal_all();
-
- if !value.has_projections() {
- return value.clone();
- }
-
- self_.infer_ctxt().enter(|infcx| {
- value.my_trans_normalize(&infcx, param_env)
- })
- }
- }
-
- // This is a copy of `Layout::field`
- //
- // FIXME: remove once validation does not depend on lifetimes
- fn field_with_lifetimes(
- &mut self,
- base: Place,
- mut layout: ty::layout::TyLayout<'tcx>,
- i: usize,
- ) -> EvalResult<'tcx, Ty<'tcx>> {
- if let Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } = base {
- layout = layout.for_variant(&self, variant_index);
- }
- let tcx = self.tcx.tcx;
- Ok(match layout.ty.sty {
- ty::TyBool |
- ty::TyChar |
- ty::TyInt(_) |
- ty::TyUint(_) |
- ty::TyFloat(_) |
- ty::TyFnPtr(_) |
- ty::TyNever |
- ty::TyFnDef(..) |
- ty::TyGeneratorWitness(..) |
- ty::TyDynamic(..) |
- ty::TyForeign(..) => {
- bug!("TyLayout::field_type({:?}): not applicable", layout)
- }
-
- // Potentially-fat pointers.
- ty::TyRef(_, pointee, _) |
- ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
- assert!(i < 2);
-
- // Reuse the fat *T type as its own thin pointer data field.
- // This provides information about e.g. DST struct pointees
- // (which may have no non-DST form), and will work as long
- // as the `Abi` or `FieldPlacement` is checked by users.
- if i == 0 {
- return Ok(layout.ty);
- }
-
- match tcx.struct_tail(pointee).sty {
- ty::TySlice(_) |
- ty::TyStr => tcx.types.usize,
- ty::TyDynamic(..) => {
- // FIXME(eddyb) use an usize/fn() array with
- // the correct number of vtables slots.
- tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil())
- }
- _ => bug!("TyLayout::field_type({:?}): not applicable", layout)
- }
- }
-
- // Arrays and slices.
- ty::TyArray(element, _) |
- ty::TySlice(element) => element,
- ty::TyStr => tcx.types.u8,
-
- // Tuples, generators and closures.
- ty::TyClosure(def_id, ref substs) => {
- substs.upvar_tys(def_id, tcx).nth(i).unwrap()
- }
-
- ty::TyGenerator(def_id, ref substs, _) => {
- substs.field_tys(def_id, tcx).nth(i).unwrap()
- }
-
- ty::TyTuple(tys) => tys[i],
-
- // SIMD vector types.
- ty::TyAdt(def, ..) if def.repr.simd() => {
- layout.ty.simd_type(tcx)
- }
-
- // ADTs.
- ty::TyAdt(def, substs) => {
- use rustc::ty::layout::Variants;
- match layout.variants {
- Variants::Single { index } => {
- def.variants[index].fields[i].ty(tcx, substs)
- }
-
- // Discriminant field for enums (where applicable).
- Variants::Tagged { tag: ref discr, .. } |
- Variants::NicheFilling { niche: ref discr, .. } => {
- assert_eq!(i, 0);
- return Ok(discr.value.to_ty(tcx))
- }
- }
- }
-
- ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
- ty::TyInfer(_) | ty::TyError => {
- bug!("TyLayout::field_type: unexpected type `{}`", layout.ty)
- }
- })
- }
-
- fn validate_fields(
- &mut self,
- query: ValidationQuery<'tcx>,
- mode: ValidationMode,
- ) -> EvalResult<'tcx> {
- let mut layout = self.layout_of(query.ty)?;
- layout.ty = query.ty;
-
- // TODO: Maybe take visibility/privacy into account.
- for idx in 0..layout.fields.count() {
- let field = mir::Field::new(idx);
- let (field_place, field_layout) =
- self.place_field(query.place.1, field, layout)?;
- // layout stuff erases lifetimes, get the field ourselves
- let field_ty = self.field_with_lifetimes(query.place.1, layout, idx)?;
- trace!("assuming \n{:?}\n == \n{:?}\n except for lifetimes", field_layout.ty, field_ty);
- self.validate(
- ValidationQuery {
- place: (query.place.0.clone().field(field), field_place),
- ty: field_ty,
- ..query
- },
- mode,
- )?;
- }
-
- Ok(())
- }
-
- fn validate_ptr(
- &mut self,
- val: Value,
- abs_place: AbsPlace<'tcx>,
- pointee_ty: Ty<'tcx>,
- re: Option<region::Scope>,
- mutbl: Mutability,
- mode: ValidationMode,
- ) -> EvalResult<'tcx> {
- // Check alignment and non-NULLness
- let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
- let ptr = self.into_ptr(val)?.unwrap_or_err()?;
- self.memory.check_align(ptr, align)?;
-
- // Recurse
- let pointee_place = self.val_to_place(val, pointee_ty)?;
- self.validate(
- ValidationQuery {
- place: (abs_place.deref(), pointee_place),
- ty: pointee_ty,
- re,
- mutbl,
- },
- mode,
- )
- }
-
- /// Validate the place at the given type. If `acquire` is false, just do a release of all write locks
- fn validate(
- &mut self,
- mut query: ValidationQuery<'tcx>,
- mode: ValidationMode,
- ) -> EvalResult<'tcx> {
- use rustc::ty::TypeVariants::*;
- use rustc::ty::RegionKind::*;
- use rustc::ty::AdtKind;
-
- // No point releasing shared stuff.
- if !mode.acquiring() && query.mutbl == MutImmutable {
- return Ok(());
- }
- // When we recover, we may see data whose validity *just* ended. Do not acquire it.
- if let ValidationMode::Recover(ending_ce) = mode {
- if query.re == Some(ending_ce) {
- return Ok(());
- }
- }
-
- query.ty = self.normalize_type_unerased(&query.ty);
- trace!("{:?} on {:#?}", mode, query);
- trace!("{:#?}", query.ty.sty);
-
- // Decide whether this type *owns* the memory it covers (like integers), or whether it
- // just assembles pieces (that each own their memory) together to a larger whole.
- // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
- let is_owning = match query.ty.sty {
- TyInt(_) | TyUint(_) | TyRawPtr(_) | TyBool | TyFloat(_) | TyChar | TyStr |
- TyRef(..) | TyFnPtr(..) | TyFnDef(..) | TyNever => true,
- TyAdt(adt, _) if adt.is_box() => true,
- TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) |
- TyDynamic(..) | TyGenerator(..) | TyForeign(_) => false,
- TyGeneratorWitness(..) => unreachable!("TyGeneratorWitness in validate"),
- TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => {
- bug!("I got an incomplete/unnormalized type for validation")
- }
- };
- if is_owning {
- // We need to lock. So we need memory. So we have to force_acquire.
- // Tracking the same state for locals not backed by memory would just duplicate too
- // much machinery.
- // FIXME: We ignore alignment.
- let (ptr, _, extra) = self.force_allocation(query.place.1)?.to_ptr_align_extra();
- // Determine the size
- // FIXME: Can we reuse size_and_align_of_dst for Places?
- let layout = self.layout_of(query.ty)?;
- let len = if !layout.is_unsized() {
- assert_eq!(extra, PlaceExtra::None, "Got a fat ptr to a sized type");
- layout.size.bytes()
- } else {
- // The only unsized typ we concider "owning" is TyStr.
- assert_eq!(
- query.ty.sty,
- TyStr,
- "Found a surprising unsized owning type"
- );
- // The extra must be the length, in bytes.
- match extra {
- PlaceExtra::Length(len) => len,
- _ => bug!("TyStr must have a length as extra"),
- }
- };
- // Handle locking
- if len > 0 {
- let ptr = ptr.unwrap_or_err()?.to_ptr()?;
- match query.mutbl {
- MutImmutable => {
- if mode.acquiring() {
- self.memory.acquire_lock(
- ptr,
- len,
- query.re,
- AccessKind::Read,
- )?;
- }
- }
- // No releasing of read locks, ever.
- MutMutable => {
- match mode {
- ValidationMode::Acquire => {
- self.memory.acquire_lock(
- ptr,
- len,
- query.re,
- AccessKind::Write,
- )?
- }
- ValidationMode::Recover(ending_ce) => {
- self.memory.recover_write_lock(
- ptr,
- len,
- &query.place.0,
- query.re,
- ending_ce,
- )?
- }
- ValidationMode::ReleaseUntil(suspended_ce) => {
- self.memory.suspend_write_lock(
- ptr,
- len,
- &query.place.0,
- suspended_ce,
- )?
- }
- }
- }
- }
- }
- }
-
- let res: EvalResult<'tcx> = do catch {
- match query.ty.sty {
- TyInt(_) | TyUint(_) | TyRawPtr(_) => {
- if mode.acquiring() {
- // Make sure we can read this.
- let val = self.read_place(query.place.1)?;
- self.follow_by_ref_value(val, query.ty)?;
- // FIXME: It would be great to rule out Undef here, but that doesn't actually work.
- // Passing around undef data is a thing that e.g. Vec::extend_with does.
- }
- }
- TyBool | TyFloat(_) | TyChar => {
- if mode.acquiring() {
- let val = self.read_place(query.place.1)?;
- let val = self.value_to_scalar(ValTy { value: val, ty: query.ty })?;
- val.to_bytes()?;
- // TODO: Check if these are valid bool/float/codepoint/UTF-8
- }
- }
- TyNever => return err!(ValidationFailure(format!("The empty type is never valid."))),
- TyRef(region, pointee_ty, mutbl) => {
- let val = self.read_place(query.place.1)?;
- // Sharing restricts our context
- if mutbl == MutImmutable {
- query.mutbl = MutImmutable;
- }
- // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
- // we record the region of this borrow to the context.
- if query.re == None {
- if let ReScope(scope) = *region {
- query.re = Some(scope);
- }
- // It is possible for us to encounter erased lifetimes here because the lifetimes in
- // this functions' Subst will be erased.
- }
- self.validate_ptr(val, query.place.0, pointee_ty, query.re, query.mutbl, mode)?;
- }
- TyAdt(adt, _) if adt.is_box() => {
- let val = self.read_place(query.place.1)?;
- self.validate_ptr(val, query.place.0, query.ty.boxed_ty(), query.re, query.mutbl, mode)?;
- }
- TyFnPtr(_sig) => {
- let ptr = self.read_place(query.place.1)?;
- let ptr = self.into_ptr(ptr)?.unwrap_or_err()?.to_ptr()?;
- self.memory.get_fn(ptr)?;
- // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
- }
- TyFnDef(..) => {
- // This is a zero-sized type with all relevant data sitting in the type.
- // There is nothing to validate.
- }
-
- // Compound types
- TyStr => {
- // TODO: Validate strings
- }
- TySlice(elem_ty) => {
- let len = match query.place.1 {
- Place::Ptr { extra: PlaceExtra::Length(len), .. } => len,
- _ => {
- bug!(
- "acquire_valid of a TySlice given non-slice place: {:?}",
- query.place
- )
- }
- };
- for i in 0..len {
- let inner_place = self.place_index(query.place.1, query.ty, i)?;
- self.validate(
- ValidationQuery {
- place: (query.place.0.clone().index(i), inner_place),
- ty: elem_ty,
- ..query
- },
- mode,
- )?;
- }
- }
- TyArray(elem_ty, len) => {
- let len = match len.val {
- ConstValue::Unevaluated(def_id, substs) => {
- self.tcx.const_eval(self.tcx.param_env(def_id).and(GlobalId {
- instance: Instance::new(def_id, substs),
- promoted: None,
- }))
- .map_err(|_err|EvalErrorKind::MachineError("<already reported>".to_string()))?
- }
- _ => len,
- };
- let len = len.unwrap_usize(self.tcx.tcx);
- for i in 0..len {
- let inner_place = self.place_index(query.place.1, query.ty, i as u64)?;
- self.validate(
- ValidationQuery {
- place: (query.place.0.clone().index(i as u64), inner_place),
- ty: elem_ty,
- ..query
- },
- mode,
- )?;
- }
- }
- TyDynamic(_data, _region) => {
- // Check that this is a valid vtable
- let vtable = match query.place.1 {
- Place::Ptr { extra: PlaceExtra::Vtable(vtable), .. } => vtable,
- _ => {
- bug!(
- "acquire_valid of a TyDynamic given non-trait-object place: {:?}",
- query.place
- )
- }
- };
- self.read_size_and_align_from_vtable(vtable)?;
- // TODO: Check that the vtable contains all the function pointers we expect it to have.
- // Trait objects cannot have any operations performed
- // on them directly. We cannot, in general, even acquire any locks as the trait object *could*
- // contain an UnsafeCell. If we call functions to get access to data, we will validate
- // their return values. So, it doesn't seem like there's anything else to do.
- }
- TyAdt(adt, _) => {
- if Some(adt.did) == self.tcx.tcx.lang_items().unsafe_cell_type() &&
- query.mutbl == MutImmutable
- {
- // No locks for shared unsafe cells. Also no other validation, the only field is private anyway.
- return Ok(());
- }
-
- match adt.adt_kind() {
- AdtKind::Enum => {
- let layout = self.layout_of(query.ty)?;
- let variant_idx = self.read_discriminant_as_variant_index(query.place.1, layout)?;
- let variant = &adt.variants[variant_idx];
-
- if !variant.fields.is_empty() {
- // Downcast to this variant, if needed
- let place = if adt.is_enum() {
- (
- query.place.0.downcast(adt, variant_idx),
- self.eval_place_projection(
- query.place.1,
- query.ty,
- &mir::ProjectionElem::Downcast(adt, variant_idx),
- )?,
- )
- } else {
- query.place
- };
-
- // Recursively validate the fields
- self.validate_fields(
- ValidationQuery { place, ..query },
- mode,
- )?;
- } else {
- // No fields, nothing left to check. Downcasting may fail, e.g. in case of a CEnum.
- }
- }
- AdtKind::Struct => {
- self.validate_fields(query, mode)?;
- }
- AdtKind::Union => {
- // No guarantees are provided for union types.
- // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
- }
- }
- }
- TyTuple(..) |
- TyClosure(..) => {
- // TODO: Check if the signature matches for `TyClosure`
- // (should be the same check as what terminator/mod.rs already does on call?).
- // Is there other things we can/should check? Like vtable pointers?
- self.validate_fields(query, mode)?;
- }
- // FIXME: generators aren't validated right now
- TyGenerator(..) => {},
- _ => bug!("We already established that this is a type we support. ({})", query.ty),
- }
- };
- match res {
- // ReleaseUntil(None) of an uninitalized variable is a NOP. This is needed because
- // we have to release the return value of a function; due to destination-passing-style
- // the callee may directly write there.
- // TODO: Ideally we would know whether the destination is already initialized, and only
- // release if it is. But of course that can't even always be statically determined.
- Err(EvalError { kind: EvalErrorKind::ReadUndefBytes, .. })
- if mode == ValidationMode::ReleaseUntil(None) => {
- Ok(())
- }
- res => res,
- }
- }
-}