use std::fmt::Write;
use std::mem;
-use syntax::source_map::{self, Span, DUMMY_SP};
-use rustc::ich::StableHashingContext;
-use rustc::hir::def_id::DefId;
use rustc::hir::def::DefKind;
+use rustc::hir::def_id::DefId;
+use rustc::ich::StableHashingContext;
use rustc::mir;
-use rustc::ty::layout::{
- self, Size, Align, HasDataLayout, LayoutOf, TyLayout
+use rustc::mir::interpret::{
+ sign_extend, truncate, AllocId, FrameInfo, GlobalId, InterpResult, Pointer, Scalar,
};
+use rustc::ty::layout::{self, Align, HasDataLayout, LayoutOf, Size, TyLayout};
+use rustc::ty::query::TyCtxtAt;
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::ty::query::TyCtxtAt;
-use rustc_index::vec::IndexVec;
-use rustc::mir::interpret::{
- GlobalId, Scalar, Pointer, FrameInfo, AllocId,
- InterpResult, truncate, sign_extend,
-};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
+use syntax::source_map::{self, Span, DUMMY_SP};
use super::{
- Immediate, Operand, MemPlace, MPlaceTy, Place, PlaceTy, ScalarMaybeUndef,
- Memory, Machine, StackPopInfo
+ Immediate, MPlaceTy, Machine, MemPlace, Memory, Operand, Place, PlaceTy, ScalarMaybeUndef,
+ StackPopInfo,
};
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
/// A stack frame.
#[derive(Clone)]
-pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
+pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> {
////////////////////////////////////////////////////////////////////////////////
// Function and callsite information
////////////////////////////////////////////////////////////////////////////////
/// State of a local variable including a memoized layout
#[derive(Clone, PartialEq, Eq, HashStable)]
-pub struct LocalState<'tcx, Tag=(), Id=AllocId> {
+pub struct LocalState<'tcx, Tag = (), Id = AllocId> {
pub value: LocalValue<Tag, Id>,
/// Don't modify if `Some`, this is only used to prevent computing the layout twice
#[stable_hasher(ignore)]
}
/// Current value of a local variable
-#[derive(Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these
-pub enum LocalValue<Tag=(), Id=AllocId> {
+#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these
+pub enum LocalValue<Tag = (), Id = AllocId> {
/// This local is not currently alive, and cannot be used at all.
Dead,
/// This local is alive but not yet initialized. It can be written to
pub fn access(&self) -> InterpResult<'tcx, Operand<Tag>> {
match self.value {
LocalValue::Dead => throw_unsup!(DeadLocal),
- LocalValue::Uninitialized =>
- bug!("The type checker should prevent reading from a never-written local"),
+ LocalValue::Uninitialized => {
+ bug!("The type checker should prevent reading from a never-written local")
+ }
LocalValue::Live(val) => Ok(val),
}
}
match self.value {
LocalValue::Dead => throw_unsup!(DeadLocal),
LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)),
- ref mut local @ LocalValue::Live(Operand::Immediate(_)) |
- ref mut local @ LocalValue::Uninitialized => {
- Ok(Ok(local))
- }
+ ref mut local @ LocalValue::Live(Operand::Immediate(_))
+ | ref mut local @ LocalValue::Uninitialized => Ok(Ok(local)),
}
}
}
pub fn force_bits(
&self,
scalar: Scalar<M::PointerTag>,
- size: Size
+ size: Size,
) -> InterpResult<'tcx, u128> {
self.memory.force_bits(scalar, size)
}
&self,
instance: ty::InstanceDef<'tcx>,
promoted: Option<mir::Promoted>,
- ) -> InterpResult<'tcx, mir::ReadOnlyBodyCache<'tcx, 'tcx>> {
+ ) -> InterpResult<'tcx, mir::ReadOnlyBodyAndCache<'tcx, 'tcx>> {
// do not continue if typeck errors occurred (can only occur in local crate)
let did = instance.def_id();
if did.is_local()
return Ok(self.tcx.promoted_mir(did)[promoted].unwrap_read_only());
}
match instance {
- ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
- Ok(self.tcx.optimized_mir(did).unwrap_read_only())
- } else {
- throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
- },
+ ty::InstanceDef::Item(def_id) => {
+ if self.tcx.is_mir_available(did) {
+ Ok(self.tcx.optimized_mir(did).unwrap_read_only())
+ } else {
+ throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
+ }
+ }
_ => Ok(self.tcx.instance_mir(instance)),
}
}
pub(super) fn resolve(
&self,
def_id: DefId,
- substs: SubstsRef<'tcx>
+ substs: SubstsRef<'tcx>,
) -> InterpResult<'tcx, ty::Instance<'tcx>> {
trace!("resolve: {:?}, {:#?}", def_id, substs);
trace!("param_env: {:#?}", self.param_env);
trace!("substs: {:#?}", substs);
- ty::Instance::resolve(
- *self.tcx,
- self.param_env,
- def_id,
- substs,
- ).ok_or_else(|| err_inval!(TooGeneric).into())
+ ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs)
+ .ok_or_else(|| err_inval!(TooGeneric).into())
}
pub fn layout_of_local(
// of `extern type`, this should be adapted. It is just a temporary hack
// to get some code to work that probably ought to work.
if sized_size == Size::ZERO {
- return Ok(None)
+ return Ok(None);
} else {
bug!("Fields cannot be extern types, unless they are at offset 0")
}
// Check if this brought us over the size limit.
if size.bytes() >= self.tcx.data_layout().obj_size_bound() {
- throw_ub_format!("wide pointer metadata contains invalid information: \
- total size is bigger than largest supported object");
+ throw_ub_format!(
+ "wide pointer metadata contains invalid information: \
+ total size is bigger than largest supported object"
+ );
}
Ok(Some((size, align)))
}
}
ty::Slice(_) | ty::Str => {
- let len = metadata.expect("slice fat ptr must have length").to_machine_usize(self)?;
+ let len =
+ metadata.expect("slice fat ptr must have length").to_machine_usize(self)?;
let elem = layout.field(self, 0)?;
// Make sure the slice is not too big.
- let size = elem.size.checked_mul(len, &*self.tcx)
- .ok_or_else(|| err_ub_format!("invalid slice: \
- total size is bigger than largest supported object"))?;
+ let size = elem.size.checked_mul(len, &*self.tcx).ok_or_else(|| {
+ err_ub_format!(
+ "invalid slice: \
+ total size is bigger than largest supported object"
+ )
+ })?;
Ok(Some((size, elem.align.abi)))
}
- ty::Foreign(_) => {
- Ok(None)
- }
+ ty::Foreign(_) => Ok(None),
_ => bug!("size_and_align_of::<{:?}> not supported", layout.ty),
}
#[inline]
pub fn size_and_align_of_mplace(
&self,
- mplace: MPlaceTy<'tcx, M::PointerTag>
+ mplace: MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, Option<(Size, Align)>> {
self.size_and_align_of(mplace.meta, mplace.layout)
}
// don't allocate at all for trivial constants
if body.local_decls.len() > 1 {
// Locals are initially uninitialized.
- let dummy = LocalState {
- value: LocalValue::Uninitialized,
- layout: Cell::new(None),
- };
+ let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
// Return place is handled specially by the `eval_place` functions, and the
// entry in `locals` should never be used. Make it dead, to be sure.
// Now mark those locals as dead that we do not want to initialize
match self.tcx.def_kind(instance.def_id()) {
// statics and constants don't have `Storage*` statements, no need to look for them
- Some(DefKind::Static)
- | Some(DefKind::Const)
- | Some(DefKind::AssocConst) => {},
+ Some(DefKind::Static) | Some(DefKind::Const) | Some(DefKind::AssocConst) => {}
_ => {
trace!("push_stack_frame: {:?}: num_bbs: {}", span, body.basic_blocks().len());
for block in body.basic_blocks() {
for stmt in block.statements.iter() {
use rustc::mir::StatementKind::{StorageDead, StorageLive};
match stmt.kind {
- StorageLive(local) |
- StorageDead(local) => {
+ StorageLive(local) | StorageDead(local) => {
locals[local].value = LocalValue::Dead;
}
_ => {}
}
}
}
- },
+ }
}
// done
self.frame_mut().locals = locals;
/// `Drop` impls for any locals that have been initialized at this point.
/// The cleanup block ends with a special `Resume` terminator, which will
/// cause us to continue unwinding.
- pub(super) fn pop_stack_frame(
- &mut self,
- unwinding: bool
- ) -> InterpResult<'tcx> {
- info!("LEAVING({}) {} (unwinding = {})",
- self.cur_frame(), self.frame().instance, unwinding);
+ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> {
+ info!(
+ "LEAVING({}) {} (unwinding = {})",
+ self.cur_frame(),
+ self.frame().instance,
+ unwinding
+ );
// Sanity check `unwinding`.
assert_eq!(
unwinding,
match self.frame().block {
None => true,
- Some(block) => self.body().basic_blocks()[block].is_cleanup
+ Some(block) => self.body().basic_blocks()[block].is_cleanup,
}
);
::log_settings::settings().indentation -= 1;
- let frame = self.stack.pop().expect(
- "tried to pop a stack frame, but there were none",
- );
+ let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
let stack_pop_info = M::stack_pop(self, frame.extra, unwinding)?;
if let (false, StackPopInfo::StopUnwinding) = (unwinding, stack_pop_info) {
bug!("Attempted to stop unwinding while there is no unwinding!");
// Now where do we jump next?
// Determine if we leave this function normally or via unwinding.
- let cur_unwinding = if let StackPopInfo::StopUnwinding = stack_pop_info {
- false
- } else {
- unwinding
- };
+ let cur_unwinding =
+ if let StackPopInfo::StopUnwinding = stack_pop_info { false } else { unwinding };
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
// In that case, we return early. We also avoid validation in that case,
let (cleanup, next_block) = match frame.return_to_block {
StackPopCleanup::Goto { ret, unwind } => {
(true, Some(if cur_unwinding { unwind } else { ret }))
- },
- StackPopCleanup::None { cleanup, .. } => (cleanup, None)
+ }
+ StackPopCleanup::None { cleanup, .. } => (cleanup, None),
};
if !cleanup {
self.deallocate_local(local.value)?;
}
-
- trace!("StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
- frame.return_to_block, stack_pop_info, cur_unwinding);
+ trace!(
+ "StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
+ frame.return_to_block,
+ stack_pop_info,
+ cur_unwinding
+ );
if cur_unwinding {
// Follow the unwind edge.
let unwind = next_block.expect("Encounted StackPopCleanup::None when unwinding!");
// invariant -- that is, unless a function somehow has a ptr to
// its return place... but the way MIR is currently generated, the
// return place is always a local and then this cannot happen.
- self.validate_operand(
- self.place_to_op(return_place)?,
- vec![],
- None,
- )?;
+ self.validate_operand(self.place_to_op(return_place)?, vec![], None)?;
}
} else {
// Uh, that shouldn't happen... the function did not intend to return
}
if self.stack.len() > 0 {
- info!("CONTINUING({}) {} (unwinding = {})",
- self.cur_frame(), self.frame().instance, cur_unwinding);
+ info!(
+ "CONTINUING({}) {} (unwinding = {})",
+ self.cur_frame(),
+ self.frame().instance,
+ cur_unwinding
+ );
}
Ok(())
/// Remember to deallocate that!
pub fn storage_live(
&mut self,
- local: mir::Local
+ local: mir::Local,
) -> InterpResult<'tcx, LocalValue<M::PointerTag>> {
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
trace!("{:?} is now live", local);
// FIXME: should we tell the user that there was a local which was never written to?
if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
trace!("deallocating local");
- let ptr = ptr.to_ptr()?;
+ // All locals have a backing allocation, even if the allocation is empty
+ // due to the local having ZST type.
+ let ptr = ptr.assert_ptr();
if log_enabled!(::log::Level::Trace) {
self.memory.dump_alloc(ptr.alloc_id);
}
match self.stack[frame].locals[local].value {
LocalValue::Dead => write!(msg, " is dead").unwrap(),
LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(),
- LocalValue::Live(Operand::Indirect(mplace)) => {
- match mplace.ptr {
- Scalar::Ptr(ptr) => {
- write!(msg, " by align({}){} ref:",
- mplace.align.bytes(),
- match mplace.meta {
- Some(meta) => format!(" meta({:?})", meta),
- None => String::new()
- }
- ).unwrap();
- allocs.push(ptr.alloc_id);
- }
- ptr => write!(msg, " by integral ref: {:?}", ptr).unwrap(),
+ LocalValue::Live(Operand::Indirect(mplace)) => match mplace.ptr {
+ Scalar::Ptr(ptr) => {
+ write!(
+ msg,
+ " by align({}){} ref:",
+ mplace.align.bytes(),
+ match mplace.meta {
+ Some(meta) => format!(" meta({:?})", meta),
+ None => String::new(),
+ }
+ )
+ .unwrap();
+ allocs.push(ptr.alloc_id);
}
- }
+ ptr => write!(msg, " by integral ref: {:?}", ptr).unwrap(),
+ },
LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
write!(msg, " {:?}", val).unwrap();
if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val {
trace!("{}", msg);
self.memory.dump_allocs(allocs);
}
- Place::Ptr(mplace) => {
- match mplace.ptr {
- Scalar::Ptr(ptr) => {
- trace!("by align({}) ref:", mplace.align.bytes());
- self.memory.dump_alloc(ptr.alloc_id);
- }
- ptr => trace!(" integral by ref: {:?}", ptr),
+ Place::Ptr(mplace) => match mplace.ptr {
+ Scalar::Ptr(ptr) => {
+ trace!("by align({}) ref:", mplace.align.bytes());
+ self.memory.dump_alloc(ptr.alloc_id);
}
- }
+ ptr => trace!(" integral by ref: {:?}", ptr),
+ },
}
}
}
impl<'ctx, 'mir, 'tcx, Tag, Extra> HashStable<StableHashingContext<'ctx>>
-for Frame<'mir, 'tcx, Tag, Extra>
- where Extra: HashStable<StableHashingContext<'ctx>>,
- Tag: HashStable<StableHashingContext<'ctx>>
+ for Frame<'mir, 'tcx, Tag, Extra>
+where
+ Extra: HashStable<StableHashingContext<'ctx>>,
+ Tag: HashStable<StableHashingContext<'ctx>>,
{
fn hash_stable(&self, hcx: &mut StableHashingContext<'ctx>, hasher: &mut StableHasher) {
self.body.hash_stable(hcx, hasher);