Skip to content

Fixes and cleanups #58000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 31, 2019
98 changes: 57 additions & 41 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ pub struct Frame<'mir, 'tcx: 'mir, Tag=(), Extra=()> {
/// 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
Expand Down Expand Up @@ -106,9 +105,17 @@ pub enum StackPopCleanup {
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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think swapping the names LocalValue and LocalState is nicer. The layout stuff looks much more state-y than value-y to me.

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;
Expand All @@ -117,18 +124,18 @@ pub enum LocalValue<Tag=(), Id=AllocId> {
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),
}
}
}
Expand Down Expand Up @@ -310,17 +317,21 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
pub fn layout_of_local(
&self,
frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
local: mir::Local
local: mir::Local,
layout: Option<TyLayout<'tcx>>,
) -> EvalResult<'tcx, TyLayout<'tcx>> {
let cell = &frame.local_layouts[local];
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);
let layout = self.layout_of(local_ty)?;
cell.set(Some(layout));
match frame.locals[local].layout.get() {
None => {
let layout = ::interpret::operand::from_known_layout(layout, || {
let local_ty = frame.mir.local_decls[local].ty;
let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs);
self.layout_of(local_ty)
})?;
frame.locals[local].layout.set(Some(layout));
Ok(layout)
}
Some(layout) => Ok(layout),
}

Ok(cell.get().unwrap())
}

pub fn str_to_immediate(&mut self, s: &str) -> EvalResult<'tcx, Immediate<M::PointerTag>> {
Expand Down Expand Up @@ -454,7 +465,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
// 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,
Expand All @@ -464,14 +474,18 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
// 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
Expand All @@ -484,7 +498,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
match stmt.kind {
StorageLive(local) |
StorageDead(local) => {
locals[local] = LocalValue::Dead;
locals[local].state = LocalState::Dead;
}
_ => {}
}
Expand All @@ -494,13 +508,15 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
}
// Finally, properly initialize all those that still have the dummy value
for (idx, local) in locals.iter_enumerated_mut() {
match *local {
LocalValue::Live(_) => {
// This needs to be peoperly initialized.
let layout = self.layout_of_local(self.frame(), idx)?;
*local = LocalValue::Live(self.uninit_operand(layout)?);
match local.state {
LocalState::Live(_) => {
// This needs to be properly initialized.
let ty = self.monomorphize(mir.local_decls[idx].ty)?;
let layout = self.layout_of(ty)?;
local.state = LocalState::Live(self.uninit_operand(layout)?);
local.layout = Cell::new(Some(layout));
}
LocalValue::Dead => {
LocalState::Dead => {
// Nothing to do
}
}
Expand Down Expand Up @@ -543,7 +559,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
}
// 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.
Expand Down Expand Up @@ -587,31 +603,31 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
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 layout = self.layout_of_local(self.frame(), local, None)?;
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);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod visitor;
pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here

pub use self::eval_context::{
EvalContext, Frame, StackPopCleanup, LocalValue,
EvalContext, Frame, StackPopCleanup, LocalValue, LocalState,
};

pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
Expand Down
14 changes: 8 additions & 6 deletions src/librustc_mir/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl<'tcx, Tag> OpTy<'tcx, Tag>
// Use the existing layout if given (but sanity check in debug mode),
// or compute the layout.
#[inline(always)]
fn from_known_layout<'tcx>(
pub(super) fn from_known_layout<'tcx>(
layout: Option<TyLayout<'tcx>>,
compute: impl FnOnce() -> EvalResult<'tcx, TyLayout<'tcx>>
) -> EvalResult<'tcx, TyLayout<'tcx>> {
Expand Down Expand Up @@ -457,14 +457,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
}

/// This is used by [priroda](https://github.com/oli-obk/priroda) to get an OpTy from a local
fn access_local(
pub fn access_local(
&self,
frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
local: mir::Local,
layout: Option<TyLayout<'tcx>>,
) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
assert_ne!(local, mir::RETURN_PLACE);
let op = *frame.locals[local].access()?;
let layout = self.layout_of_local(frame, local)?;
let layout = self.layout_of_local(frame, local, layout)?;
Ok(OpTy { op, layout })
}

Expand All @@ -473,14 +474,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
fn eval_place_to_op(
&self,
mir_place: &mir::Place<'tcx>,
layout: Option<TyLayout<'tcx>>,
) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
use rustc::mir::Place::*;
let op = match *mir_place {
Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer),
Local(local) => self.access_local(self.frame(), local)?,
Local(local) => self.access_local(self.frame(), local, layout)?,

Projection(ref proj) => {
let op = self.eval_place_to_op(&proj.base)?;
let op = self.eval_place_to_op(&proj.base, None)?;
self.operand_projection(op, &proj.elem)?
}

Expand All @@ -504,7 +506,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
// FIXME: do some more logic on `move` to invalidate the old location
Copy(ref place) |
Move(ref place) =>
self.eval_place_to_op(place)?,
self.eval_place_to_op(place, layout)?,

Constant(ref constant) => {
let layout = from_known_layout(layout, || {
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ where
// their layout on return.
PlaceTy {
place: *return_place,
layout: self.layout_of_local(self.frame(), mir::RETURN_PLACE)?,
layout: self.layout_of(self.monomorphize(self.frame().mir.return_ty())?)?,
},
None => return err!(InvalidNullPointerUsage),
},
Expand All @@ -633,7 +633,7 @@ where
frame: self.cur_frame(),
local,
},
layout: self.layout_of_local(self.frame(), local)?,
layout: self.layout_of_local(self.frame(), local, None)?,
},

Projection(ref proj) => {
Expand Down Expand Up @@ -901,7 +901,7 @@ where
// We need the layout of the local. We can NOT use the layout we got,
// that might e.g., be an inner field of a struct with `Scalar` layout,
// that has different alignment than the outer field.
let local_layout = self.layout_of_local(&self.stack[frame], local)?;
let local_layout = self.layout_of_local(&self.stack[frame], local, None)?;
let ptr = self.allocate(local_layout, MemoryKind::Stack);
// We don't have to validate as we can assume the local
// was already valid for its type.
Expand Down
35 changes: 27 additions & 8 deletions src/librustc_mir/interpret/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use std::hash::{Hash, Hasher};

use rustc::ich::StableHashingContextProvider;
use rustc::ich::{StableHashingContextProvider, StableHashingContext};
use rustc::mir;
use rustc::mir::interpret::{
AllocId, Pointer, Scalar,
Expand All @@ -19,12 +19,12 @@ use rustc::ty::{self, TyCtxt};
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)]
Expand Down Expand Up @@ -250,11 +250,11 @@ impl_snapshot_for!(enum Operand {
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,
});
Expand Down Expand Up @@ -309,7 +309,7 @@ struct FrameSnapshot<'a, 'tcx: 'a> {
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,
}
Expand All @@ -321,7 +321,6 @@ impl_stable_hash_for!(impl<'mir, 'tcx: 'mir> for struct Frame<'mir, 'tcx> {
return_to_block,
return_place -> (return_place.as_ref().map(|r| &**r)),
locals,
local_layouts -> _,
block,
stmt,
extra,
Expand All @@ -340,7 +339,6 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
return_to_block,
return_place,
locals,
local_layouts: _,
block,
stmt,
extra: _,
Expand All @@ -358,6 +356,27 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
}
}

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We otherwise follow the pattern to use LocalState { state, layout: _ } = self to make sure we revisit this when new fields get added.

impl_snapshot_for! doesn't work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, it's a very simple macro with no support for lifetimes or so

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done and opened rust-lang/rust-clippy#3724 to make sure we take care of this issue once and for all at some point

}
}


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>>
{
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
mir.spread_arg,
mir.args_iter()
.map(|local|
(local, self.layout_of_local(self.frame(), local).unwrap().ty)
(local, self.layout_of_local(self.frame(), local, None).unwrap().ty)
)
.collect::<Vec<_>>()
);
Expand Down Expand Up @@ -383,7 +383,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
}
} else {
let callee_layout =
self.layout_of_local(self.frame(), mir::RETURN_PLACE)?;
self.layout_of_local(self.frame(), mir::RETURN_PLACE, None)?;
if !callee_layout.abi.is_uninhabited() {
return err!(FunctionRetMismatch(
self.tcx.types.never, callee_layout.ty
Expand Down