Introduce a ConstPropMachine
This allows us to avoid changing things directly in the miri engine just for const prop.
This commit is contained in:
parent
4e58e2e3a3
commit
dcc6c28c53
@ -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"),
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 })
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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(
|
||||
|
@ -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`.
|
||||
|
Loading…
Reference in New Issue
Block a user