about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-12-02 11:43:50 +0000
committerbors <bors@rust-lang.org>2019-12-02 11:43:50 +0000
commit2da942f32802c8233a09744024dfbc34431adf65 (patch)
treef8dc4421da3bd87eb86b895597c024337e961ada
parent4af3ee8ee2a2bc1286b021db7600ba990359cf3f (diff)
parent5e51a153f97b141dfeef795d22ec9e47967764f2 (diff)
downloadrust-2da942f32802c8233a09744024dfbc34431adf65.tar.gz
rust-2da942f32802c8233a09744024dfbc34431adf65.zip
Auto merge of #66874 - RalfJung:miri-assert-panic, r=oli-obk
Miri engine: proper support for `Assert` MIR terminators

This puts down the basis for https://github.com/rust-lang/miri/issues/1070, and I also did some clean-up. The Miri side of this is at https://github.com/rust-lang/miri/pull/1084.

r? @oli-obk
-rw-r--r--src/librustc/mir/interpret/value.rs7
-rw-r--r--src/librustc_mir/const_eval.rs40
-rw-r--r--src/librustc_mir/interpret/cast.rs5
-rw-r--r--src/librustc_mir/interpret/eval_context.rs42
-rw-r--r--src/librustc_mir/interpret/intrinsics.rs8
-rw-r--r--src/librustc_mir/interpret/intrinsics/caller_location.rs58
-rw-r--r--src/librustc_mir/interpret/machine.rs25
-rw-r--r--src/librustc_mir/interpret/memory.rs21
-rw-r--r--src/librustc_mir/interpret/operand.rs15
-rw-r--r--src/librustc_mir/interpret/place.rs23
-rw-r--r--src/librustc_mir/interpret/terminator.rs54
-rw-r--r--src/librustc_mir/interpret/traits.rs4
-rw-r--r--src/librustc_mir/transform/const_prop.rs11
13 files changed, 195 insertions, 118 deletions
diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs
index f82af62c5f3..a038ca23ae9 100644
--- a/src/librustc/mir/interpret/value.rs
+++ b/src/librustc/mir/interpret/value.rs
@@ -471,6 +471,13 @@ impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
     }
 }
 
+impl<Tag> From<Pointer<Tag>> for ScalarMaybeUndef<Tag> {
+    #[inline(always)]
+    fn from(s: Pointer<Tag>) -> Self {
+        ScalarMaybeUndef::Scalar(s.into())
+    }
+}
+
 impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 0967b257885..ff0cf6f4fdd 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -23,7 +23,7 @@ use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol};
 use crate::interpret::{self,
     PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
     RawConst, ConstValue, Machine,
-    InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
+    InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, AssertMessage,
     Allocation, AllocId, MemoryKind, Memory,
     snapshot, RefTracking, intern_const_alloc_recursive,
 };
@@ -395,6 +395,40 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         )
     }
 
+    fn assert_panic(
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _span: Span,
+        msg: &AssertMessage<'tcx>,
+        _unwind: Option<mir::BasicBlock>,
+    ) -> InterpResult<'tcx> {
+        use rustc::mir::interpret::PanicInfo::*;
+        Err(match msg {
+            BoundsCheck { ref len, ref index } => {
+                let len = ecx
+                    .read_immediate(ecx.eval_operand(len, None)?)
+                    .expect("can't eval len")
+                    .to_scalar()?
+                    .to_machine_usize(&*ecx)?;
+                let index = ecx
+                    .read_immediate(ecx.eval_operand(index, None)?)
+                    .expect("can't eval index")
+                    .to_scalar()?
+                    .to_machine_usize(&*ecx)?;
+                err_panic!(BoundsCheck { len, index })
+            }
+            Overflow(op) => err_panic!(Overflow(*op)),
+            OverflowNeg => err_panic!(OverflowNeg),
+            DivisionByZero => err_panic!(DivisionByZero),
+            RemainderByZero => err_panic!(RemainderByZero),
+            ResumedAfterReturn(generator_kind)
+                => err_panic!(ResumedAfterReturn(*generator_kind)),
+            ResumedAfterPanic(generator_kind)
+                => err_panic!(ResumedAfterPanic(*generator_kind)),
+            Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
+        }
+        .into())
+    }
+
     fn ptr_to_int(
         _mem: &Memory<'mir, 'tcx, Self>,
         _ptr: Pointer,
@@ -423,7 +457,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
     }
 
     #[inline(always)]
-    fn tag_allocation<'b>(
+    fn init_allocation_extra<'b>(
         _memory_extra: &(),
         _id: AllocId,
         alloc: Cow<'b, Allocation>,
@@ -518,7 +552,7 @@ pub fn const_caller_location<'tcx>(
         tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
             .subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
     );
-    let loc_place = ecx.alloc_caller_location(file, line, col).unwrap();
+    let loc_place = ecx.alloc_caller_location(file, line, col);
     intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
     let loc_const = ty::Const {
         ty: loc_ty,
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 1fb8b3ca63f..e9602ecfa4c 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -55,7 +55,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         ).ok_or_else(|| err_inval!(TooGeneric))?;
 
                         let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
-                        self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
+                        self.write_scalar(fn_ptr, dest)?;
                     }
                     _ => bug!("reify fn pointer on {:?}", src.layout.ty),
                 }
@@ -88,8 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             ty::ClosureKind::FnOnce,
                         );
                         let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
-                        let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
-                        self.write_immediate(val, dest)?;
+                        self.write_scalar(fn_ptr, dest)?;
                     }
                     _ => bug!("closure fn pointer on {:?}", src.layout.ty),
                 }
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 6c16c4f2219..8250cadb01d 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -164,6 +164,20 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
     }
 }
 
+impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> {
+    /// Return the `SourceInfo` of the current instruction.
+    pub fn current_source_info(&self) -> Option<mir::SourceInfo> {
+        self.block.map(|block| {
+            let block = &self.body.basic_blocks()[block];
+            if self.stmt < block.statements.len() {
+                block.statements[self.stmt].source_info
+            } else {
+                block.terminator().source_info
+            }
+        })
+    }
+}
+
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
     #[inline]
     fn data_layout(&self) -> &layout::TargetDataLayout {
@@ -236,6 +250,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         self.memory.force_bits(scalar, size)
     }
 
+    /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
+    /// the *canonical* machine pointer to the allocation.  Must never be used
+    /// for any other pointers!
+    ///
+    /// This represents a *direct* access to that memory, as opposed to access
+    /// through a pointer that was created by the program.
     #[inline(always)]
     pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
         self.memory.tag_static_base_pointer(ptr)
@@ -828,34 +848,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> {
         let mut last_span = None;
         let mut frames = Vec::new();
-        for &Frame { instance, span, body, block, stmt, .. } in self.stack().iter().rev() {
+        for frame in self.stack().iter().rev() {
             // make sure we don't emit frames that are duplicates of the previous
-            if explicit_span == Some(span) {
-                last_span = Some(span);
+            if explicit_span == Some(frame.span) {
+                last_span = Some(frame.span);
                 continue;
             }
             if let Some(last) = last_span {
-                if last == span {
+                if last == frame.span {
                     continue;
                 }
             } else {
-                last_span = Some(span);
+                last_span = Some(frame.span);
             }
 
-            let lint_root = block.and_then(|block| {
-                let block = &body.basic_blocks()[block];
-                let source_info = if stmt < block.statements.len() {
-                    block.statements[stmt].source_info
-                } else {
-                    block.terminator().source_info
-                };
-                match &body.source_scopes[source_info.scope].local_data {
+            let lint_root = frame.current_source_info().and_then(|source_info| {
+                match &frame.body.source_scopes[source_info.scope].local_data {
                     mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
                     mir::ClearCrossCrate::Clear => None,
                 }
             });
 
-            frames.push(FrameInfo { call_site: span, instance, lint_root });
+            frames.push(FrameInfo { call_site: frame.span, instance: frame.instance, lint_root });
         }
         trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
         frames
diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs
index 7bcf84a7b2d..118dfcb3d9a 100644
--- a/src/librustc_mir/interpret/intrinsics.rs
+++ b/src/librustc_mir/interpret/intrinsics.rs
@@ -110,13 +110,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         match intrinsic_name {
             "caller_location" => {
-                let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
-                let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
-                let location = self.alloc_caller_location(
-                    Symbol::intern(&caller.file.name.to_string()),
-                    caller.line as u32,
-                    caller.col_display as u32 + 1,
-                )?;
+                let location = self.alloc_caller_location_for_span(span);
                 self.write_scalar(location.ptr, dest)?;
             }
 
diff --git a/src/librustc_mir/interpret/intrinsics/caller_location.rs b/src/librustc_mir/interpret/intrinsics/caller_location.rs
index 9e07a3f1072..ecf4b7a39b7 100644
--- a/src/librustc_mir/interpret/intrinsics/caller_location.rs
+++ b/src/librustc_mir/interpret/intrinsics/caller_location.rs
@@ -1,50 +1,48 @@
 use rustc::middle::lang_items::PanicLocationLangItem;
-use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar};
 use rustc::ty::subst::Subst;
-use rustc_target::abi::{LayoutOf, Size};
-use syntax_pos::Symbol;
+use rustc_target::abi::LayoutOf;
+use syntax_pos::{Symbol, Span};
 
-use crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}};
+use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};
 
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    pub fn alloc_caller_location(
+    crate fn alloc_caller_location(
         &mut self,
         filename: Symbol,
         line: u32,
         col: u32,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+    ) -> MPlaceTy<'tcx, M::PointerTag> {
+        let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation);
         let line = Scalar::from_u32(line);
         let col = Scalar::from_u32(col);
 
-        let ptr_size = self.pointer_size();
-        let u32_size = Size::from_bits(32);
-
+        // Allocate memory for `CallerLocation` struct.
         let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
             .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter()));
-        let loc_layout = self.layout_of(loc_ty)?;
-
-        let file_alloc = self.tcx.allocate_bytes(filename.as_str().as_bytes());
-        let file_ptr = Pointer::new(file_alloc, Size::ZERO);
-        let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr));
-        let file_len = Scalar::from_uint(filename.as_str().len() as u128, ptr_size);
-
+        let loc_layout = self.layout_of(loc_ty).unwrap();
         let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
 
-        let file_out = self.mplace_field(location, 0)?;
-        let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?;
-        let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?;
-        let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?;
-        let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?;
+        // Initialize fields.
+        self.write_immediate(file.to_ref(), self.mplace_field(location, 0).unwrap().into())
+            .expect("writing to memory we just allocated cannot fail");
+        self.write_scalar(line, self.mplace_field(location, 1).unwrap().into())
+            .expect("writing to memory we just allocated cannot fail");
+        self.write_scalar(col, self.mplace_field(location, 2).unwrap().into())
+            .expect("writing to memory we just allocated cannot fail");
 
-        let layout = &self.tcx.data_layout;
-        // We just allocated this, so we can skip the bounds checks.
-        let alloc = self.memory.get_raw_mut(file_ptr_out.alloc_id)?;
-
-        alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?;
-        alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?;
-        alloc.write_scalar(layout, line_out, line.into(), u32_size)?;
-        alloc.write_scalar(layout, col_out, col.into(), u32_size)?;
+        location
+    }
 
-        Ok(location)
+    pub fn alloc_caller_location_for_span(
+        &mut self,
+        span: Span,
+    ) -> MPlaceTy<'tcx, M::PointerTag> {
+        let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+        let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
+        self.alloc_caller_location(
+            Symbol::intern(&caller.file.name.to_string()),
+            caller.line as u32,
+            caller.col_display as u32 + 1,
+        )
     }
 }
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index b7cde626415..2ecc8d88ad3 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -11,7 +11,7 @@ use rustc::ty::{self, Ty, TyCtxt};
 use syntax_pos::Span;
 
 use super::{
-    Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
+    Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
     InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
     Frame, Operand,
 };
@@ -175,6 +175,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
         unwind: Option<mir::BasicBlock>,
     ) -> InterpResult<'tcx>;
 
+    /// Called to evaluate `Assert` MIR terminators that trigger a panic.
+    fn assert_panic(
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        span: Span,
+        msg: &AssertMessage<'tcx>,
+        unwind: Option<mir::BasicBlock>,
+    ) -> InterpResult<'tcx>;
+
     /// Called for read access to a foreign static item.
     ///
     /// This will only be called once per static and machine; the result is cached in
@@ -233,20 +241,19 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
     /// owned allocation to the map even when the map is shared.)
     ///
-    /// For static allocations, the tag returned must be the same as the one returned by
-    /// `tag_static_base_pointer`.
-    fn tag_allocation<'b>(
+    /// Also return the "base" tag to use for this allocation: the one that is used for direct
+    /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
+    /// with `tag_static_base_pointer`.
+    fn init_allocation_extra<'b>(
         memory_extra: &Self::MemoryExtra,
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKinds>>,
     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
 
-    /// Return the "base" tag for the given static allocation: the one that is used for direct
-    /// accesses to this static/const/fn allocation.
-    ///
-    /// Be aware that requesting the `Allocation` for that `id` will lead to cycles
-    /// for cyclic statics!
+    /// Return the "base" tag for the given *static* allocation: the one that is used for direct
+    /// accesses to this static/const/fn allocation. If `id` is not a static allocation,
+    /// this will return an unusable tag (i.e., accesses will be UB)!
     fn tag_static_base_pointer(
         memory_extra: &Self::MemoryExtra,
         id: AllocId,
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index a8011f7abb1..ee7fb18fd05 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -143,6 +143,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         }
     }
 
+    /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
+    /// the *canonical* machine pointer to the allocation.  Must never be used
+    /// for any other pointers!
+    ///
+    /// This represents a *direct* access to that memory, as opposed to access
+    /// through a pointer that was created by the program.
     #[inline]
     pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
         ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id))
@@ -191,7 +197,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         kind: MemoryKind<M::MemoryKinds>,
     ) -> Pointer<M::PointerTag> {
         let id = self.tcx.alloc_map.lock().reserve();
-        let (alloc, tag) = M::tag_allocation(&self.extra, id, Cow::Owned(alloc), Some(kind));
+        debug_assert_ne!(Some(kind), M::STATIC_KIND.map(MemoryKind::Machine),
+            "dynamically allocating static memory");
+        let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
         self.alloc_map.insert(id, (kind, alloc.into_owned()));
         Pointer::from(id).with_tag(tag)
     }
@@ -350,7 +358,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             sptr
         } else {
             // A "real" access, we must get a pointer.
-            Scalar::Ptr(self.force_ptr(sptr)?)
+            Scalar::from(self.force_ptr(sptr)?)
         };
         Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
             Ok(bits) => {
@@ -473,14 +481,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 }
             }
         };
-        // We got tcx memory. Let the machine figure out whether and how to
-        // turn that into memory with the right pointer tag.
-        Ok(M::tag_allocation(
+        // We got tcx memory. Let the machine initialize its "extra" stuff.
+        let (alloc, tag) = M::init_allocation_extra(
             memory_extra,
             id, // always use the ID we got as input, not the "hidden" one.
             alloc,
             M::STATIC_KIND.map(MemoryKind::Machine),
-        ).0)
+        );
+        debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id));
+        Ok(alloc)
     }
 
     /// Gives raw access to the `Allocation`, without bounds or alignment checks.
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 8dd5807f7cf..9e94ae2c160 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -24,7 +24,7 @@ use rustc_macros::HashStable;
 ///
 /// For optimization of a few very common cases, there is also a representation for a pair of
 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
-/// operations and fat pointers. This idea was taken from rustc's codegen.
+/// operations and wide pointers. This idea was taken from rustc's codegen.
 /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
 /// defined on `Immediate`, and do not have to work with a `Place`.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
@@ -47,6 +47,13 @@ impl<Tag> From<Scalar<Tag>> for Immediate<Tag> {
     }
 }
 
+impl<Tag> From<Pointer<Tag>> for Immediate<Tag> {
+    #[inline(always)]
+    fn from(val: Pointer<Tag>) -> Self {
+        Immediate::Scalar(Scalar::from(val).into())
+    }
+}
+
 impl<'tcx, Tag> Immediate<Tag> {
     pub fn new_slice(
         val: Scalar<Tag>,
@@ -60,14 +67,14 @@ impl<'tcx, Tag> Immediate<Tag> {
     }
 
     pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self {
-        Immediate::ScalarPair(val.into(), Scalar::Ptr(vtable).into())
+        Immediate::ScalarPair(val.into(), vtable.into())
     }
 
     #[inline]
     pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> {
         match self {
             Immediate::Scalar(val) => val,
-            Immediate::ScalarPair(..) => bug!("Got a fat pointer where a scalar was expected"),
+            Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
         }
     }
 
@@ -324,7 +331,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(self.read_immediate(op)?.to_scalar_or_undef())
     }
 
-    // Turn the MPlace into a string (must already be dereferenced!)
+    // Turn the wide MPlace into a string (must already be dereferenced!)
     pub fn read_str(
         &self,
         mplace: MPlaceTy<'tcx, M::PointerTag>,
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 70d9836b6ff..da601c3a9f0 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -125,7 +125,7 @@ impl<Tag> MemPlace<Tag> {
         Self::from_scalar_ptr(ptr.into(), align)
     }
 
-    /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
+    /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
     /// This is the inverse of `ref_to_mplace`.
     #[inline(always)]
     pub fn to_ref(self) -> Immediate<Tag> {
@@ -278,7 +278,7 @@ where
     M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
     M::AllocExtra: AllocationExtra<Tag>,
 {
-    /// Take a value, which represents a (thin or fat) reference, and make it a place.
+    /// Take a value, which represents a (thin or wide) reference, and make it a place.
     /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
     ///
     /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
@@ -694,6 +694,7 @@ where
     }
 
     /// Write a scalar to a place
+    #[inline(always)]
     pub fn write_scalar(
         &mut self,
         val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
@@ -1041,6 +1042,24 @@ where
         MPlaceTy::from_aligned_ptr(ptr, layout)
     }
 
+    /// Returns a wide MPlace.
+    pub fn allocate_str(
+        &mut self,
+        str: &str,
+        kind: MemoryKind<M::MemoryKinds>,
+    ) -> MPlaceTy<'tcx, M::PointerTag> {
+        let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind);
+        let meta = Scalar::from_uint(str.len() as u128, self.pointer_size());
+        let mplace = MemPlace {
+            ptr: ptr.into(),
+            align: Align::from_bytes(1).unwrap(),
+            meta: Some(meta),
+        };
+
+        let layout = self.layout_of(self.tcx.mk_static_str()).unwrap();
+        MPlaceTy { mplace, layout }
+    }
+
     pub fn write_discriminant_index(
         &mut self,
         variant_index: VariantIdx,
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index 50cd3188510..06c3969fbc5 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -7,8 +7,8 @@ use syntax::source_map::Span;
 use rustc_target::spec::abi::Abi;
 
 use super::{
-    GlobalId, InterpResult, PointerArithmetic,
-    InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
+    GlobalId, InterpResult, InterpCx, Machine,
+    OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
 };
 
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@@ -115,40 +115,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 expected,
                 ref msg,
                 target,
-                ..
+                cleanup,
             } => {
                 let cond_val = self.read_immediate(self.eval_operand(cond, None)?)?
                     .to_scalar()?.to_bool()?;
                 if expected == cond_val {
                     self.go_to_block(target);
                 } else {
-                    // Compute error message
-                    use rustc::mir::interpret::PanicInfo::*;
-                    return Err(match msg {
-                        BoundsCheck { ref len, ref index } => {
-                            let len = self
-                                .read_immediate(self.eval_operand(len, None)?)
-                                .expect("can't eval len")
-                                .to_scalar()?
-                                .to_bits(self.memory.pointer_size())? as u64;
-                            let index = self
-                                .read_immediate(self.eval_operand(index, None)?)
-                                .expect("can't eval index")
-                                .to_scalar()?
-                                .to_bits(self.memory.pointer_size())? as u64;
-                            err_panic!(BoundsCheck { len, index })
-                        }
-                        Overflow(op) => err_panic!(Overflow(*op)),
-                        OverflowNeg => err_panic!(OverflowNeg),
-                        DivisionByZero => err_panic!(DivisionByZero),
-                        RemainderByZero => err_panic!(RemainderByZero),
-                        ResumedAfterReturn(generator_kind)
-                            => err_panic!(ResumedAfterReturn(*generator_kind)),
-                        ResumedAfterPanic(generator_kind)
-                            => err_panic!(ResumedAfterPanic(*generator_kind)),
-                        Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
-                    }
-                    .into());
+                    M::assert_panic(self, terminator.source_info.span, msg, cleanup)?;
                 }
             }
 
@@ -164,15 +138,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 return Ok(())
             },
 
+            // It is UB to ever encounter this.
+            Unreachable => throw_ub!(Unreachable),
+
+            // These should never occur for MIR we actually run.
+            DropAndReplace { .. } |
+            FalseEdges { .. } |
+            FalseUnwind { .. } =>
+                bug!("{:#?} should have been eliminated by MIR pass", terminator.kind),
+
+            // These are not (yet) supported. It is unclear if they even can occur in
+            // MIR that we actually run.
             Yield { .. } |
             GeneratorDrop |
-            DropAndReplace { .. } |
-            Abort => unimplemented!("{:#?}", terminator.kind),
-            FalseEdges { .. } => bug!("should have been eliminated by\
-                                      `simplify_branches` mir pass"),
-            FalseUnwind { .. } => bug!("should have been eliminated by\
-                                       `simplify_branches` mir pass"),
-            Unreachable => throw_ub!(Unreachable),
+            Abort =>
+                throw_unsup_format!("Unsupported terminator kind: {:#?}", terminator.kind),
         }
 
         Ok(())
diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs
index 3a7f47a2aac..efa0d266cbc 100644
--- a/src/librustc_mir/interpret/traits.rs
+++ b/src/librustc_mir/interpret/traits.rs
@@ -67,7 +67,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // allocation is correctly aligned as we created it above. Also we're only offsetting by
         // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
         let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
-        vtable_alloc.write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?;
+        vtable_alloc.write_ptr_sized(tcx, vtable, drop.into())?;
 
         let size_ptr = vtable.offset(ptr_size, tcx)?;
         vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
@@ -87,7 +87,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
                 let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
                 self.memory.get_raw_mut(vtable.alloc_id)?
-                    .write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?;
+                    .write_ptr_sized(tcx, method_ptr, fn_ptr.into())?;
             }
         }
 
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 67958af3460..bbbaac145f5 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -156,6 +156,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
         throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
     }
 
+    fn assert_panic(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _span: Span,
+        _msg: &rustc::mir::interpret::AssertMessage<'tcx>,
+        _unwind: Option<rustc::mir::BasicBlock>,
+    ) -> InterpResult<'tcx> {
+        bug!("panics terminators are not evaluated in ConstProp");
+    }
+
     fn ptr_to_int(
         _mem: &Memory<'mir, 'tcx, Self>,
         _ptr: Pointer,
@@ -182,7 +191,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
     }
 
     #[inline(always)]
-    fn tag_allocation<'b>(
+    fn init_allocation_extra<'b>(
         _memory_extra: &(),
         _id: AllocId,
         alloc: Cow<'b, Allocation>,