Scalar, Allocation, AllocId, ConstValue,
};
use interpret::{self,
- Place, PlaceTy, MemPlace, OpTy, Operand, Value,
+ PlaceTy, MemPlace, OpTy, Operand, Value,
EvalContext, StackPopCleanup, MemoryKind,
snapshot,
};
let param_env = tcx.param_env(instance.def_id());
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
// insert a stack frame so any queries have the correct substs
+ // cannot use `push_stack_frame`; if we do `const_prop` explodes
ecx.stack.push(interpret::Frame {
block: mir::START_BLOCK,
locals: IndexVec::new(),
instance,
span,
mir,
- return_place: Place::null(tcx),
+ return_place: None,
return_to_block: StackPopCleanup::Goto(None), // never pop
stmt: 0,
});
instance,
mir.span,
mir,
- Place::null(tcx),
+ None,
StackPopCleanup::Goto(None), // never pop
)?;
Ok(ecx)
cid.instance,
mir.span,
mir,
- Place::Ptr(*ret),
+ Some(ret.into()),
StackPopCleanup::None { cleanup: false },
)?;
use syntax::source_map::{self, Span};
use super::{
- Value, Operand, MemPlace, MPlaceTy, Place, ScalarMaybeUndef,
+ Value, Operand, MemPlace, MPlaceTy, Place, PlaceTy, ScalarMaybeUndef,
Memory, Machine
};
/// Work to perform when returning from this function
pub return_to_block: StackPopCleanup,
- /// The location where the result of the current stack frame should be written to.
- pub return_place: Place<Tag>,
+ /// The location where the result of the current stack frame should be written to,
+ /// and its layout in the caller.
+ pub return_place: Option<PlaceTy<'tcx, Tag>>,
/// The list of locals for this stack frame, stored in order as
/// `[return_ptr, arguments..., variables..., temporaries...]`.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum StackPopCleanup {
/// Jump to the next block in the caller, or cause UB if None (that's a function
- /// that may never return).
+ /// that may never return). Also store layout of return place so
+ /// we can validate it at that layout.
Goto(Option<mir::BasicBlock>),
/// Just do nohing: Used by Main and for the box_alloc hook in miri.
/// `cleanup` says whether locals are deallocated. Static computation
instance: ty::Instance<'tcx>,
span: source_map::Span,
mir: &'mir mir::Mir<'tcx>,
- return_place: Place<M::PointerTag>,
+ return_place: Option<PlaceTy<'tcx, M::PointerTag>>,
return_to_block: StackPopCleanup,
) -> EvalResult<'tcx> {
::log_settings::settings().indentation += 1;
}
StackPopCleanup::None { cleanup } => {
if !cleanup {
- // Leak the locals
+ // Leak the locals. Also skip validation, this is only used by
+ // static/const computation which does its own (stronger) final
+ // validation.
return Ok(());
}
}
}
- // deallocate all locals that are backed by an allocation
+ // Deallocate all locals that are backed by an allocation.
for local in frame.locals {
self.deallocate_local(local)?;
}
+ // Validate the return value.
+ if let Some(return_place) = frame.return_place {
+ if M::ENFORCE_VALIDITY {
+ // Data got changed, better make sure it matches the type!
+ // It is still possible that the return place held invalid data while
+ // the function is running, but that's okay because nobody could have
+ // accessed that same data from the "outside" to observe any broken
+ // 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)?,
+ &mut vec![],
+ None,
+ /*const_mode*/false,
+ )?;
+ }
+ } else {
+ // Uh, that shouln't happen... the function did not intend to return
+ return err!(Unreachable);
+ }
Ok(())
}
}
impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> {
- /// Produces a Place that will error if attempted to be read from or written to
- #[inline]
- pub fn null(cx: impl HasDataLayout, layout: TyLayout<'tcx>) -> Self {
- PlaceTy { place: Place::from_scalar_ptr(Scalar::ptr_null(cx), layout.align), layout }
- }
-
#[inline]
pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
MPlaceTy { mplace: self.place.to_mem_place(), layout: self.layout }
) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::Place::*;
let place = match *mir_place {
- Local(mir::RETURN_PLACE) => PlaceTy {
- place: self.frame().return_place,
- layout: self.layout_of_local(self.cur_frame(), mir::RETURN_PLACE)?,
+ Local(mir::RETURN_PLACE) => match self.frame().return_place {
+ Some(return_place) =>
+ // We use our layout to verify our assumption; caller will validate
+ // their layout on return.
+ PlaceTy {
+ place: *return_place,
+ layout: self.layout_of_local(self.cur_frame(), mir::RETURN_PLACE)?,
+ },
+ None => return err!(InvalidNullPointerUsage),
},
Local(local) => PlaceTy {
place: Place::Local {
instance: &'a ty::Instance<'tcx>,
span: &'a Span,
return_to_block: &'a StackPopCleanup,
- return_place: Place<(), AllocIdSnapshot<'a>>,
+ return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
locals: IndexVec<mir::Local, LocalValue<(), AllocIdSnapshot<'a>>>,
block: &'a mir::BasicBlock,
stmt: usize,
} = self;
(mir, instance, span, return_to_block).hash_stable(hcx, hasher);
- (return_place, locals, block, stmt).hash_stable(hcx, hasher);
+ (return_place.as_ref().map(|r| &**r), locals, block, stmt).hash_stable(hcx, hasher);
}
}
impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
return_to_block,
block,
stmt: *stmt,
- return_place: return_place.snapshot(ctx),
+ return_place: return_place.map(|r| r.snapshot(ctx)),
locals: locals.iter().map(|local| local.snapshot(ctx)).collect(),
}
}
use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar};
use super::{
- EvalContext, Machine, Value, OpTy, Place, PlaceTy, Operand, StackPopCleanup
+ EvalContext, Machine, Value, OpTy, PlaceTy, MPlaceTy, Operand, StackPopCleanup
};
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
use rustc::mir::TerminatorKind::*;
match terminator.kind {
Return => {
- self.dump_place(self.frame().return_place);
+ self.frame().return_place.map(|r| self.dump_place(*r));
self.pop_stack_frame()?
}
None => return Ok(()),
};
- let return_place = match dest {
- Some(place) => *place,
- None => Place::null(&self), // any access will error. good!
- };
self.push_stack_frame(
instance,
span,
mir,
- return_place,
+ dest,
StackPopCleanup::Goto(ret),
)?;
return err!(FunctionArgCountMismatch);
}
// Don't forget to check the return type!
- let callee_ret = self.eval_place(&mir::Place::Local(mir::RETURN_PLACE))?;
if let Some(caller_ret) = dest {
+ let callee_ret = self.eval_place(&mir::Place::Local(mir::RETURN_PLACE))?;
if !Self::check_argument_compat(caller_ret.layout, callee_ret.layout) {
return err!(FunctionRetMismatch(
caller_ret.layout.ty, callee_ret.layout.ty
));
}
} else {
- if !callee_ret.layout.abi.is_uninhabited() {
+ let callee_layout =
+ self.layout_of_local(self.cur_frame(), mir::RETURN_PLACE)?;
+ if !callee_layout.abi.is_uninhabited() {
return err!(FunctionRetMismatch(
- self.tcx.types.never, callee_ret.layout.ty
+ self.tcx.types.never, callee_layout.ty
));
}
}
};
let ty = self.tcx.mk_unit(); // return type is ()
- let dest = PlaceTy::null(&self, self.layout_of(ty)?);
+ let dest = MPlaceTy::dangling(self.layout_of(ty)?, &self);
self.eval_fn_call(
instance,
span,
Abi::Rust,
&[arg],
- Some(dest),
+ Some(dest.into()),
Some(target),
)
}