about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWesley Wiser <wwiser@gmail.com>2019-09-24 21:12:59 -0400
committerWesley Wiser <wwiser@gmail.com>2019-09-27 20:11:13 -0400
commitdcc6c28c53e6f59a6e966b47ec872a7d26e2b23b (patch)
tree2370be1acc6d03db908a1e8afe86f19d00a81bdc
parent4e58e2e3a3f3cda15bad6cbb87242eaf555dda85 (diff)
downloadrust-dcc6c28c53e6f59a6e966b47ec872a7d26e2b23b.tar.gz
rust-dcc6c28c53e6f59a6e966b47ec872a7d26e2b23b.zip
Introduce a `ConstPropMachine`
This allows us to avoid changing things directly in the miri engine just
for const prop.
-rw-r--r--src/librustc/mir/interpret/error.rs12
-rw-r--r--src/librustc_mir/const_eval.rs10
-rw-r--r--src/librustc_mir/interpret/eval_context.rs3
-rw-r--r--src/librustc_mir/interpret/machine.rs18
-rw-r--r--src/librustc_mir/interpret/operand.rs2
-rw-r--r--src/librustc_mir/interpret/place.rs11
-rw-r--r--src/librustc_mir/transform/const_prop.rs157
-rw-r--r--src/test/ui/issues/issue-52060.stderr3
8 files changed, 177 insertions, 39 deletions
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index 4da5d979cc3..71967b513a0 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 ca238867421..bb02b99dd8d 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 bc7a5a1a7c3..fdf85260c3d 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 bb74a50156e..e0be53b80d7 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<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.
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 89af0031bfd..861e5ebef87 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 1ece913eb59..c882f4ef4dd 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 d7d58327865..ce1acc3b7ed 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<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 @@ 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 6fbc1ed52c4..2f90f7f9e03 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`.