/// The locals are stored as `Option<Value>`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`.
- pub locals: IndexVec<mir::Local, LocalValue<Tag>>,
- pub local_layouts: IndexVec<mir::Local, Cell<Option<TyLayout<'tcx>>>>,
+ pub locals: IndexVec<mir::Local, LocalValue<'tcx, Tag>>,
////////////////////////////////////////////////////////////////////////////////
// Current position within the function
None { cleanup: bool },
}
-// State of a local variable
+/// State of a local variable including a memoized layout
+#[derive(Clone, PartialEq, Eq)]
+pub struct LocalValue<'tcx, Tag=(), Id=AllocId> {
+ pub state: LocalState<Tag, Id>,
+ /// Don't modify if `Some`, this is only used to prevent computing the layout twice
+ pub layout: Cell<Option<TyLayout<'tcx>>>,
+}
+
+/// State of a local variable
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub enum LocalValue<Tag=(), Id=AllocId> {
+pub enum LocalState<Tag=(), Id=AllocId> {
Dead,
// Mostly for convenience, we re-use the `Operand` type here.
// This is an optimization over just always having a pointer here;
Live(Operand<Tag, Id>),
}
-impl<'tcx, Tag> LocalValue<Tag> {
+impl<'tcx, Tag> LocalValue<'tcx, Tag> {
pub fn access(&self) -> EvalResult<'tcx, &Operand<Tag>> {
- match self {
- LocalValue::Dead => err!(DeadLocal),
- LocalValue::Live(ref val) => Ok(val),
+ match self.state {
+ LocalState::Dead => err!(DeadLocal),
+ LocalState::Live(ref val) => Ok(val),
}
}
pub fn access_mut(&mut self) -> EvalResult<'tcx, &mut Operand<Tag>> {
- match self {
- LocalValue::Dead => err!(DeadLocal),
- LocalValue::Live(ref mut val) => Ok(val),
+ match self.state {
+ LocalState::Dead => err!(DeadLocal),
+ LocalState::Live(ref mut val) => Ok(val),
}
}
}
frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
local: mir::Local
) -> EvalResult<'tcx, TyLayout<'tcx>> {
- let cell = &frame.local_layouts[local];
+ let cell = &frame.locals[local].layout;
if cell.get().is_none() {
let local_ty = frame.mir.local_decls[local].ty;
let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs);
// empty local array, we fill it in below, after we are inside the stack frame and
// all methods actually know about the frame
locals: IndexVec::new(),
- local_layouts: IndexVec::from_elem_n(Default::default(), mir.local_decls.len()),
span,
instance,
stmt: 0,
// don't allocate at all for trivial constants
if mir.local_decls.len() > 1 {
// We put some marker immediate into the locals that we later want to initialize.
- // This can be anything except for LocalValue::Dead -- because *that* is the
+ // This can be anything except for LocalState::Dead -- because *that* is the
// value we use for things that we know are initially dead.
- let dummy =
- LocalValue::Live(Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Undef)));
+ let dummy = LocalValue {
+ state: LocalState::Live(Operand::Immediate(Immediate::Scalar(
+ ScalarMaybeUndef::Undef,
+ ))),
+ layout: Cell::new(None),
+ };
let mut locals = IndexVec::from_elem(dummy, &mir.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.
- locals[mir::RETURN_PLACE] = LocalValue::Dead;
+ locals[mir::RETURN_PLACE].state = LocalState::Dead;
// Now mark those locals as dead that we do not want to initialize
match self.tcx.describe_def(instance.def_id()) {
// statics and constants don't have `Storage*` statements, no need to look for them
match stmt.kind {
StorageLive(local) |
StorageDead(local) => {
- locals[local] = LocalValue::Dead;
+ locals[local].state = LocalState::Dead;
}
_ => {}
}
}
// Finally, properly initialize all those that still have the dummy value
for (idx, local) in locals.iter_enumerated_mut() {
- match *local {
- LocalValue::Live(_) => {
+ match local.state {
+ LocalState::Live(_) => {
// This needs to be peoperly initialized.
let layout = self.layout_of_local(self.frame(), idx)?;
- *local = LocalValue::Live(self.uninit_operand(layout)?);
+ local.state = LocalState::Live(self.uninit_operand(layout)?);
}
- LocalValue::Dead => {
+ LocalState::Dead => {
// Nothing to do
}
}
}
// Deallocate all locals that are backed by an allocation.
for local in frame.locals {
- self.deallocate_local(local)?;
+ self.deallocate_local(local.state)?;
}
// Validate the return value. Do this after deallocating so that we catch dangling
// references.
pub fn storage_live(
&mut self,
local: mir::Local
- ) -> EvalResult<'tcx, LocalValue<M::PointerTag>> {
+ ) -> EvalResult<'tcx, LocalState<M::PointerTag>> {
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
trace!("{:?} is now live", local);
let layout = self.layout_of_local(self.frame(), local)?;
- let init = LocalValue::Live(self.uninit_operand(layout)?);
+ let init = LocalState::Live(self.uninit_operand(layout)?);
// StorageLive *always* kills the value that's currently stored
- Ok(mem::replace(&mut self.frame_mut().locals[local], init))
+ Ok(mem::replace(&mut self.frame_mut().locals[local].state, init))
}
/// Returns the old value of the local.
/// Remember to deallocate that!
- pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue<M::PointerTag> {
+ pub fn storage_dead(&mut self, local: mir::Local) -> LocalState<M::PointerTag> {
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
trace!("{:?} is now dead", local);
- mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
+ mem::replace(&mut self.frame_mut().locals[local].state, LocalState::Dead)
}
pub(super) fn deallocate_local(
&mut self,
- local: LocalValue<M::PointerTag>,
+ local: LocalState<M::PointerTag>,
) -> EvalResult<'tcx> {
// 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 {
+ if let LocalState::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
trace!("deallocating local");
let ptr = ptr.to_ptr()?;
self.memory.dump_alloc(ptr.alloc_id);
use std::hash::{Hash, Hasher};
-use rustc::ich::StableHashingContextProvider;
+use rustc::ich::{StableHashingContextProvider, StableHashingContext};
use rustc::mir;
use rustc::mir::interpret::{
AllocId, Pointer, Scalar,
use rustc::ty::layout::Align;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::IndexVec;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use syntax::ast::Mutability;
use syntax::source_map::Span;
use super::eval_context::{LocalValue, StackPopCleanup};
-use super::{Frame, Memory, Operand, MemPlace, Place, Immediate, ScalarMaybeUndef};
+use super::{Frame, Memory, Operand, MemPlace, Place, Immediate, ScalarMaybeUndef, LocalState};
use const_eval::CompileTimeInterpreter;
#[derive(Default)]
Indirect(m),
});
-impl_stable_hash_for!(enum ::interpret::LocalValue {
+impl_stable_hash_for!(enum ::interpret::LocalState {
Dead,
Live(x),
});
-impl_snapshot_for!(enum LocalValue {
+impl_snapshot_for!(enum LocalState {
Live(v),
Dead,
});
span: &'a Span,
return_to_block: &'a StackPopCleanup,
return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
- locals: IndexVec<mir::Local, LocalValue<(), AllocIdSnapshot<'a>>>,
+ locals: IndexVec<mir::Local, LocalState<(), AllocIdSnapshot<'a>>>,
block: &'a mir::BasicBlock,
stmt: usize,
}
return_to_block,
return_place -> (return_place.as_ref().map(|r| &**r)),
locals,
- local_layouts -> _,
block,
stmt,
extra,
return_to_block,
return_place,
locals,
- local_layouts: _,
block,
stmt,
extra: _,
}
}
+impl<'a, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a LocalValue<'tcx>
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = LocalState<(), AllocIdSnapshot<'a>>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ self.state.snapshot(ctx)
+ }
+}
+
+
+impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for LocalValue<'gcx> {
+ fn hash_stable<W: StableHasherResult>(
+ &self,
+ hcx: &mut StableHashingContext<'a>,
+ hasher: &mut StableHasher<W>,
+ ) {
+ self.state.hash_stable(hcx, hasher);
+ }
+}
+
impl<'a, 'b, 'mir, 'tcx: 'a+'mir> SnapshotContext<'b>
for Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>
{