From dcc6c28c53e6f59a6e966b47ec872a7d26e2b23b Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 24 Sep 2019 21:12:59 -0400 Subject: [PATCH] Introduce a `ConstPropMachine` This allows us to avoid changing things directly in the miri engine just for const prop. --- src/librustc/mir/interpret/error.rs | 12 -- src/librustc_mir/const_eval.rs | 10 +- src/librustc_mir/interpret/eval_context.rs | 3 +- src/librustc_mir/interpret/machine.rs | 18 +++ src/librustc_mir/interpret/operand.rs | 2 +- src/librustc_mir/interpret/place.rs | 11 +- src/librustc_mir/transform/const_prop.rs | 157 +++++++++++++++++++-- src/test/ui/issues/issue-52060.stderr | 3 +- 8 files changed, 177 insertions(+), 39 deletions(-) diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 4da5d979cc3e8..71967b513a049 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -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 @@ 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 => @@ -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"), } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index ca238867421ab..bb02b99dd8d87 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -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, @@ -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>, @@ -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(); diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index bc7a5a1a7c374..fdf85260c3d96 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -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), } } diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index bb74a50156e56..e0be53b80d734 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -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 @@ -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> { + 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. diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 89af0031bfd26..861e5ebef877d 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -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 }) } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 1ece913eb59a4..c882f4ef4ddf3 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -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)?; diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index d7d58327865b9..ce1acc3b7edf9 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/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, @@ -17,6 +19,7 @@ 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, @@ -24,11 +27,11 @@ use rustc::ty::layout::{ 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 @@ 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, 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>, + _ret: Option, + ) -> 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>, + _ret: Option, + ) -> 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>> { + throw_unsup!(ReadForeignStatic) + } + + #[inline(always)] + fn tag_allocation<'b>( + _memory_extra: &(), + _id: AllocId, + alloc: Cow<'b, Allocation>, + _kind: Option>, + ) -> (Cow<'b, Allocation>, 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> { + 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, @@ -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( diff --git a/src/test/ui/issues/issue-52060.stderr b/src/test/ui/issues/issue-52060.stderr index 6fbc1ed52c478..2f90f7f9e035b 100644 --- a/src/test/ui/issues/issue-52060.stderr +++ b/src/test/ui/issues/issue-52060.stderr @@ -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`.