Introduce a ConstPropMachine

This allows us to avoid changing things directly in the miri engine just
for const prop.
This commit is contained in:
Wesley Wiser 2019-09-24 21:12:59 -04:00
parent 4e58e2e3a3
commit dcc6c28c53
8 changed files with 177 additions and 39 deletions

View File

@ -389,14 +389,6 @@ pub enum UnsupportedOpInfo<'tcx> {
/// Free-form case. Only for errors that are never caught!
Unsupported(String),
/// Error used by the `ConstProp` pass when an attempt is made
/// to read an uninitialized local.
UninitializedLocal,
/// Error used by the `ConstProp` pass to prevent reading statics
/// while evaluating `const` items.
ReadOfStaticInConst,
/// FIXME(#64506) Error used to work around accessing projections of
/// uninhabited types.
UninhabitedValue,
@ -523,8 +515,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
addresses, e.g., comparing pointers into different allocations"),
DeadLocal =>
write!(f, "tried to access a dead local variable"),
UninitializedLocal =>
write!(f, "tried to access an uninitialized local variable"),
DerefFunctionPointer =>
write!(f, "tried to dereference a function pointer"),
ExecuteMemory =>
@ -566,8 +556,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
not a power of two"),
Unsupported(ref msg) =>
write!(f, "{}", msg),
ReadOfStaticInConst =>
write!(f, "tried to read from a static during const evaluation"),
UninhabitedValue =>
write!(f, "tried to use an uninhabited value"),
}

View File

@ -21,7 +21,7 @@
use crate::interpret::{self,
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
RawConst, ConstValue,
RawConst, ConstValue, Machine,
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
Allocation, AllocId, MemoryKind, Memory,
snapshot, RefTracking, intern_const_alloc_recursive,
@ -41,7 +41,7 @@
/// that inform us about the generic bounds of the constant. E.g., using an associated constant
/// of a function's generic parameter will require knowledge about the bounds on the generic
/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
pub(crate) fn mk_eval_cx<'mir, 'tcx>(
fn mk_eval_cx<'mir, 'tcx>(
tcx: TyCtxt<'tcx>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
@ -169,7 +169,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
}
#[derive(Clone, Debug)]
enum ConstEvalError {
pub enum ConstEvalError {
NeedsRfc(String),
}
@ -521,8 +521,8 @@ pub fn const_variant_index<'tcx>(
/// Turn an interpreter error into something to report to the user.
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
/// Should be called only if the error is actually going to to be reported!
pub fn error_to_const_error<'mir, 'tcx>(
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
pub fn error_to_const_error<'mir, 'tcx, M: Machine<'mir, 'tcx>>(
ecx: &InterpCx<'mir, 'tcx, M>,
mut error: InterpErrorInfo<'tcx>,
) -> ConstEvalErr<'tcx> {
error.print_backtrace();

View File

@ -135,8 +135,7 @@ pub fn access(&self) -> InterpResult<'tcx, Operand<Tag>> {
match self.value {
LocalValue::Dead => throw_unsup!(DeadLocal),
LocalValue::Uninitialized =>
// this is reachable from ConstProp
throw_unsup!(UninitializedLocal),
bug!("The type checker should prevent reading from a never-written local"),
LocalValue::Live(val) => Ok(val),
}
}

View File

@ -12,6 +12,7 @@
use super::{
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
Frame, Operand,
};
/// Whether this kind of memory is allowed to leak
@ -184,6 +185,23 @@ fn box_alloc(
dest: PlaceTy<'tcx, Self::PointerTag>,
) -> InterpResult<'tcx>;
/// Called to read the specified `local` from the `frame`.
fn access_local(
_ecx: &InterpCx<'mir, 'tcx, Self>,
frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
local: mir::Local,
) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
frame.locals[local].access()
}
/// Called before a `StaticKind::Static` value is read.
fn before_eval_static(
_ecx: &InterpCx<'mir, 'tcx, Self>,
_place_static: &mir::Static<'tcx>,
) -> InterpResult<'tcx> {
Ok(())
}
/// Called to initialize the "extra" state of an allocation and make the pointers
/// it contains (in relocations) tagged. The way we construct allocations is
/// to always first construct it without extra and then add the extra.

View File

@ -458,7 +458,7 @@ pub fn access_local(
// Do not read from ZST, they might not be initialized
Operand::Immediate(Scalar::zst().into())
} else {
frame.locals[local].access()?
M::access_local(&self, frame, local)?
};
Ok(OpTy { op, layout })
}

View File

@ -601,15 +601,8 @@ pub(super) fn eval_static_to_mplace(
}
StaticKind::Static => {
//if the first frame on the stack isn't a static item, then we shouldn't
//eval any static places (unless -Z unleash-the-miri-inside-of-you is on)
if let ty::InstanceDef::Item(item_def_id) = self.stack[0].instance.def {
if !self.tcx.is_static(item_def_id) &&
!self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
trace!("eval_static_to_mplace: can't eval static in constant");
throw_unsup!(ReadOfStaticInConst);
}
}
M::before_eval_static(self, place_static)?;
let ty = place_static.ty;
assert!(!ty.needs_subst());
let layout = self.layout_of(ty)?;

View File

@ -1,14 +1,16 @@
//! Propagates constants for early reporting of statically known
//! assertion failures
use std::borrow::Cow;
use std::cell::Cell;
use rustc::hir::def::DefKind;
use rustc::hir::def_id::DefId;
use rustc::mir::{
AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue,
Local, NullOp, UnOp, StatementKind, Statement, LocalKind,
Local, NullOp, UnOp, StatementKind, Statement, LocalKind, Static,
TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp,
SourceScope, SourceScopeLocalData, LocalDecl,
SourceScope, SourceScopeLocalData, LocalDecl, BasicBlock,
};
use rustc::mir::visit::{
Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
@ -17,6 +19,7 @@
use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
use syntax_pos::{Span, DUMMY_SP};
use rustc::ty::subst::InternalSubsts;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::ty::layout::{
LayoutOf, TyLayout, LayoutError, HasTyCtxt, TargetDataLayout, HasDataLayout,
@ -24,11 +27,11 @@
use crate::interpret::{
self, InterpCx, ScalarMaybeUndef, Immediate, OpTy,
StackPopCleanup, LocalValue, LocalState,
};
use crate::const_eval::{
CompileTimeInterpreter, error_to_const_error, mk_eval_cx,
StackPopCleanup, LocalValue, LocalState, AllocId, Frame,
Allocation, MemoryKind, ImmTy, Pointer, Memory, PlaceTy,
Operand as InterpOperand,
};
use crate::const_eval::error_to_const_error;
use crate::transform::{MirPass, MirSource};
pub struct ConstProp;
@ -111,11 +114,149 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'
}
}
struct ConstPropMachine;
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
type MemoryKinds= !;
type PointerTag = ();
type ExtraFnVal = !;
type FrameExtra = ();
type MemoryExtra = ();
type AllocExtra = ();
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
const STATIC_KIND: Option<!> = None;
const CHECK_ALIGN: bool = false;
#[inline(always)]
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
false
}
fn find_fn(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
_args: &[OpTy<'tcx>],
_dest: Option<PlaceTy<'tcx>>,
_ret: Option<BasicBlock>,
) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
Ok(None)
}
fn call_extra_fn(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
fn_val: !,
_args: &[OpTy<'tcx>],
_dest: Option<PlaceTy<'tcx>>,
_ret: Option<BasicBlock>,
) -> InterpResult<'tcx> {
match fn_val {}
}
fn call_intrinsic(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
_args: &[OpTy<'tcx>],
_dest: PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
throw_unsup_format!("calling intrinsics isn't supported in ConstProp");
}
fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer,
) -> InterpResult<'tcx, u64> {
throw_unsup_format!("ptr-to-int casts aren't supported in ConstProp");
}
fn binary_ptr_op(
_ecx: &InterpCx<'mir, 'tcx, Self>,
_bin_op: BinOp,
_left: ImmTy<'tcx>,
_right: ImmTy<'tcx>,
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
// We can't do this because aliasing of memory can differ between const eval and llvm
throw_unsup_format!("pointer arithmetic or comparisons aren't supported in ConstProp");
}
fn find_foreign_static(
_tcx: TyCtxt<'tcx>,
_def_id: DefId,
) -> InterpResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>> {
throw_unsup!(ReadForeignStatic)
}
#[inline(always)]
fn tag_allocation<'b>(
_memory_extra: &(),
_id: AllocId,
alloc: Cow<'b, Allocation>,
_kind: Option<MemoryKind<!>>,
) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
// We do not use a tag so we can just cheaply forward the allocation
(alloc, ())
}
#[inline(always)]
fn tag_static_base_pointer(
_memory_extra: &(),
_id: AllocId,
) -> Self::PointerTag {
()
}
fn box_alloc(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_dest: PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
throw_unsup_format!("can't const prop `box` keyword");
}
fn access_local(
_ecx: &InterpCx<'mir, 'tcx, Self>,
frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
local: Local,
) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> {
let l = &frame.locals[local];
if l.value == LocalValue::Uninitialized {
throw_unsup_format!("tried to access an uninitialized local");
}
l.access()
}
fn before_eval_static(
_ecx: &InterpCx<'mir, 'tcx, Self>,
_place_static: &Static<'tcx>,
) -> InterpResult<'tcx> {
throw_unsup_format!("can't eval statics in ConstProp");
}
fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
Ok(())
}
#[inline(always)]
fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
Ok(())
}
/// Called immediately before a stack frame gets popped.
#[inline(always)]
fn stack_pop(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _extra: ()) -> InterpResult<'tcx> {
Ok(())
}
}
type Const<'tcx> = OpTy<'tcx>;
/// Finds optimization opportunities on the MIR.
struct ConstPropagator<'mir, 'tcx> {
ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
ecx: InterpCx<'mir, 'tcx, ConstPropMachine>,
tcx: TyCtxt<'tcx>,
source: MirSource<'tcx>,
can_const_prop: IndexVec<Local, bool>,
@ -158,7 +299,7 @@ fn new(
let def_id = source.def_id();
let param_env = tcx.param_env(def_id);
let span = tcx.def_span(def_id);
let mut ecx = mk_eval_cx(tcx, span, param_env);
let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
let can_const_prop = CanConstProp::check(body);
ecx.push_stack_frame(

View File

@ -6,5 +6,4 @@ LL | static B: [u32; 1] = [0; A.len()];
error: aborting due to previous error
Some errors have detailed explanations: E0013, E0080.
For more information about an error, try `rustc --explain E0013`.
For more information about this error, try `rustc --explain E0013`.