use std::fmt::Write; use std::hash::{Hash, Hasher}; use std::mem; use rustc::hir::def_id::DefId; use rustc::hir::def::Def; use rustc::hir::map::definitions::DefPathData; use rustc::mir; use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf, TyLayout, Primitive}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeAndMut}; use rustc::ty::query::TyCtxtAt; use rustc_data_structures::fx::{FxHashSet, FxHasher}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc::mir::interpret::{ GlobalId, Value, Scalar, FrameInfo, AllocType, EvalResult, EvalErrorKind, Pointer, ConstValue, }; use syntax::codemap::{self, Span}; use syntax::ast::Mutability; use super::{Place, PlaceExtra, Memory, HasMemory, MemoryKind, Machine}; macro_rules! validation_failure{ ($what:expr, $where:expr, $details:expr) => {{ let where_ = if $where.is_empty() { String::new() } else { format!(" at {}", $where) }; err!(ValidationFailure(format!( "encountered {}{}, but expected {}", $what, where_, $details, ))) }}; ($what:expr, $where:expr) => {{ let where_ = if $where.is_empty() { String::new() } else { format!(" at {}", $where) }; err!(ValidationFailure(format!( "encountered {}{}", $what, where_, ))) }}; } pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. pub machine: M, /// The results of the type checker, from rustc. pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>, /// Bounds in scope for polymorphic evaluations. pub param_env: ty::ParamEnv<'tcx>, /// The virtual memory system. pub memory: Memory<'a, 'mir, 'tcx, M>, /// The virtual call stack. pub(crate) stack: Vec>, /// The maximum number of stack frames allowed pub(crate) stack_limit: usize, /// When this value is negative, it indicates the number of interpreter /// steps *until* the loop detector is enabled. When it is positive, it is /// the number of steps after the detector has been enabled modulo the loop /// detector period. pub(crate) steps_since_detector_enabled: isize, pub(crate) loop_detector: InfiniteLoopDetector<'a, 'mir, 'tcx, M>, } /// A stack frame. #[derive(Clone)] pub struct Frame<'mir, 'tcx: 'mir> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// /// The MIR for the function called on this frame. pub mir: &'mir mir::Mir<'tcx>, /// The def_id and substs of the current function pub instance: ty::Instance<'tcx>, /// The span of the call site. pub span: codemap::Span, //////////////////////////////////////////////////////////////////////////////// // Return place and locals //////////////////////////////////////////////////////////////////////////////// /// The block to return to when returning from the current stack frame pub return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to. pub return_place: Place, /// The list of locals for this stack frame, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. The locals are stored as `Option`s. /// `None` represents a local that is currently dead, while a live local /// can either directly contain `Scalar` or refer to some part of an `Allocation`. /// /// Before being initialized, arguments are `Value::Scalar(Scalar::undef())` and other locals are `None`. pub locals: IndexVec>, //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// /// The block that is currently executed (or will be executed after the above call stacks /// return). pub block: mir::BasicBlock, /// The index of the currently evaluated statement. pub stmt: usize, } impl<'mir, 'tcx: 'mir> Eq for Frame<'mir, 'tcx> {} impl<'mir, 'tcx: 'mir> PartialEq for Frame<'mir, 'tcx> { fn eq(&self, other: &Self) -> bool { let Frame { mir: _, instance, span: _, return_to_block, return_place, locals, block, stmt, } = self; // Some of these are constant during evaluation, but are included // anyways for correctness. *instance == other.instance && *return_to_block == other.return_to_block && *return_place == other.return_place && *locals == other.locals && *block == other.block && *stmt == other.stmt } } impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> { fn hash(&self, state: &mut H) { let Frame { mir: _, instance, span: _, return_to_block, return_place, locals, block, stmt, } = self; instance.hash(state); return_to_block.hash(state); return_place.hash(state); locals.hash(state); block.hash(state); stmt.hash(state); } } /// The virtual machine state during const-evaluation at a given point in time. type EvalSnapshot<'a, 'mir, 'tcx, M> = (M, Vec>, Memory<'a, 'mir, 'tcx, M>); pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The set of all `EvalSnapshot` *hashes* observed by this detector. /// /// When a collision occurs in this table, we store the full snapshot in /// `snapshots`. hashes: FxHashSet, /// The set of all `EvalSnapshot`s observed by this detector. /// /// An `EvalSnapshot` will only be fully cloned once it has caused a /// collision in `hashes`. As a result, the detector must observe at least /// *two* full cycles of an infinite loop before it triggers. snapshots: FxHashSet>, } impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, 'tcx: 'a + 'mir, { fn default() -> Self { InfiniteLoopDetector { hashes: FxHashSet::default(), snapshots: FxHashSet::default(), } } } impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, 'tcx: 'a + 'mir, { /// Returns `true` if the loop detector has not yet observed a snapshot. pub fn is_empty(&self) -> bool { self.hashes.is_empty() } pub fn observe_and_analyze( &mut self, machine: &M, stack: &Vec>, memory: &Memory<'a, 'mir, 'tcx, M>, ) -> EvalResult<'tcx, ()> { let snapshot = (machine, stack, memory); let mut fx = FxHasher::default(); snapshot.hash(&mut fx); let hash = fx.finish(); if self.hashes.insert(hash) { // No collision return Ok(()) } if self.snapshots.insert((machine.clone(), stack.clone(), memory.clone())) { // Spurious collision or first cycle return Ok(()) } // Second cycle Err(EvalErrorKind::InfiniteLoop.into()) } } #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum StackPopCleanup { /// The stackframe existed to compute the initial value of a static/constant, make sure it /// isn't modifyable afterwards in case of constants. /// In case of `static mut`, mark the memory to ensure it's never marked as immutable through /// references or deallocated MarkStatic(Mutability), /// A regular stackframe added due to a function call will need to get forwarded to the next /// block Goto(mir::BasicBlock), /// The main function and diverging functions have nowhere to return to None, } #[derive(Copy, Clone, Debug)] pub struct TyAndPacked<'tcx> { pub ty: Ty<'tcx>, pub packed: bool, } #[derive(Copy, Clone, Debug)] pub struct ValTy<'tcx> { pub value: Value, pub ty: Ty<'tcx>, } impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { type Target = Value; fn deref(&self) -> &Value { &self.value } } impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { &self.tcx.data_layout } } impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { &self.tcx.data_layout } } impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { *self.tcx } } impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> layout::HasTyCtxt<'tcx> for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> { *self.tcx } } impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for &'a EvalContext<'a, 'mir, 'tcx, M> { type Ty = Ty<'tcx>; type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { self.tcx.layout_of(self.param_env.and(ty)) .map_err(|layout| EvalErrorKind::Layout(layout).into()) } } impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> { type Ty = Ty<'tcx>; type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; #[inline] fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { (&**self).layout_of(ty) } } const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000; impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn new( tcx: TyCtxtAt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, machine: M, memory_data: M::MemoryData, ) -> Self { EvalContext { machine, tcx, param_env, memory: Memory::new(tcx, memory_data), stack: Vec::new(), stack_limit: tcx.sess.const_eval_stack_frame_limit, loop_detector: Default::default(), steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED, } } pub(crate) fn with_fresh_body R, R>(&mut self, f: F) -> R { let stack = mem::replace(&mut self.stack, Vec::new()); let steps = mem::replace(&mut self.steps_since_detector_enabled, -STEPS_UNTIL_DETECTOR_ENABLED); let r = f(self); self.stack = stack; self.steps_since_detector_enabled = steps; r } pub fn alloc_ptr(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Pointer> { assert!(!layout.is_unsized(), "cannot alloc memory for unsized type"); self.memory.allocate(layout.size, layout.align, MemoryKind::Stack) } pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { &self.memory } pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> { &mut self.memory } pub fn stack(&self) -> &[Frame<'mir, 'tcx>] { &self.stack } #[inline] pub fn cur_frame(&self) -> usize { assert!(self.stack.len() > 0); self.stack.len() - 1 } pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { let ptr = self.memory.allocate_bytes(s.as_bytes()); Ok(Scalar::Ptr(ptr).to_value_with_len(s.len() as u64, self.tcx.tcx)) } pub fn const_to_value( &mut self, val: ConstValue<'tcx>, ) -> EvalResult<'tcx, Value> { match val { ConstValue::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; self.read_global_as_value(GlobalId { instance, promoted: None, }) } ConstValue::ByRef(alloc, offset) => { // FIXME: Allocate new AllocId for all constants inside let id = self.memory.allocate_value(alloc.clone(), MemoryKind::Stack)?; Ok(Value::ByRef(Pointer::new(id, offset).into(), alloc.align)) }, ConstValue::ScalarPair(a, b) => Ok(Value::ScalarPair(a, b)), ConstValue::Scalar(val) => Ok(Value::Scalar(val)), } } pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { trace!("resolve: {:?}, {:#?}", def_id, substs); trace!("substs: {:#?}", self.substs()); trace!("param_env: {:#?}", self.param_env); let substs = self.tcx.subst_and_normalize_erasing_regions( self.substs(), self.param_env, &substs, ); ty::Instance::resolve( *self.tcx, self.param_env, def_id, substs, ).ok_or_else(|| EvalErrorKind::TooGeneric.into()) } pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { ty.is_sized(self.tcx, self.param_env) } pub fn load_mir( &self, instance: ty::InstanceDef<'tcx>, ) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> { // do not continue if typeck errors occurred (can only occur in local crate) let did = instance.def_id(); if did.is_local() && self.tcx.has_typeck_tables(did) && self.tcx.typeck_tables_of(did).tainted_by_errors { return err!(TypeckError); } trace!("load mir {:?}", instance); match instance { ty::InstanceDef::Item(def_id) => { self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalErrorKind::NoMirFor(self.tcx.item_path_str(def_id)).into() ) } _ => Ok(self.tcx.instance_mir(instance)), } } pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { // miri doesn't care about lifetimes, and will choke on some crazy ones // let's simply get rid of them let substituted = ty.subst(*self.tcx, substs); self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted) } /// Return the size and aligment of the value at the given type. /// Note that the value does not matter if the type is sized. For unsized types, /// the value has to be a fat pointer, and we only care about the "extra" data in it. pub fn size_and_align_of_dst( &mut self, ty: Ty<'tcx>, value: Value, ) -> EvalResult<'tcx, (Size, Align)> { let layout = self.layout_of(ty)?; if !layout.is_unsized() { Ok(layout.size_and_align()) } else { match ty.sty { ty::TyAdt(..) | ty::TyTuple(..) => { // First get the size of all statically known fields. // Don't use type_of::sizing_type_of because that expects t to be sized, // and it also rounds up to alignment, which we want to avoid, // as the unsized field's alignment could be smaller. assert!(!ty.is_simd()); debug!("DST {} layout: {:?}", ty, layout); let sized_size = layout.fields.offset(layout.fields.count() - 1); let sized_align = layout.align; debug!( "DST {} statically sized prefix size: {:?} align: {:?}", ty, sized_size, sized_align ); // Recurse to get the size of the dynamically sized field (must be // the last field). let field_ty = layout.field(&self, layout.fields.count() - 1)?.ty; let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?; // FIXME (#26403, #27023): We should be adding padding // to `sized_size` (to accommodate the `unsized_align` // required of the unsized field that follows) before // summing it with `sized_size`. (Note that since #26403 // is unfixed, we do not yet add the necessary padding // here. But this is where the add would go.) // Return the sum of sizes and max of aligns. let size = sized_size + unsized_size; // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). let align = sized_align.max(unsized_align); // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. // // Namely, the returned size should be, in C notation: // // `size + ((size & (align-1)) ? align : 0)` // // emulated via the semi-standard fast bit trick: // // `(size + (align-1)) & -align` Ok((size.abi_align(align), align)) } ty::TyDynamic(..) => { let (_, vtable) = self.into_ptr_vtable_pair(value)?; // the second entry in the vtable is the dynamic size of the object. self.read_size_and_align_from_vtable(vtable) } ty::TySlice(_) | ty::TyStr => { let (elem_size, align) = layout.field(&self, 0)?.size_and_align(); let (_, len) = self.into_slice(value)?; Ok((elem_size * len, align)) } _ => bug!("size_of_val::<{:?}>", ty), } } } pub fn push_stack_frame( &mut self, instance: ty::Instance<'tcx>, span: codemap::Span, mir: &'mir mir::Mir<'tcx>, return_place: Place, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; let locals = if mir.local_decls.len() > 1 { let mut locals = IndexVec::from_elem(Some(Value::Scalar(Scalar::undef())), &mir.local_decls); match self.tcx.describe_def(instance.def_id()) { // statics and constants don't have `Storage*` statements, no need to look for them Some(Def::Static(..)) | Some(Def::Const(..)) | Some(Def::AssociatedConst(..)) => {}, _ => { trace!("push_stack_frame: {:?}: num_bbs: {}", span, mir.basic_blocks().len()); for block in mir.basic_blocks() { for stmt in block.statements.iter() { use rustc::mir::StatementKind::{StorageDead, StorageLive}; match stmt.kind { StorageLive(local) | StorageDead(local) => locals[local] = None, _ => {} } } } }, } locals } else { // don't allocate at all for trivial constants IndexVec::new() }; self.stack.push(Frame { mir, block: mir::START_BLOCK, return_to_block, return_place, locals, span, instance, stmt: 0, }); self.memory.cur_frame = self.cur_frame(); if self.stack.len() > self.stack_limit { err!(StackFrameLimitReached) } else { Ok(()) } } pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> { ::log_settings::settings().indentation -= 1; M::end_region(self, None)?; let frame = self.stack.pop().expect( "tried to pop a stack frame, but there were none", ); if !self.stack.is_empty() { // TODO: Is this the correct time to start considering these accesses as originating from the returned-to stack frame? self.memory.cur_frame = self.cur_frame(); } match frame.return_to_block { StackPopCleanup::MarkStatic(mutable) => { if let Place::Ptr { ptr, .. } = frame.return_place { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions self.memory.mark_static_initialized( ptr.to_ptr()?.alloc_id, mutable, )? } else { bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_place); } } StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {} } // deallocate all locals that are backed by an allocation for local in frame.locals { self.deallocate_local(local)?; } Ok(()) } pub fn deallocate_local(&mut self, local: Option) -> EvalResult<'tcx> { if let Some(Value::ByRef(ptr, _align)) = local { trace!("deallocating local"); let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); self.memory.deallocate_local(ptr)?; }; Ok(()) } /// Evaluate an assignment statement. /// /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue /// type writes its results directly into the memory specified by the place. pub(super) fn eval_rvalue_into_place( &mut self, rvalue: &mir::Rvalue<'tcx>, place: &mir::Place<'tcx>, ) -> EvalResult<'tcx> { let dest = self.eval_place(place)?; let dest_ty = self.place_ty(place); use rustc::mir::Rvalue::*; match *rvalue { Use(ref operand) => { let value = self.eval_operand(operand)?.value; let valty = ValTy { value, ty: dest_ty, }; self.write_value(valty, dest)?; } BinaryOp(bin_op, ref left, ref right) => { let left = self.eval_operand(left)?; let right = self.eval_operand(right)?; self.intrinsic_overflowing( bin_op, left, right, dest, dest_ty, )?; } CheckedBinaryOp(bin_op, ref left, ref right) => { let left = self.eval_operand(left)?; let right = self.eval_operand(right)?; self.intrinsic_with_overflow( bin_op, left, right, dest, dest_ty, )?; } UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_scalar(operand)?; let val = self.unary_op(un_op, val, dest_ty)?; self.write_scalar( dest, val, dest_ty, )?; } Aggregate(ref kind, ref operands) => { let (dest, active_field_index) = match **kind { mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { self.write_discriminant_value(dest_ty, dest, variant_index)?; if adt_def.is_enum() { (self.place_downcast(dest, variant_index)?, active_field_index) } else { (dest, active_field_index) } } _ => (dest, None) }; let layout = self.layout_of(dest_ty)?; for (i, operand) in operands.iter().enumerate() { let value = self.eval_operand(operand)?; // Ignore zero-sized fields. if !self.layout_of(value.ty)?.is_zst() { let field_index = active_field_index.unwrap_or(i); let (field_dest, _) = self.place_field(dest, mir::Field::new(field_index), layout)?; self.write_value(value, field_dest)?; } } } Repeat(ref operand, _) => { let (elem_ty, length) = match dest_ty.sty { ty::TyArray(elem_ty, n) => (elem_ty, n.unwrap_usize(self.tcx.tcx)), _ => { bug!( "tried to assign array-repeat to non-array type {:?}", dest_ty ) } }; let elem_size = self.layout_of(elem_ty)?.size; let value = self.eval_operand(operand)?.value; let (dest, dest_align) = self.force_allocation(dest)?.to_ptr_align(); if length > 0 { //write the first value self.write_value_to_ptr(value, dest, dest_align, elem_ty)?; if length > 1 { let rest = dest.ptr_offset(elem_size * 1 as u64, &self)?; self.memory.copy_repeatedly(dest, dest_align, rest, dest_align, elem_size, length - 1, false)?; } } } Len(ref place) => { // FIXME(CTFE): don't allow computing the length of arrays in const eval let src = self.eval_place(place)?; let ty = self.place_ty(place); let (_, len) = src.elem_ty_and_len(ty, self.tcx.tcx); let defined = self.memory.pointer_size().bits() as u8; self.write_scalar( dest, Scalar::Bits { bits: len as u128, defined, }, dest_ty, )?; } Ref(_, _, ref place) => { let src = self.eval_place(place)?; // We ignore the alignment of the place here -- special handling for packed structs ends // at the `&` operator. let (ptr, _align, extra) = self.force_allocation(src)?.to_ptr_align_extra(); let val = match extra { PlaceExtra::None => ptr.to_value(), PlaceExtra::Length(len) => ptr.to_value_with_len(len, self.tcx.tcx), PlaceExtra::Vtable(vtable) => ptr.to_value_with_vtable(vtable), PlaceExtra::DowncastVariant(..) => { bug!("attempted to take a reference to an enum downcast place") } }; let valty = ValTy { value: val, ty: dest_ty, }; self.write_value(valty, dest)?; } NullaryOp(mir::NullOp::Box, ty) => { let ty = self.monomorphize(ty, self.substs()); M::box_alloc(self, ty, dest)?; } NullaryOp(mir::NullOp::SizeOf, ty) => { let ty = self.monomorphize(ty, self.substs()); let layout = self.layout_of(ty)?; assert!(!layout.is_unsized(), "SizeOf nullary MIR operator called for unsized type"); let defined = self.memory.pointer_size().bits() as u8; self.write_scalar( dest, Scalar::Bits { bits: layout.size.bytes() as u128, defined, }, dest_ty, )?; } Cast(kind, ref operand, cast_ty) => { debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); let src = self.eval_operand(operand)?; self.cast(src, kind, dest_ty, dest)?; } Discriminant(ref place) => { let ty = self.place_ty(place); let layout = self.layout_of(ty)?; let place = self.eval_place(place)?; let discr_val = self.read_discriminant_value(place, layout)?; let defined = self.layout_of(dest_ty).unwrap().size.bits() as u8; self.write_scalar(dest, Scalar::Bits { bits: discr_val, defined, }, dest_ty)?; } } self.dump_local(dest); Ok(()) } pub(super) fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyRef(_, ty, _) => !self.type_is_sized(ty), ty::TyAdt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()), _ => false, } } pub(super) fn eval_operand_to_scalar( &mut self, op: &mir::Operand<'tcx>, ) -> EvalResult<'tcx, Scalar> { let valty = self.eval_operand(op)?; self.value_to_scalar(valty) } pub(crate) fn operands_to_args( &mut self, ops: &[mir::Operand<'tcx>], ) -> EvalResult<'tcx, Vec>> { ops.into_iter() .map(|op| self.eval_operand(op)) .collect() } pub fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> { use rustc::mir::Operand::*; let ty = self.monomorphize(op.ty(self.mir(), *self.tcx), self.substs()); match *op { // FIXME: do some more logic on `move` to invalidate the old location Copy(ref place) | Move(ref place) => { Ok(ValTy { value: self.eval_and_read_place(place)?, ty }) }, Constant(ref constant) => { let value = self.const_to_value(constant.literal.val)?; Ok(ValTy { value, ty, }) } } } /// reads a tag and produces the corresponding variant index pub fn read_discriminant_as_variant_index( &self, place: Place, layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, usize> { match layout.variants { ty::layout::Variants::Single { index } => Ok(index), ty::layout::Variants::Tagged { .. } => { let discr_val = self.read_discriminant_value(place, layout)?; layout .ty .ty_adt_def() .expect("tagged layout for non adt") .discriminants(self.tcx.tcx) .position(|var| var.val == discr_val) .ok_or_else(|| EvalErrorKind::InvalidDiscriminant.into()) } ty::layout::Variants::NicheFilling { .. } => { let discr_val = self.read_discriminant_value(place, layout)?; assert_eq!(discr_val as usize as u128, discr_val); Ok(discr_val as usize) }, } } pub fn read_discriminant_value( &self, place: Place, layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, u128> { trace!("read_discriminant_value {:#?}", layout); if layout.abi == layout::Abi::Uninhabited { return Ok(0); } match layout.variants { layout::Variants::Single { index } => { let discr_val = layout.ty.ty_adt_def().map_or( index as u128, |def| def.discriminant_for_variant(*self.tcx, index).val); return Ok(discr_val); } layout::Variants::Tagged { .. } | layout::Variants::NicheFilling { .. } => {}, } let discr_place_val = self.read_place(place)?; let (discr_val, discr) = self.read_field(discr_place_val, None, mir::Field::new(0), layout)?; trace!("discr value: {:?}, {:?}", discr_val, discr); let raw_discr = self.value_to_scalar(ValTy { value: discr_val, ty: discr.ty })?; let discr_val = match layout.variants { layout::Variants::Single { .. } => bug!(), // FIXME: should we catch invalid discriminants here? layout::Variants::Tagged { .. } => { if discr.ty.is_signed() { let i = raw_discr.to_bits(discr.size)? as i128; // going from layout tag type to typeck discriminant type // requires first sign extending with the layout discriminant let shift = 128 - discr.size.bits(); let sexted = (i << shift) >> shift; // and then zeroing with the typeck discriminant type let discr_ty = layout .ty .ty_adt_def().expect("tagged layout corresponds to adt") .repr .discr_type(); let discr_ty = layout::Integer::from_attr(self.tcx.tcx, discr_ty); let shift = 128 - discr_ty.size().bits(); let truncatee = sexted as u128; (truncatee << shift) >> shift } else { raw_discr.to_bits(discr.size)? } }, layout::Variants::NicheFilling { dataful_variant, ref niche_variants, niche_start, .. } => { let variants_start = *niche_variants.start() as u128; let variants_end = *niche_variants.end() as u128; match raw_discr { Scalar::Ptr(_) => { assert!(niche_start == 0); assert!(variants_start == variants_end); dataful_variant as u128 }, Scalar::Bits { bits: raw_discr, defined } => { if defined < discr.size.bits() as u8 { return err!(ReadUndefBytes); } let discr = raw_discr.wrapping_sub(niche_start) .wrapping_add(variants_start); if variants_start <= discr && discr <= variants_end { discr } else { dataful_variant as u128 } }, } } }; Ok(discr_val) } pub fn write_discriminant_value( &mut self, dest_ty: Ty<'tcx>, dest: Place, variant_index: usize, ) -> EvalResult<'tcx> { let layout = self.layout_of(dest_ty)?; match layout.variants { layout::Variants::Single { index } => { if index != variant_index { // If the layout of an enum is `Single`, all // other variants are necessarily uninhabited. assert_eq!(layout.for_variant(&self, variant_index).abi, layout::Abi::Uninhabited); } } layout::Variants::Tagged { ref tag, .. } => { let discr_val = dest_ty.ty_adt_def().unwrap() .discriminant_for_variant(*self.tcx, variant_index) .val; // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation let size = tag.value.size(self.tcx.tcx).bits(); let shift = 128 - size; let discr_val = (discr_val << shift) >> shift; let (discr_dest, tag) = self.place_field(dest, mir::Field::new(0), layout)?; self.write_scalar(discr_dest, Scalar::Bits { bits: discr_val, defined: size as u8, }, tag.ty)?; } layout::Variants::NicheFilling { dataful_variant, ref niche_variants, niche_start, .. } => { if variant_index != dataful_variant { let (niche_dest, niche) = self.place_field(dest, mir::Field::new(0), layout)?; let niche_value = ((variant_index - niche_variants.start()) as u128) .wrapping_add(niche_start); self.write_scalar(niche_dest, Scalar::Bits { bits: niche_value, defined: niche.size.bits() as u8, }, niche.ty)?; } } } Ok(()) } pub fn read_global_as_value(&mut self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, Value> { let cv = self.const_eval(gid)?; self.const_to_value(cv.val) } pub fn const_eval(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { let param_env = if self.tcx.is_static(gid.instance.def_id()).is_some() { ty::ParamEnv::reveal_all() } else { self.param_env }; self.tcx.const_eval(param_env.and(gid)).map_err(|err| EvalErrorKind::ReferencedConstant(err).into()) } pub fn allocate_place_for_value( &mut self, value: Value, layout: TyLayout<'tcx>, variant: Option, ) -> EvalResult<'tcx, Place> { let (ptr, align) = match value { Value::ByRef(ptr, align) => (ptr, align), Value::ScalarPair(..) | Value::Scalar(_) => { let ptr = self.alloc_ptr(layout)?.into(); self.write_value_to_ptr(value, ptr, layout.align, layout.ty)?; (ptr, layout.align) }, }; Ok(Place::Ptr { ptr, align, extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), }) } pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { let new_place = match place { Place::Local { frame, local } => { match self.stack[frame].locals[local] { None => return err!(DeadLocal), Some(Value::ByRef(ptr, align)) => { Place::Ptr { ptr, align, extra: PlaceExtra::None, } } Some(val) => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let layout = self.layout_of(ty)?; let ptr = self.alloc_ptr(layout)?; self.stack[frame].locals[local] = Some(Value::ByRef(ptr.into(), layout.align)); // it stays live let place = Place::from_ptr(ptr, layout.align); self.write_value(ValTy { value: val, ty }, place)?; place } } } Place::Ptr { .. } => place, }; Ok(new_place) } /// ensures this Value is not a ByRef pub fn follow_by_ref_value( &self, value: Value, ty: Ty<'tcx>, ) -> EvalResult<'tcx, Value> { match value { Value::ByRef(ptr, align) => { self.read_value(ptr, align, ty) } other => Ok(other), } } pub fn value_to_scalar( &self, ValTy { value, ty } : ValTy<'tcx>, ) -> EvalResult<'tcx, Scalar> { match self.follow_by_ref_value(value, ty)? { Value::ByRef { .. } => bug!("follow_by_ref_value can't result in `ByRef`"), Value::Scalar(scalar) => Ok(scalar), Value::ScalarPair(..) => bug!("value_to_scalar can't work with fat pointers"), } } pub fn write_ptr(&mut self, dest: Place, val: Scalar, dest_ty: Ty<'tcx>) -> EvalResult<'tcx> { let valty = ValTy { value: val.to_value(), ty: dest_ty, }; self.write_value(valty, dest) } pub fn write_scalar( &mut self, dest: Place, val: Scalar, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { let valty = ValTy { value: Value::Scalar(val), ty: dest_ty, }; self.write_value(valty, dest) } pub fn write_value( &mut self, ValTy { value: src_val, ty: dest_ty } : ValTy<'tcx>, dest: Place, ) -> EvalResult<'tcx> { //trace!("Writing {:?} to {:?} at type {:?}", src_val, dest, dest_ty); // Note that it is really important that the type here is the right one, and matches the type things are read at. // In case `src_val` is a `ScalarPair`, we don't do any magic here to handle padding properly, which is only // correct if we never look at this data with the wrong type. match dest { Place::Ptr { ptr, align, extra } => { assert_eq!(extra, PlaceExtra::None); self.write_value_to_ptr(src_val, ptr, align, dest_ty) } Place::Local { frame, local } => { let dest = self.stack[frame].get_local(local)?; self.write_value_possibly_by_val( src_val, |this, val| this.stack[frame].set_local(local, val), dest, dest_ty, ) } } } // The cases here can be a bit subtle. Read carefully! fn write_value_possibly_by_val EvalResult<'tcx>>( &mut self, src_val: Value, write_dest: F, old_dest_val: Value, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { if let Value::ByRef(dest_ptr, align) = old_dest_val { // If the value is already `ByRef` (that is, backed by an `Allocation`), // then we must write the new value into this allocation, because there may be // other pointers into the allocation. These other pointers are logically // pointers into the local variable, and must be able to observe the change. // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we // knew for certain that there were no outstanding pointers to this allocation. self.write_value_to_ptr(src_val, dest_ptr, align, dest_ty)?; } else if let Value::ByRef(src_ptr, align) = src_val { // If the value is not `ByRef`, then we know there are no pointers to it // and we can simply overwrite the `Value` in the locals array directly. // // In this specific case, where the source value is `ByRef`, we must duplicate // the allocation, because this is a by-value operation. It would be incorrect // if they referred to the same allocation, since then a change to one would // implicitly change the other. // // It is a valid optimization to attempt reading a primitive value out of the // source and write that into the destination without making an allocation, so // we do so here. if let Ok(Some(src_val)) = self.try_read_value(src_ptr, align, dest_ty) { write_dest(self, src_val)?; } else { let layout = self.layout_of(dest_ty)?; let dest_ptr = self.alloc_ptr(layout)?.into(); self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size, false)?; write_dest(self, Value::ByRef(dest_ptr, layout.align))?; } } else { // Finally, we have the simple case where neither source nor destination are // `ByRef`. We may simply copy the source value over the the destintion. write_dest(self, src_val)?; } Ok(()) } pub fn write_value_to_ptr( &mut self, value: Value, dest: Scalar, dest_align: Align, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { let layout = self.layout_of(dest_ty)?; trace!("write_value_to_ptr: {:#?}, {}, {:#?}", value, dest_ty, layout); match value { Value::ByRef(ptr, align) => { self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size, false) } Value::Scalar(scalar) => { let signed = match layout.abi { layout::Abi::Scalar(ref scal) => match scal.value { layout::Primitive::Int(_, signed) => signed, _ => false, }, _ => match scalar { Scalar::Bits { defined: 0, .. } => false, _ => bug!("write_value_to_ptr: invalid ByVal layout: {:#?}", layout), } }; self.memory.write_scalar(dest, dest_align, scalar, layout.size, signed) } Value::ScalarPair(a_val, b_val) => { trace!("write_value_to_ptr valpair: {:#?}", layout); let (a, b) = match layout.abi { layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value), _ => bug!("write_value_to_ptr: invalid ScalarPair layout: {:#?}", layout) }; let (a_size, b_size) = (a.size(&self), b.size(&self)); let a_ptr = dest; let b_offset = a_size.abi_align(b.align(&self)); let b_ptr = dest.ptr_offset(b_offset, &self)?.into(); // TODO: What about signedess? self.memory.write_scalar(a_ptr, dest_align, a_val, a_size, false)?; self.memory.write_scalar(b_ptr, dest_align, b_val, b_size, false) } } } pub fn read_value(&self, ptr: Scalar, align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if let Some(val) = self.try_read_value(ptr, align, ty)? { Ok(val) } else { bug!("primitive read failed for type: {:?}", ty); } } pub(crate) fn read_ptr( &self, ptr: Pointer, ptr_align: Align, pointee_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Value> { let ptr_size = self.memory.pointer_size(); let p: Scalar = self.memory.read_ptr_sized(ptr, ptr_align)?.into(); if self.type_is_sized(pointee_ty) { Ok(p.to_value()) } else { trace!("reading fat pointer extra of type {}", pointee_ty); let extra = ptr.offset(ptr_size, self)?; match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => Ok(p.to_value_with_vtable( self.memory.read_ptr_sized(extra, ptr_align)?.to_ptr()?, )), ty::TySlice(..) | ty::TyStr => { let len = self .memory .read_ptr_sized(extra, ptr_align)? .to_bits(ptr_size)?; Ok(p.to_value_with_len(len as u64, self.tcx.tcx)) }, _ => bug!("unsized scalar ptr read from {:?}", pointee_ty), } } } fn validate_scalar( &self, value: Scalar, size: Size, scalar: &layout::Scalar, path: &str, ty: Ty, ) -> EvalResult<'tcx> { trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty); let (lo, hi) = scalar.valid_range.clone().into_inner(); let (bits, defined) = match value { Scalar::Bits { bits, defined } => (bits, defined), Scalar::Ptr(_) => { let ptr_size = self.memory.pointer_size(); let ptr_max = u128::max_value() >> (128 - ptr_size.bits()); return if lo > hi { if lo - hi == 1 { // no gap, all values are ok Ok(()) } else if hi < ptr_max || lo > 1 { let max = u128::max_value() >> (128 - size.bits()); validation_failure!( "pointer", path, format!("something in the range {:?} or {:?}", 0..=lo, hi..=max) ) } else { Ok(()) } } else if hi < ptr_max || lo > 1 { validation_failure!( "pointer", path, format!("something in the range {:?}", scalar.valid_range) ) } else { Ok(()) }; }, }; // char gets a special treatment, because its number space is not contiguous so `TyLayout` // has no special checks for chars match ty.sty { ty::TyChar => { assert_eq!(size.bytes(), 4); if ::std::char::from_u32(bits as u32).is_none() { return err!(InvalidChar(bits)); } } _ => {}, } use std::ops::RangeInclusive; let in_range = |bound: RangeInclusive| { defined as u64 >= size.bits() && bound.contains(&bits) }; if lo > hi { if in_range(0..=hi) || in_range(lo..=u128::max_value()) { Ok(()) } else if defined as u64 >= size.bits() { validation_failure!( bits, path, format!("something in the range {:?} or {:?}", ..=hi, lo..) ) } else { validation_failure!("undefined bytes", path) } } else { if in_range(scalar.valid_range.clone()) { Ok(()) } else if defined as u64 >= size.bits() { validation_failure!( bits, path, format!("something in the range {:?}", scalar.valid_range) ) } else { validation_failure!("undefined bytes", path) } } } /// This function checks the memory where `ptr` points to. /// It will error if the bits at the destination do not match the ones described by the layout. pub fn validate_ptr_target( &self, ptr: Pointer, ptr_align: Align, mut layout: TyLayout<'tcx>, path: String, seen: &mut FxHashSet<(Pointer, Ty<'tcx>)>, todo: &mut Vec<(Pointer, Ty<'tcx>, String)>, ) -> EvalResult<'tcx> { self.memory.dump_alloc(ptr.alloc_id); trace!("validate_ptr_target: {:?}, {:#?}", ptr, layout); let variant; match layout.variants { layout::Variants::NicheFilling { niche: ref tag, .. } | layout::Variants::Tagged { ref tag, .. } => { let size = tag.value.size(self); let (tag_value, tag_layout) = self.read_field( Value::ByRef(ptr.into(), ptr_align), None, mir::Field::new(0), layout, )?; let tag_value = self.value_to_scalar(ValTy { value: tag_value, ty: tag_layout.ty, })?; let path = format!("{}.TAG", path); self.validate_scalar(tag_value, size, tag, &path, tag_layout.ty)?; let variant_index = self.read_discriminant_as_variant_index( Place::from_ptr(ptr, ptr_align), layout, )?; variant = variant_index; layout = layout.for_variant(self, variant_index); trace!("variant layout: {:#?}", layout); }, layout::Variants::Single { index } => variant = index, } match layout.fields { // primitives are unions with zero fields layout::FieldPlacement::Union(0) => { match layout.abi { // nothing to do, whatever the pointer points to, it is never going to be read layout::Abi::Uninhabited => validation_failure!("a value of an uninhabited type", path), // check that the scalar is a valid pointer or that its bit range matches the // expectation. layout::Abi::Scalar(ref scalar) => { let size = scalar.value.size(self); let value = self.memory.read_scalar(ptr, ptr_align, size)?; self.validate_scalar(value, size, scalar, &path, layout.ty)?; if scalar.value == Primitive::Pointer { // ignore integer pointers, we can't reason about the final hardware if let Scalar::Ptr(ptr) = value { let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id); if let Some(AllocType::Static(did)) = alloc_kind { // statics from other crates are already checked // extern statics should not be validated as they have no body if !did.is_local() || self.tcx.is_foreign_item(did) { return Ok(()); } } if let Some(tam) = layout.ty.builtin_deref(false) { // we have not encountered this pointer+layout combination before if seen.insert((ptr, tam.ty)) { todo.push((ptr, tam.ty, format!("(*{})", path))) } } } } Ok(()) }, _ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", layout.abi), } } layout::FieldPlacement::Union(_) => { // We can't check unions, their bits are allowed to be anything. // The fields don't need to correspond to any bit pattern of the union's fields. // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389 Ok(()) }, layout::FieldPlacement::Array { stride, count } => { let elem_layout = layout.field(self, 0)?; for i in 0..count { let mut path = path.clone(); self.write_field_name(&mut path, layout.ty, i as usize, variant).unwrap(); self.validate_ptr_target(ptr.offset(stride * i, self)?, ptr_align, elem_layout, path, seen, todo)?; } Ok(()) }, layout::FieldPlacement::Arbitrary { ref offsets, .. } => { // check length field and vtable field match layout.ty.builtin_deref(false).map(|tam| &tam.ty.sty) { | Some(ty::TyStr) | Some(ty::TySlice(_)) => { let (len, len_layout) = self.read_field( Value::ByRef(ptr.into(), ptr_align), None, mir::Field::new(1), layout, )?; let len = self.value_to_scalar(ValTy { value: len, ty: len_layout.ty })?; if len.to_bits(len_layout.size).is_err() { return validation_failure!("length is not a valid integer", path); } }, Some(ty::TyDynamic(..)) => { let (vtable, vtable_layout) = self.read_field( Value::ByRef(ptr.into(), ptr_align), None, mir::Field::new(1), layout, )?; let vtable = self.value_to_scalar(ValTy { value: vtable, ty: vtable_layout.ty })?; if vtable.to_ptr().is_err() { return validation_failure!("vtable address is not a pointer", path); } } _ => {}, } for (i, &offset) in offsets.iter().enumerate() { let field_layout = layout.field(self, i)?; let mut path = path.clone(); self.write_field_name(&mut path, layout.ty, i, variant).unwrap(); self.validate_ptr_target(ptr.offset(offset, self)?, ptr_align, field_layout, path, seen, todo)?; } Ok(()) } } } pub fn try_read_by_ref(&self, mut val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { // Convert to ByVal or ScalarPair if possible if let Value::ByRef(ptr, align) = val { if let Some(read_val) = self.try_read_value(ptr, align, ty)? { val = read_val; } } Ok(val) } pub fn try_read_value(&self, ptr: Scalar, ptr_align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { let layout = self.layout_of(ty)?; self.memory.check_align(ptr, ptr_align)?; if layout.size.bytes() == 0 { return Ok(Some(Value::Scalar(Scalar::undef()))); } let ptr = ptr.to_ptr()?; match layout.abi { layout::Abi::Scalar(..) => { let scalar = self.memory.read_scalar(ptr, ptr_align, layout.size)?; Ok(Some(Value::Scalar(scalar))) } layout::Abi::ScalarPair(ref a, ref b) => { let (a, b) = (&a.value, &b.value); let (a_size, b_size) = (a.size(self), b.size(self)); let a_ptr = ptr; let b_offset = a_size.abi_align(b.align(self)); let b_ptr = ptr.offset(b_offset, self)?.into(); let a_val = self.memory.read_scalar(a_ptr, ptr_align, a_size)?; let b_val = self.memory.read_scalar(b_ptr, ptr_align, b_size)?; Ok(Some(Value::ScalarPair(a_val, b_val))) } _ => Ok(None), } } pub fn frame(&self) -> &Frame<'mir, 'tcx> { self.stack.last().expect("no call frames exist") } pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> { self.stack.last_mut().expect("no call frames exist") } pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> { self.frame().mir } pub fn substs(&self) -> &'tcx Substs<'tcx> { if let Some(frame) = self.stack.last() { frame.instance.substs } else { Substs::empty() } } fn unsize_into_ptr( &mut self, src: Value, src_ty: Ty<'tcx>, dest: Place, dest_ty: Ty<'tcx>, sty: Ty<'tcx>, dty: Ty<'tcx>, ) -> EvalResult<'tcx> { // A -> A conversion let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty); match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = self.into_ptr(src)?; // u64 cast is from usize to u64, which is always good let valty = ValTy { value: ptr.to_value_with_len(length.unwrap_usize(self.tcx.tcx), self.tcx.tcx), ty: dest_ty, }; self.write_value(valty, dest) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. let valty = ValTy { value: src, ty: dest_ty, }; self.write_value(valty, dest) } (_, &ty::TyDynamic(ref data, _)) => { let trait_ref = data.principal().unwrap().with_self_ty( *self.tcx, src_pointee_ty, ); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = self.into_ptr(src)?; let valty = ValTy { value: ptr.to_value_with_vtable(vtable), ty: dest_ty, }; self.write_value(valty, dest) } _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), } } crate fn unsize_into( &mut self, src: Value, src_layout: TyLayout<'tcx>, dst: Place, dst_layout: TyLayout<'tcx>, ) -> EvalResult<'tcx> { match (&src_layout.ty.sty, &dst_layout.ty.sty) { (&ty::TyRef(_, s, _), &ty::TyRef(_, d, _)) | (&ty::TyRef(_, s, _), &ty::TyRawPtr(TypeAndMut { ty: d, .. })) | (&ty::TyRawPtr(TypeAndMut { ty: s, .. }), &ty::TyRawPtr(TypeAndMut { ty: d, .. })) => { self.unsize_into_ptr(src, src_layout.ty, dst, dst_layout.ty, s, d) } (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { assert_eq!(def_a, def_b); if def_a.is_box() || def_b.is_box() { if !def_a.is_box() || !def_b.is_box() { bug!("invalid unsizing between {:?} -> {:?}", src_layout, dst_layout); } return self.unsize_into_ptr( src, src_layout.ty, dst, dst_layout.ty, src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty(), ); } // unsizing of generic struct with pointer fields // Example: `Arc` -> `Arc` // here we need to increase the size of every &T thin ptr field to a fat ptr for i in 0..src_layout.fields.count() { let (dst_f_place, dst_field) = self.place_field(dst, mir::Field::new(i), dst_layout)?; if dst_field.is_zst() { continue; } let (src_f_value, src_field) = match src { Value::ByRef(ptr, align) => { let src_place = Place::from_scalar_ptr(ptr, align); let (src_f_place, src_field) = self.place_field(src_place, mir::Field::new(i), src_layout)?; (self.read_place(src_f_place)?, src_field) } Value::Scalar(_) | Value::ScalarPair(..) => { let src_field = src_layout.field(&self, i)?; assert_eq!(src_layout.fields.offset(i).bytes(), 0); assert_eq!(src_field.size, src_layout.size); (src, src_field) } }; if src_field.ty == dst_field.ty { self.write_value(ValTy { value: src_f_value, ty: src_field.ty, }, dst_f_place)?; } else { self.unsize_into(src_f_value, src_field, dst_f_place, dst_field)?; } } Ok(()) } _ => { bug!( "unsize_into: invalid conversion: {:?} -> {:?}", src_layout, dst_layout ) } } } pub fn dump_local(&self, place: Place) { // Debug output if !log_enabled!(::log::Level::Trace) { return; } match place { Place::Local { frame, local } => { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); if frame != self.cur_frame() { write!(msg, " ({} frames up)", self.cur_frame() - frame).unwrap(); } write!(msg, ":").unwrap(); match self.stack[frame].get_local(local) { Err(err) => { if let EvalErrorKind::DeadLocal = err.kind { write!(msg, " is dead").unwrap(); } else { panic!("Failed to access local: {:?}", err); } } Ok(Value::ByRef(ptr, align)) => { match ptr { Scalar::Ptr(ptr) => { write!(msg, " by align({}) ref:", align.abi()).unwrap(); allocs.push(ptr.alloc_id); } ptr => write!(msg, " integral by ref: {:?}", ptr).unwrap(), } } Ok(Value::Scalar(val)) => { write!(msg, " {:?}", val).unwrap(); if let Scalar::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } } Ok(Value::ScalarPair(val1, val2)) => { write!(msg, " ({:?}, {:?})", val1, val2).unwrap(); if let Scalar::Ptr(ptr) = val1 { allocs.push(ptr.alloc_id); } if let Scalar::Ptr(ptr) = val2 { allocs.push(ptr.alloc_id); } } } trace!("{}", msg); self.memory.dump_allocs(allocs); } Place::Ptr { ptr, align, .. } => { match ptr { Scalar::Ptr(ptr) => { trace!("by align({}) ref:", align.abi()); self.memory.dump_alloc(ptr.alloc_id); } ptr => trace!(" integral by ref: {:?}", ptr), } } } } /// Convenience function to ensure correct usage of locals pub fn modify_local(&mut self, frame: usize, local: mir::Local, f: F) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { let val = self.stack[frame].get_local(local)?; let new_val = f(self, val)?; self.stack[frame].set_local(local, new_val)?; // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { // self.memory.deallocate(ptr)?; // } Ok(()) } pub fn generate_stacktrace(&self, explicit_span: Option) -> (Vec, Span) { let mut last_span = None; let mut frames = Vec::new(); // skip 1 because the last frame is just the environment of the constant for &Frame { instance, span, mir, block, stmt, .. } in self.stack().iter().skip(1).rev() { // make sure we don't emit frames that are duplicates of the previous if explicit_span == Some(span) { last_span = Some(span); continue; } if let Some(last) = last_span { if last == span { continue; } } else { last_span = Some(span); } let location = if self.tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { "closure".to_owned() } else { instance.to_string() }; let block = &mir.basic_blocks()[block]; let source_info = if stmt < block.statements.len() { block.statements[stmt].source_info } else { block.terminator().source_info }; let lint_root = match mir.source_scope_local_data { mir::ClearCrossCrate::Set(ref ivs) => Some(ivs[source_info.scope].lint_root), mir::ClearCrossCrate::Clear => None, }; frames.push(FrameInfo { span, location, lint_root }); } trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); (frames, self.tcx.span) } pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { super::sign_extend(self.tcx.tcx, value, ty) } pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { super::truncate(self.tcx.tcx, value, ty) } fn write_field_name(&self, s: &mut String, ty: Ty<'tcx>, i: usize, variant: usize) -> ::std::fmt::Result { match ty.sty { ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyFnPtr(_) | ty::TyNever | ty::TyFnDef(..) | ty::TyGeneratorWitness(..) | ty::TyForeign(..) | ty::TyDynamic(..) => { bug!("field_name({:?}): not applicable", ty) } // 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 write!(s, ".data_ptr"); } match self.tcx.struct_tail(pointee).sty { ty::TySlice(_) | ty::TyStr => write!(s, ".len"), ty::TyDynamic(..) => write!(s, ".vtable_ptr"), _ => bug!("field_name({:?}): not applicable", ty) } } // Arrays and slices. ty::TyArray(_, _) | ty::TySlice(_) | ty::TyStr => write!(s, "[{}]", i), // generators and closures. ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => { let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); let freevar = self.tcx.with_freevars(node_id, |fv| fv[i]); write!(s, ".upvar({})", self.tcx.hir.name(freevar.var_id())) } ty::TyTuple(_) => write!(s, ".{}", i), // enums ty::TyAdt(def, ..) if def.is_enum() => { let variant = &def.variants[variant]; write!(s, ".{}::{}", variant.name, variant.fields[i].ident) } // other ADTs. ty::TyAdt(def, _) => write!(s, ".{}", def.non_enum_variant().fields[i].ident), ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) | ty::TyInfer(_) | ty::TyError => { bug!("write_field_name: unexpected type `{}`", ty) } } } } impl<'mir, 'tcx> Frame<'mir, 'tcx> { pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> { self.locals[local].ok_or_else(|| EvalErrorKind::DeadLocal.into()) } fn set_local(&mut self, local: mir::Local, value: Value) -> EvalResult<'tcx> { match self.locals[local] { None => err!(DeadLocal), Some(ref mut local) => { *local = value; Ok(()) } } } pub fn storage_live(&mut self, local: mir::Local) -> Option { trace!("{:?} is now live", local); // StorageLive *always* kills the value that's currently stored mem::replace(&mut self.locals[local], Some(Value::Scalar(Scalar::undef()))) } /// Returns the old value of the local pub fn storage_dead(&mut self, local: mir::Local) -> Option { trace!("{:?} is now dead", local); self.locals[local].take() } }