about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/any.rs2
-rw-r--r--src/librustc/mir/interpret/error.rs49
-rw-r--r--src/librustc/mir/interpret/mod.rs4
-rw-r--r--src/librustc_mir/transform/const_prop.rs32
4 files changed, 63 insertions, 24 deletions
diff --git a/src/libcore/any.rs b/src/libcore/any.rs
index 97ef513cbcc..39df803bbea 100644
--- a/src/libcore/any.rs
+++ b/src/libcore/any.rs
@@ -164,7 +164,7 @@ impl dyn Any {
         // Get `TypeId` of the type this function is instantiated with.
         let t = TypeId::of::<T>();
 
-        // Get `TypeId` of the type in the trait object.
+        // Get `TypeId` of the type in the trait object (`self`).
         let concrete = self.type_id();
 
         // Compare both `TypeId`s on equality.
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index ff107a5f1e2..fd7f5361214 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -14,7 +14,10 @@ use rustc_hir as hir;
 use rustc_macros::HashStable;
 use rustc_session::CtfeBacktrace;
 use rustc_span::{def_id::DefId, Pos, Span};
-use std::{any::Any, fmt};
+use std::{
+    any::{Any, TypeId},
+    fmt, mem,
+};
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
 pub enum ErrorHandled {
@@ -451,9 +454,6 @@ impl fmt::Debug for UndefinedBehaviorInfo {
 pub enum UnsupportedOpInfo {
     /// 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 (hence `str`, not `String`).
-    ConstPropUnsupported(&'static str),
     /// Accessing an unsupported foreign static.
     ReadForeignStatic(DefId),
     /// Could not find MIR for a function.
@@ -472,9 +472,6 @@ impl fmt::Debug for UnsupportedOpInfo {
         use UnsupportedOpInfo::*;
         match self {
             Unsupported(ref msg) => write!(f, "{}", msg),
-            ConstPropUnsupported(ref msg) => {
-                write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
-            }
             ReadForeignStatic(did) => {
                 write!(f, "tried to read from foreign (extern) static {:?}", did)
             }
@@ -516,6 +513,35 @@ impl fmt::Debug for ResourceExhaustionInfo {
     }
 }
 
+/// A trait for machine-specific errors (or other "machine stop" conditions).
+pub trait MachineStopType: Any + fmt::Debug + Send {}
+impl MachineStopType for String {}
+
+// Copy-pasted from `any.rs`; there does not seem to be a way to re-use that.
+impl dyn MachineStopType {
+    pub fn is<T: Any>(&self) -> bool {
+        // Get `TypeId` of the type this function is instantiated with.
+        let t = TypeId::of::<T>();
+
+        // Get `TypeId` of the type in the trait object (`self`).
+        let concrete = self.type_id();
+
+        // Compare both `TypeId`s on equality.
+        t == concrete
+    }
+
+    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
+        if self.is::<T>() {
+            // SAFETY: just checked whether we are pointing to the correct type, and we can rely on
+            // that check for memory safety because `Any` is implemented for all types; no other
+            // impls can exist as they would conflict with our impl.
+            unsafe { Some(&*(self as *const dyn MachineStopType as *const T)) }
+        } else {
+            None
+        }
+    }
+}
+
 pub enum InterpError<'tcx> {
     /// The program caused undefined behavior.
     UndefinedBehavior(UndefinedBehaviorInfo),
@@ -529,7 +555,7 @@ pub enum InterpError<'tcx> {
     ResourceExhaustion(ResourceExhaustionInfo),
     /// Stop execution for a machine-controlled reason. This is never raised by
     /// the core engine itself.
-    MachineStop(Box<dyn Any + Send>),
+    MachineStop(Box<dyn MachineStopType>),
 }
 
 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
@@ -549,7 +575,7 @@ impl fmt::Debug for InterpError<'_> {
             InvalidProgram(ref msg) => write!(f, "{:?}", msg),
             UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
             ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
-            MachineStop(_) => bug!("unhandled MachineStop"),
+            MachineStop(ref msg) => write!(f, "{:?}", msg),
         }
     }
 }
@@ -560,8 +586,9 @@ impl InterpError<'_> {
     /// waste of resources.
     pub fn allocates(&self) -> bool {
         match self {
-            InterpError::MachineStop(_)
-            | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
+            // Zero-sized boxes to not allocate.
+            InterpError::MachineStop(b) => mem::size_of_val(&**b) > 0,
+            InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs
index dfe5adb1bbf..35876c8e95a 100644
--- a/src/librustc/mir/interpret/mod.rs
+++ b/src/librustc/mir/interpret/mod.rs
@@ -92,8 +92,8 @@ mod value;
 
 pub use self::error::{
     struct_error, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, FrameInfo,
-    InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, ResourceExhaustionInfo,
-    UndefinedBehaviorInfo, UnsupportedOpInfo,
+    InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType,
+    ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
 };
 
 pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUndef};
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 8d7cafc34b3..687bacfdc1b 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -4,7 +4,7 @@
 use std::borrow::Cow;
 use std::cell::Cell;
 
-use rustc::mir::interpret::{InterpResult, Scalar};
+use rustc::mir::interpret::{InterpResult, MachineStopType, Scalar};
 use rustc::mir::visit::{
     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
 };
@@ -192,7 +192,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
         _unwind: Option<BasicBlock>,
     ) -> InterpResult<'tcx> {
-        throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"))
+        #[derive(Debug)]
+        struct ConstPropIntrinsic;
+        impl MachineStopType for ConstPropIntrinsic {}
+        throw_machine_stop!(ConstPropIntrinsic)
     }
 
     fn assert_panic(
@@ -204,7 +207,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
     }
 
     fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> {
-        throw_unsup!(ConstPropUnsupported("ptr-to-int casts aren't supported in ConstProp"))
+        throw_unsup!(ReadPointerAsBytes)
     }
 
     fn binary_ptr_op(
@@ -213,11 +216,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _left: ImmTy<'tcx>,
         _right: ImmTy<'tcx>,
     ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
+        #[derive(Debug)]
+        struct ConstPropPtrOp;
+        impl MachineStopType for ConstPropPtrOp {}
         // We can't do this because aliasing of memory can differ between const eval and llvm
-        throw_unsup!(ConstPropUnsupported(
-            "pointer arithmetic or comparisons aren't supported \
-            in ConstProp"
-        ))
+        throw_machine_stop!(ConstPropPtrOp)
     }
 
     #[inline(always)]
@@ -240,7 +243,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
         _dest: PlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
-        throw_unsup!(ConstPropUnsupported("can't const prop `box` keyword"))
+        #[derive(Debug)]
+        struct ConstPropBox;
+        impl MachineStopType for ConstPropBox {}
+        throw_machine_stop!(ConstPropBox)
     }
 
     fn access_local(
@@ -251,7 +257,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         let l = &frame.locals[local];
 
         if l.value == LocalValue::Uninitialized {
-            throw_unsup!(ConstPropUnsupported("tried to access an uninitialized local"));
+            #[derive(Debug)]
+            struct ConstPropUninitLocal;
+            impl MachineStopType for ConstPropUninitLocal {}
+            throw_machine_stop!(ConstPropUninitLocal)
         }
 
         l.access()
@@ -261,10 +270,13 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         _memory_extra: &(),
         allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
     ) -> InterpResult<'tcx> {
+        #[derive(Debug)]
+        struct ConstPropGlobalMem;
+        impl MachineStopType for ConstPropGlobalMem {}
         // 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::Mut || allocation.relocations().len() > 0 {
-            throw_unsup!(ConstPropUnsupported("can't eval mutable statics in ConstProp"));
+            throw_machine_stop!(ConstPropGlobalMem)
         }
 
         Ok(())