about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-12-01 04:49:27 +0100
committerGitHub <noreply@github.com>2019-12-01 04:49:27 +0100
commitd91e63b7a2b92a66e6dd34dd76485fd906447111 (patch)
tree997fabe151f83fd3eb4325e1bfe8cd9adb60655e
parent75fd413e7b5eefacfc85b8c313fe3dfbc149ed1e (diff)
parent5d23518a12548fdbbca74fcc3171fdc2f3888334 (diff)
downloadrust-d91e63b7a2b92a66e6dd34dd76485fd906447111.tar.gz
rust-d91e63b7a2b92a66e6dd34dd76485fd906447111.zip
Rollup merge of #66832 - RalfJung:const-prop-no-alloc, r=oli-obk
const_prop: detect and avoid catching Miri errors that require allocation

r? @wesleywiser @oli-obk
-rw-r--r--src/librustc/mir/interpret/error.rs20
-rw-r--r--src/librustc_mir/transform/const_prop.rs32
2 files changed, 38 insertions, 14 deletions
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index 98e6770c70d..9f133597ab4 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -44,14 +44,14 @@ CloneTypeFoldableImpls! {
 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
 pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
 
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug)]
 pub struct ConstEvalErr<'tcx> {
     pub span: Span,
     pub error: crate::mir::interpret::InterpError<'tcx>,
     pub stacktrace: Vec<FrameInfo<'tcx>>,
 }
 
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, Debug)]
 pub struct FrameInfo<'tcx> {
     /// This span is in the caller.
     pub call_site: Span,
@@ -331,7 +331,7 @@ impl<O: fmt::Debug> fmt::Debug for PanicInfo<O> {
 /// Error information for when the program we executed turned out not to actually be a valid
 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
 /// where we work on generic code or execution does not have all information available.
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, HashStable)]
 pub enum InvalidProgramInfo<'tcx> {
     /// Resolution can fail if we are in a too generic context.
     TooGeneric,
@@ -361,7 +361,7 @@ impl fmt::Debug for InvalidProgramInfo<'tcx> {
 }
 
 /// Error information for when the program caused Undefined Behavior.
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, HashStable)]
 pub enum UndefinedBehaviorInfo {
     /// Free-form case. Only for errors that are never caught!
     Ub(String),
@@ -394,11 +394,15 @@ impl fmt::Debug for UndefinedBehaviorInfo {
 ///
 /// Currently, we also use this as fall-back error kind for errors that have not been
 /// categorized yet.
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, HashStable)]
 pub enum UnsupportedOpInfo<'tcx> {
     /// Free-form case. Only for errors that are never caught!
     Unsupported(String),
 
+    /// When const-prop encounters a situation it does not support, it raises this error.
+    /// This must not allocate for performance reasons.
+    ConstPropUnsupported(&'tcx str),
+
     // -- Everything below is not categorized yet --
     FunctionAbiMismatch(Abi, Abi),
     FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
@@ -559,13 +563,15 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
                     not a power of two"),
             Unsupported(ref msg) =>
                 write!(f, "{}", msg),
+            ConstPropUnsupported(ref msg) =>
+                write!(f, "Constant propagation encountered an unsupported situation: {}", msg),
         }
     }
 }
 
 /// Error information for when the program exhausted the resources granted to it
 /// by the interpreter.
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, HashStable)]
 pub enum ResourceExhaustionInfo {
     /// The stack grew too big.
     StackFrameLimitReached,
@@ -586,7 +592,7 @@ impl fmt::Debug for ResourceExhaustionInfo {
     }
 }
 
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, HashStable)]
 pub enum InterpError<'tcx> {
     /// The program panicked.
     Panic(PanicInfo<u64>),
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index d2740cf3771..0fe75301fc1 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -168,14 +168,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
         _unwind: Option<BasicBlock>
     ) -> InterpResult<'tcx> {
-        throw_unsup_format!("calling intrinsics isn't supported in ConstProp");
+        throw_unsup!(ConstPropUnsupported("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");
+        throw_unsup!(ConstPropUnsupported("ptr-to-int casts aren't supported in ConstProp"));
     }
 
     fn binary_ptr_op(
@@ -185,7 +185,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _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");
+        throw_unsup!(ConstPropUnsupported("pointer arithmetic or comparisons aren't supported \
+            in ConstProp"));
     }
 
     fn find_foreign_static(
@@ -218,7 +219,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
         _dest: PlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
-        throw_unsup_format!("can't const prop `box` keyword");
+        throw_unsup!(ConstPropUnsupported("can't const prop `box` keyword"));
     }
 
     fn access_local(
@@ -229,7 +230,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         let l = &frame.locals[local];
 
         if l.value == LocalValue::Uninitialized {
-            throw_unsup_format!("tried to access an uninitialized local");
+            throw_unsup!(ConstPropUnsupported("tried to access an uninitialized local"));
         }
 
         l.access()
@@ -241,7 +242,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         // if the static allocation is mutable or if it has relocations (it may be legal to mutate
         // the memory behind that in the future), then we can't const prop it
         if allocation.mutability == Mutability::Mutable || allocation.relocations().len() > 0 {
-            throw_unsup_format!("can't eval mutable statics in ConstProp");
+            throw_unsup!(ConstPropUnsupported("can't eval mutable statics in ConstProp"));
         }
 
         Ok(())
@@ -389,9 +390,26 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         let r = match f(self) {
             Ok(val) => Some(val),
             Err(error) => {
-                use rustc::mir::interpret::InterpError::*;
+                use rustc::mir::interpret::{
+                    UnsupportedOpInfo,
+                    UndefinedBehaviorInfo,
+                    InterpError::*
+                };
                 match error.kind {
                     Exit(_) => bug!("the CTFE program cannot exit"),
+
+                    // Some error shouldn't come up because creating them causes
+                    // an allocation, which we should avoid. When that happens,
+                    // dedicated error variants should be introduced instead.
+                    // Only test this in debug builds though to avoid disruptions.
+                    Unsupported(UnsupportedOpInfo::Unsupported(_))
+                    | Unsupported(UnsupportedOpInfo::ValidationFailure(_))
+                    | UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
+                    | UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_))
+                      if cfg!(debug_assertions) => {
+                        bug!("const-prop encountered allocating error: {:?}", error.kind);
+                    }
+
                     Unsupported(_)
                     | UndefinedBehavior(_)
                     | InvalidProgram(_)