move CTFE engine snapshot state out of miri engine into CTFE machine instance

This commit is contained in:
Ralf Jung 2018-09-20 10:12:21 +02:00
parent ff6422d7a3
commit 169f7911e9
16 changed files with 270 additions and 286 deletions

View File

@ -22,7 +22,7 @@ use rustc::ty::subst::Subst;
use rustc_data_structures::indexed_vec::IndexVec;
use syntax::ast::Mutability;
use syntax::source_map::Span;
use syntax::source_map::{Span, DUMMY_SP};
use rustc::mir::interpret::{
EvalResult, EvalError, EvalErrorKind, GlobalId,
@ -31,6 +31,7 @@ use rustc::mir::interpret::{
use interpret::{self,
Place, PlaceTy, MemPlace, OpTy, Operand, Value,
EvalContext, StackPopCleanup, MemoryKind,
snapshot,
};
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
@ -38,10 +39,10 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
instance: Instance<'tcx>,
mir: &'mir mir::Mir<'tcx>,
span: Span,
) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
debug!("mk_borrowck_eval_cx: {:?}", instance);
let param_env = tcx.param_env(instance.def_id());
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator::new(), ());
// insert a stack frame so any queries have the correct substs
ecx.stack.push(interpret::Frame {
block: mir::START_BLOCK,
@ -60,10 +61,10 @@ pub fn mk_eval_cx<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
let span = tcx.def_span(instance.def_id());
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator::new(), ());
let mir = ecx.load_mir(instance.def)?;
// insert a stack frame so any queries have the correct substs
ecx.push_stack_frame(
@ -77,18 +78,17 @@ pub fn mk_eval_cx<'a, 'tcx>(
}
pub fn eval_promoted<'a, 'mir, 'tcx>(
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
cid: GlobalId<'tcx>,
mir: &'mir mir::Mir<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> EvalResult<'tcx, OpTy<'tcx>> {
ecx.with_fresh_body(|ecx| {
eval_body_using_ecx(ecx, cid, Some(mir), param_env)
})
let mut ecx = mk_borrowck_eval_cx(tcx, cid.instance, mir, DUMMY_SP).unwrap();
eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env)
}
pub fn op_to_const<'tcx>(
ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
op: OpTy<'tcx>,
normalize: bool,
) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
@ -128,19 +128,19 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
cid: GlobalId<'tcx>,
mir: Option<&'mir mir::Mir<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
) -> (EvalResult<'tcx, OpTy<'tcx>>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
) -> (EvalResult<'tcx, OpTy<'tcx>>, CompileTimeEvalContext<'a, 'mir, 'tcx>) {
// we start out with the best span we have
// and try improving it down the road when more information is available
let span = tcx.def_span(cid.instance.def_id());
let span = mir.map(|mir| mir.span).unwrap_or(span);
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator::new(), ());
let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
(r, ecx)
}
// Returns a pointer to where the result lives
fn eval_body_using_ecx<'a, 'mir, 'tcx>(
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
fn eval_body_using_ecx<'mir, 'tcx>(
ecx: &mut CompileTimeEvalContext<'_, 'mir, 'tcx>,
cid: GlobalId<'tcx>,
mir: Option<&'mir mir::Mir<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
@ -187,17 +187,12 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
Ok(ret.into())
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct CompileTimeEvaluator;
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
EvalErrorKind::MachineError(self.to_string()).into()
}
}
impl_stable_hash_for!(struct CompileTimeEvaluator {});
#[derive(Clone, Debug)]
enum ConstEvalError {
NeedsRfc(String),
@ -234,14 +229,39 @@ impl Error for ConstEvalError {
}
}
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
// Extra machine state for CTFE, and the Machine instance
pub struct CompileTimeEvaluator<'a, 'mir, 'tcx: 'a+'mir> {
/// 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(super) steps_since_detector_enabled: isize,
/// Extra state to detect loops.
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'a, 'mir, 'tcx>,
}
impl<'a, 'mir, 'tcx> CompileTimeEvaluator<'a, 'mir, 'tcx> {
fn new() -> Self {
CompileTimeEvaluator {
loop_detector: Default::default(),
steps_since_detector_enabled: -snapshot::STEPS_UNTIL_DETECTOR_ENABLED,
}
}
}
type CompileTimeEvalContext<'a, 'mir, 'tcx> =
EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>;
impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
for CompileTimeEvaluator<'a, 'mir, 'tcx>
{
type MemoryData = ();
type MemoryKinds = !;
const MUT_STATIC_KIND: Option<!> = None; // no mutating of statics allowed
const DETECT_LOOPS: bool = true;
fn find_fn<'a>(
fn find_fn(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
@ -275,7 +295,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
}))
}
fn call_intrinsic<'a>(
fn call_intrinsic(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
@ -291,7 +311,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
)
}
fn ptr_op<'a>(
fn ptr_op(
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
_bin_op: mir::BinOp,
_left: Scalar,
@ -304,14 +324,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
)
}
fn find_foreign_static<'a>(
fn find_foreign_static(
_tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
_def_id: DefId,
) -> EvalResult<'tcx, &'tcx Allocation> {
err!(ReadForeignStatic)
}
fn box_alloc<'a>(
fn box_alloc(
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
_dest: PlaceTy<'tcx>,
) -> EvalResult<'tcx> {
@ -319,6 +339,36 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
)
}
fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
{
let steps = &mut ecx.machine.steps_since_detector_enabled;
*steps += 1;
if *steps < 0 {
return Ok(());
}
*steps %= snapshot::DETECTOR_SNAPSHOT_PERIOD;
if *steps != 0 {
return Ok(());
}
}
if ecx.machine.loop_detector.is_empty() {
// First run of the loop detector
// FIXME(#49980): make this warning a lint
ecx.tcx.sess.span_warn(ecx.frame().span,
"Constant evaluating a complex constant, this might take some time");
}
ecx.machine.loop_detector.observe_and_analyze(
&ecx.tcx,
&ecx.memory,
&ecx.stack[..],
)
}
}
/// Project to a field of a (variant of a) const

View File

@ -21,7 +21,7 @@ use rustc_apfloat::Float;
use super::{EvalContext, Machine, PlaceTy, OpTy, Value};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
match ty.sty {
ty::RawPtr(ty::TypeAndMut { ty, .. }) |

View File

@ -14,7 +14,6 @@ use std::mem;
use rustc::hir::def_id::DefId;
use rustc::hir::def::Def;
use rustc::hir::map::definitions::DefPathData;
use rustc::ich::StableHashingContext;
use rustc::mir;
use rustc::ty::layout::{
self, Size, Align, HasDataLayout, LayoutOf, TyLayout
@ -23,7 +22,6 @@ use rustc::ty::subst::{Subst, Substs};
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::query::TyCtxtAt;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use rustc::mir::interpret::{
GlobalId, Scalar, FrameInfo, AllocId,
EvalResult, EvalErrorKind,
@ -38,9 +36,7 @@ use super::{
Memory, Machine
};
use super::snapshot::InfiniteLoopDetector;
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
/// Stores the `Machine` instance.
pub machine: M,
@ -55,19 +51,6 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
/// The virtual call stack.
pub(crate) stack: Vec<Frame<'mir, 'tcx>>,
/// The maximum number of stack frames allowed
pub(super) 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(super) steps_since_detector_enabled: isize,
/// Extra state to detect loops.
/// FIXME: Move this to the CTFE machine's state, out of the general miri engine.
pub(super) loop_detector: InfiniteLoopDetector<'a, 'mir, 'tcx, M>,
}
/// A stack frame.
@ -112,29 +95,6 @@ pub struct Frame<'mir, 'tcx: 'mir> {
pub stmt: usize,
}
// Not using the macro because that does not support types depending on 'tcx
impl<'a, 'mir, 'tcx: 'mir> HashStable<StableHashingContext<'a>> for Frame<'mir, 'tcx> {
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
let Frame {
mir,
instance,
span,
return_to_block,
return_place,
locals,
block,
stmt,
} = self;
(mir, instance, span, return_to_block).hash_stable(hcx, hasher);
(return_place, locals, block, stmt).hash_stable(hcx, hasher);
}
}
#[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
@ -147,21 +107,6 @@ pub enum StackPopCleanup {
None { cleanup: bool },
}
// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup {
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher),
StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher),
}
}
}
// State of a local variable
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum LocalValue<Id=AllocId> {
@ -189,19 +134,16 @@ impl<'tcx> LocalValue {
}
}
impl_stable_hash_for!(enum self::LocalValue {
Dead,
Live(x),
});
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> {
impl<'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
for &'b 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
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M>
{
#[inline]
@ -210,24 +152,27 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
}
}
impl<'a, 'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>
impl<'b, 'a, 'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for &'b EvalContext<'a, 'mir, 'tcx, M>
where M: Machine<'a, 'mir, 'tcx>
{
#[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> {
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'a, '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<'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> LayoutOf
for &'b EvalContext<'a, 'mir, 'tcx, M>
{
type Ty = Ty<'tcx>;
type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
@ -238,8 +183,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for &'a EvalContext<'a, 'm
}
}
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M> {
impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> LayoutOf
for &'c &'b mut EvalContext<'a, 'mir, 'tcx, M>
{
type Ty = Ty<'tcx>;
type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
@ -249,9 +195,7 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf
}
}
const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
pub fn new(
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
@ -264,22 +208,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
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<F: FnOnce(&mut Self) -> 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 memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
&self.memory
}
@ -553,7 +484,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
self.frame_mut().locals = locals;
}
if self.stack.len() > self.stack_limit {
if self.stack.len() > self.tcx.sess.const_eval_stack_frame_limit {
err!(StackFrameLimitReached)
} else {
Ok(())

View File

@ -46,7 +46,7 @@ fn numeric_intrinsic<'tcx>(
Ok(Scalar::from_uint(bits_out, size))
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Returns whether emulation happened.
pub fn emulate_intrinsic(
&mut self,

View File

@ -20,20 +20,22 @@ use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
use super::{EvalContext, PlaceTy, OpTy};
/// Methods of this trait signifies a point where CTFE evaluation would fail
/// and some use case dependent behaviour can instead be applied
pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// and some use case dependent behaviour can instead be applied.
/// FIXME: We should be able to get rid of the 'a here if we can get rid of the 'a in
/// `snapshot::EvalSnapshot`.
pub trait Machine<'a, 'mir, 'tcx>: Sized {
/// Additional data that can be accessed via the Memory
type MemoryData: Clone + Eq;
type MemoryData;
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq;
type MemoryKinds: ::std::fmt::Debug + Copy + Eq;
/// The memory kind to use for mutated statics -- or None if those are not supported.
const MUT_STATIC_KIND: Option<Self::MemoryKinds>;
/// Whether to attempt to detect infinite loops (any kind of infinite
/// execution, really).
const DETECT_LOOPS: bool;
/// Called before a basic block terminator is executed.
/// You can use this to detect endlessly running programs.
fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>;
/// Entry point to all function calls.
///
@ -45,7 +47,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// nor just jump to `ret`, but instead push their own stack frame.)
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
/// was used.
fn find_fn<'a>(
fn find_fn(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
@ -55,7 +57,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// Directly process an intrinsic without pushing a stack frame.
/// If this returns successfully, the engine will take care of jumping to the next block.
fn call_intrinsic<'a>(
fn call_intrinsic(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
@ -66,7 +68,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// This can be called multiple times for the same static item and should return consistent
/// results. Once the item is *written* the first time, as usual for statics a copy is
/// made and this function is not called again.
fn find_foreign_static<'a>(
fn find_foreign_static(
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
def_id: DefId,
) -> EvalResult<'tcx, &'tcx Allocation>;
@ -75,7 +77,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// value, and for the `Offset` operation that is inherently about pointers.
///
/// Returns a (value, overflowed) pair if the operation succeeded
fn ptr_op<'a>(
fn ptr_op(
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
bin_op: mir::BinOp,
left: Scalar,
@ -87,13 +89,13 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq {
/// Heap allocations via the `box` keyword
///
/// Returns a pointer to the allocated memory
fn box_alloc<'a>(
fn box_alloc(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
dest: PlaceTy<'tcx>,
) -> EvalResult<'tcx>;
/// Execute a validation operation
fn validation_op<'a>(
fn validation_op(
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
_op: ::rustc::mir::ValidationOp,
_operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,

View File

@ -39,8 +39,9 @@ pub enum MemoryKind<T> {
Machine(T),
}
#[derive(Clone)]
pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
// `Memory` has to depend on the `Machine` because some of its operations
// (e.g. `get`) call a `Machine` hook.
pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
/// Additional data required by the Machine
pub data: M::MemoryData,
@ -59,13 +60,15 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a Memory<'a, 'mir, 'tcx, M> {
impl<'b, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
for &'b Memory<'a, 'mir, 'tcx, M>
{
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
&self.tcx.data_layout
}
}
impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
for &'b &'c mut Memory<'a, 'mir, 'tcx, M>
{
#[inline]
@ -74,7 +77,23 @@ impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
}
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
// FIXME: Really we shouldnt clone memory, ever. Snapshot machinery should instad
// carefully copy only the reachable parts.
impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>>
Clone for Memory<'a, 'mir, 'tcx, M>
where M::MemoryData: Clone
{
fn clone(&self) -> Self {
Memory {
data: self.data.clone(),
alloc_map: self.alloc_map.clone(),
dead_alloc_map: self.dead_alloc_map.clone(),
tcx: self.tcx,
}
}
}
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
Memory {
data,
@ -279,7 +298,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
}
/// Allocation accessors
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
/// Helper function to obtain the global (tcx) allocation for a static
fn get_static_alloc(
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
@ -491,7 +510,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
}
/// Byte accessors
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
/// The last argument controls whether we error out when there are undefined
/// or pointer bytes. You should never call this, call `get_bytes` or
/// `get_bytes_with_undef_and_ptr` instead,
@ -564,7 +583,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
}
/// Reading and writing
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
/// mark an allocation as static and initialized, either mutable or not
pub fn intern_static(
&mut self,
@ -877,7 +896,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
}
/// Relocations
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
/// Return all relocations overlapping with the given ptr-offset pair.
fn relocations(
&self,
@ -950,7 +969,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
}
/// Undefined bytes
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
// FIXME(solson): This is a very naive, slow version.
fn copy_undef_mask(
&mut self,

View File

@ -17,7 +17,7 @@ mod operand;
mod machine;
mod memory;
mod operator;
mod snapshot;
pub(crate) mod snapshot; // for const_eval
mod step;
mod terminator;
mod traits;

View File

@ -82,11 +82,6 @@ impl<'tcx> Value {
}
}
impl_stable_hash_for!(enum ::interpret::Value {
Scalar(x),
ScalarPair(x, y),
});
// ScalarPair needs a type to interpret, so we often have a value and a type together
// as input for binary and cast operations.
#[derive(Copy, Clone, Debug)]
@ -132,11 +127,6 @@ impl Operand {
}
}
impl_stable_hash_for!(enum ::interpret::Operand {
Immediate(x),
Indirect(x),
});
#[derive(Copy, Clone, Debug)]
pub struct OpTy<'tcx> {
crate op: Operand, // ideally we'd make this private, but const_prop needs this
@ -206,7 +196,7 @@ fn from_known_layout<'tcx>(
}
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Try reading a value in memory; this is interesting particularily for ScalarPair.
/// Return None if the layout does not permit loading this as a value.
pub(super) fn try_read_value_from_mplace(

View File

@ -18,7 +18,7 @@ use rustc::mir::interpret::{EvalResult, Scalar};
use super::{EvalContext, PlaceTy, Value, Machine, ValTy};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
/// and a boolean signifying the potential overflow to the destination.
pub fn binop_with_overflow(
@ -47,7 +47,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
}
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
fn binary_char_op(
&self,
bin_op: mir::BinOp,

View File

@ -13,13 +13,10 @@
//! All high-level functions to write to memory work on places as destinations.
use std::convert::TryFrom;
use std::mem;
use rustc::ich::StableHashingContext;
use rustc::mir;
use rustc::ty::{self, Ty};
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use rustc::mir::interpret::{
GlobalId, AllocId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic
@ -39,12 +36,6 @@ pub struct MemPlace<Id=AllocId> {
pub extra: Option<Scalar<Id>>,
}
impl_stable_hash_for!(struct ::interpret::MemPlace {
ptr,
align,
extra,
});
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Place<Id=AllocId> {
/// A place referring to a value allocated in the `Memory` system.
@ -58,23 +49,6 @@ pub enum Place<Id=AllocId> {
},
}
// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for Place {
fn hash_stable<W: StableHasherResult>(
&self, hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),
Place::Local { frame, local } => {
frame.hash_stable(hcx, hasher);
local.hash_stable(hcx, hasher);
},
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct PlaceTy<'tcx> {
place: Place,
@ -255,7 +229,7 @@ impl<'tcx> PlaceTy<'tcx> {
}
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Take a value, which represents a (thin or fat) reference, and make it a place.
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref`.
pub fn ref_to_mplace(

View File

@ -2,7 +2,11 @@
//! during const-evaluation by taking snapshots of the state of the interpreter
//! at regular intervals.
// This lives in `interpret` because it needs access to all sots of private state. However,
// it is not used by the general miri engine, just by CTFE.
use std::hash::{Hash, Hasher};
use std::mem;
use rustc::ich::{StableHashingContext, StableHashingContextProvider};
use rustc::mir;
@ -21,9 +25,18 @@ use syntax::ast::Mutability;
use syntax::source_map::Span;
use super::eval_context::{LocalValue, StackPopCleanup};
use super::{Frame, Memory, Machine, Operand, MemPlace, Place, Value};
use super::{Frame, Memory, Operand, MemPlace, Place, Value};
use const_eval::CompileTimeEvaluator;
pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
/// Number of steps until the detector even starts doing anything.
/// Also, a warning is shown to the user when this number is reached.
pub(crate) const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
/// The number of steps between loop detector snapshots.
/// Should be a power of two for performance reasons.
pub(crate) const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
#[derive(Default)]
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
/// The set of all `EvalSnapshot` *hashes* observed by this detector.
///
/// When a collision occurs in this table, we store the full snapshot in
@ -35,34 +48,20 @@ pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mi
/// 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<EvalSnapshot<'a, 'mir, 'tcx, M>>,
snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx>>,
}
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,
impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
{
/// 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(
pub fn observe_and_analyze<'b>(
&mut self,
tcx: &TyCtxt<'b, 'tcx, 'tcx>,
memory: &Memory<'a, 'mir, 'tcx, M>,
memory: &Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>,
stack: &[Frame<'mir, 'tcx>],
) -> EvalResult<'tcx, ()> {
@ -179,7 +178,7 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId
impl_snapshot_for!(struct Pointer {
alloc_id,
offset -> *offset,
offset -> *offset, // just copy offset verbatim
});
impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar
@ -203,12 +202,34 @@ impl_snapshot_for!(enum ScalarMaybeUndef {
Undef,
});
impl_stable_hash_for!(struct ::interpret::MemPlace {
ptr,
align,
extra,
});
impl_snapshot_for!(struct MemPlace {
ptr,
extra,
align -> *align,
align -> *align, // just copy alignment verbatim
});
// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for Place {
fn hash_stable<W: StableHasherResult>(
&self, hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),
Place::Local { frame, local } => {
frame.hash_stable(hcx, hasher);
local.hash_stable(hcx, hasher);
},
}
}
}
impl<'a, Ctx> Snapshot<'a, Ctx> for Place
where Ctx: SnapshotContext<'a>,
{
@ -226,16 +247,28 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Place
}
}
impl_stable_hash_for!(enum ::interpret::Value {
Scalar(x),
ScalarPair(x, y),
});
impl_snapshot_for!(enum Value {
Scalar(s),
ScalarPair(s, t),
});
impl_stable_hash_for!(enum ::interpret::Operand {
Immediate(x),
Indirect(x),
});
impl_snapshot_for!(enum Operand {
Immediate(v),
Indirect(m),
});
impl_stable_hash_for!(enum ::interpret::LocalValue {
Dead,
Live(x),
});
impl_snapshot_for!(enum LocalValue {
Live(v),
Dead,
@ -280,6 +313,21 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
}
}
// Can't use the macro here because that does not support named enum fields.
impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup {
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>)
{
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher),
StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher),
}
}
}
#[derive(Eq, PartialEq)]
struct FrameSnapshot<'a, 'tcx: 'a> {
instance: &'a ty::Instance<'tcx>,
@ -291,6 +339,28 @@ struct FrameSnapshot<'a, 'tcx: 'a> {
stmt: usize,
}
// Not using the macro because that does not support types depending on 'tcx
impl<'a, 'mir, 'tcx: 'mir> HashStable<StableHashingContext<'a>> for Frame<'mir, 'tcx> {
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
let Frame {
mir,
instance,
span,
return_to_block,
return_place,
locals,
block,
stmt,
} = self;
(mir, instance, span, return_to_block).hash_stable(hcx, hasher);
(return_place, locals, block, stmt).hash_stable(hcx, hasher);
}
}
impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
where Ctx: SnapshotContext<'a>,
{
@ -320,22 +390,8 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
}
}
#[derive(Eq, PartialEq)]
struct MemorySnapshot<'a, 'mir: 'a, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx> + 'a> {
data: &'a M::MemoryData,
}
impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
{
fn snapshot<'b: 'a>(&'b self) -> MemorySnapshot<'b, 'mir, 'tcx, M> {
let Memory { data, .. } = self;
MemorySnapshot { data }
}
}
impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
impl<'a, 'b, 'mir, 'tcx: 'a+'mir> SnapshotContext<'b>
for Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>
{
fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> {
self.get(*id).ok()
@ -343,16 +399,17 @@ impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M>
}
/// The virtual machine state during const-evaluation at a given point in time.
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
memory: Memory<'a, 'mir, 'tcx, M>,
/// We assume the `CompileTimeEvaluator` has no interesting extra state that
/// is worth considering here.
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir> {
memory: Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>,
stack: Vec<Frame<'mir, 'tcx>>,
}
impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
impl<'a, 'mir, 'tcx: 'a + 'mir> EvalSnapshot<'a, 'mir, 'tcx>
{
fn new(
memory: &Memory<'a, 'mir, 'tcx, M>,
memory: &Memory<'a, 'mir, 'tcx, CompileTimeEvaluator<'a, 'mir, 'tcx>>,
stack: &[Frame<'mir, 'tcx>]
) -> Self {
EvalSnapshot {
@ -361,16 +418,17 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
}
}
fn snapshot<'b: 'a>(&'b self)
-> (MemorySnapshot<'b, 'mir, 'tcx, M>, Vec<FrameSnapshot<'a, 'tcx>>)
// Used to compare two snapshots
fn snapshot(&'b self)
-> Vec<FrameSnapshot<'b, 'tcx>>
{
let EvalSnapshot{ memory, stack } = self;
(memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect())
// Start with the stack, iterate and recursively snapshot
self.stack.iter().map(|frame| frame.snapshot(&self.memory)).collect()
}
}
impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
impl<'a, 'mir, 'tcx> Hash for EvalSnapshot<'a, 'mir, 'tcx>
{
fn hash<H: Hasher>(&self, state: &mut H) {
// Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2)
@ -383,26 +441,24 @@ impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M>
// Not using the macro because we need special handling for `memory`, which the macro
// does not support at the same time as the extra bounds on the type.
impl<'a, 'b, 'mir, 'tcx, M> HashStable<StableHashingContext<'b>>
for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
impl<'a, 'b, 'mir, 'tcx> HashStable<StableHashingContext<'b>>
for EvalSnapshot<'a, 'mir, 'tcx>
{
fn hash_stable<W: StableHasherResult>(
&self,
hcx: &mut StableHashingContext<'b>,
hasher: &mut StableHasher<W>)
{
// Not hashing memory: Avoid hashing memory all the time during execution
let EvalSnapshot{ memory: _, stack } = self;
stack.hash_stable(hcx, hasher);
}
}
impl<'a, 'mir, 'tcx, M> Eq for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
impl<'a, 'mir, 'tcx> Eq for EvalSnapshot<'a, 'mir, 'tcx>
{}
impl<'a, 'mir, 'tcx, M> PartialEq for EvalSnapshot<'a, 'mir, 'tcx, M>
where M: Machine<'mir, 'tcx>,
impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
{
fn eq(&self, other: &Self) -> bool {
self.snapshot() == other.snapshot()

View File

@ -45,45 +45,7 @@ fn binop_right_homogeneous(op: mir::BinOp) -> bool {
}
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
pub fn inc_step_counter_and_detect_loops(&mut self) -> EvalResult<'tcx, ()> {
/// The number of steps between loop detector snapshots.
/// Should be a power of two for performance reasons.
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
{
let steps = &mut self.steps_since_detector_enabled;
*steps += 1;
if *steps < 0 {
return Ok(());
}
*steps %= DETECTOR_SNAPSHOT_PERIOD;
if *steps != 0 {
return Ok(());
}
}
if !M::DETECT_LOOPS {
return Ok(());
}
if self.loop_detector.is_empty() {
// First run of the loop detector
// FIXME(#49980): make this warning a lint
self.tcx.sess.span_warn(self.frame().span,
"Constant evaluating a complex constant, this might take some time");
}
self.loop_detector.observe_and_analyze(
&self.tcx,
&self.memory,
&self.stack[..],
)
}
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
pub fn run(&mut self) -> EvalResult<'tcx> {
while self.step()? {}
Ok(())
@ -108,7 +70,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
return Ok(true);
}
self.inc_step_counter_and_detect_loops()?;
M::before_terminator(self)?;
let terminator = basic_block.terminator();
assert_eq!(old_frames, self.cur_frame());

View File

@ -20,7 +20,7 @@ use super::{
EvalContext, Machine, Value, OpTy, Place, PlaceTy, Operand, StackPopCleanup
};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
#[inline]
pub fn goto_block(&mut self, target: Option<mir::BasicBlock>) -> EvalResult<'tcx> {
if let Some(target) = target {

View File

@ -16,7 +16,7 @@ use syntax::ast::Mutability;
use super::{EvalContext, Machine, MemoryKind};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
/// objects.
///

View File

@ -95,7 +95,7 @@ fn path_format(path: &Vec<PathElem>) -> String {
out
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
fn validate_scalar(
&self,
value: ScalarMaybeUndef,

View File

@ -69,7 +69,7 @@ type Const<'tcx> = (OpTy<'tcx>, Span);
/// Finds optimization opportunities on the MIR.
struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
ecx: EvalContext<'a, 'b, 'tcx, CompileTimeEvaluator>,
ecx: EvalContext<'a, 'b, 'tcx, CompileTimeEvaluator<'a, 'b, 'tcx>>,
mir: &'b Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: MirSource,
@ -310,7 +310,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
// cannot use `const_eval` here, because that would require having the MIR
// for the current function available, but we're producing said MIR right now
let res = self.use_ecx(source_info, |this| {
eval_promoted(&mut this.ecx, cid, this.mir, this.param_env)
eval_promoted(this.tcx, cid, this.mir, this.param_env)
})?;
trace!("evaluated promoted {:?} to {:?}", promoted, res);
Some((res, source_info.span))