Skip to content

Commit

Permalink
Introduce a ConstPropMachine
Browse files Browse the repository at this point in the history
This allows us to avoid changing things directly in the miri engine just
for const prop.
  • Loading branch information
wesleywiser committed Sep 28, 2019
1 parent 4e58e2e commit dcc6c28
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 39 deletions.
12 changes: 0 additions & 12 deletions src/librustc/mir/interpret/error.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -523,8 +515,6 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
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 =>
Expand Down Expand Up @@ -566,8 +556,6 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
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"),
}
Expand Down
10 changes: 5 additions & 5 deletions src/librustc_mir/const_eval.rs
Expand Up @@ -21,7 +21,7 @@ use syntax::source_map::{Span, DUMMY_SP};

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,
Expand All @@ -41,7 +41,7 @@ const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
/// 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>,
Expand Down Expand Up @@ -169,7 +169,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
}

#[derive(Clone, Debug)]
enum ConstEvalError {
pub enum ConstEvalError {
NeedsRfc(String),
}

Expand Down Expand Up @@ -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();
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_mir/interpret/eval_context.rs
Expand Up @@ -135,8 +135,7 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, 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),
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/librustc_mir/interpret/machine.rs
Expand Up @@ -12,6 +12,7 @@ use rustc::ty::{self, Ty, TyCtxt};
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
Expand Down Expand Up @@ -184,6 +185,23 @@ pub trait Machine<'mir, 'tcx>: Sized {
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.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/operand.rs
Expand Up @@ -458,7 +458,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// 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 })
}
Expand Down
11 changes: 2 additions & 9 deletions src/librustc_mir/interpret/place.rs
Expand Up @@ -601,15 +601,8 @@ where
}

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)?;
Expand Down
157 changes: 149 additions & 8 deletions src/librustc_mir/transform/const_prop.rs
@@ -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,
Expand All @@ -17,18 +19,19 @@ use rustc::mir::interpret::{Scalar, InterpResult, PanicInfo};
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,
};

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;
Expand Down Expand Up @@ -111,11 +114,149 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
}
}

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>,
Expand Down Expand Up @@ -158,7 +299,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
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(
Expand Down
3 changes: 1 addition & 2 deletions src/test/ui/issues/issue-52060.stderr
Expand Up @@ -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`.

0 comments on commit dcc6c28

Please sign in to comment.