about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/mir/interpret/mod.rs8
-rw-r--r--src/librustc_mir/const_eval.rs52
-rw-r--r--src/librustc_mir/interpret/cast.rs27
-rw-r--r--src/librustc_mir/interpret/eval_context.rs124
-rw-r--r--src/librustc_mir/interpret/machine.rs99
-rw-r--r--src/librustc_mir/interpret/memory.rs67
-rw-r--r--src/librustc_mir/interpret/mod.rs4
-rw-r--r--src/librustc_mir/interpret/place.rs59
-rw-r--r--src/librustc_mir/interpret/snapshot.rs2
-rw-r--r--src/librustc_mir/interpret/step.rs5
-rw-r--r--src/librustc_mir/interpret/terminator.rs5
-rw-r--r--src/librustc_mir/interpret/traits.rs2
-rw-r--r--src/librustc_mir/interpret/validity.rs4
m---------src/tools/miri24
14 files changed, 316 insertions, 166 deletions
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs
index 8c8073080aa..4c2b2b2d41d 100644
--- a/src/librustc/mir/interpret/mod.rs
+++ b/src/librustc/mir/interpret/mod.rs
@@ -524,7 +524,7 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
-pub struct Allocation<Tag=()> {
+pub struct Allocation<Tag=(),Extra=()> {
     /// The actual bytes of the allocation.
     /// Note that the bytes of a pointer represent the offset of the pointer
     pub bytes: Vec<u8>,
@@ -541,9 +541,11 @@ pub struct Allocation<Tag=()> {
     /// Also used by codegen to determine if a static should be put into mutable memory,
     /// which happens for `static mut` and `static` with interior mutability.
     pub mutability: Mutability,
+    /// Extra state for the machine.
+    pub extra: Extra,
 }
 
-impl<Tag> Allocation<Tag> {
+impl<Tag, Extra: Default> Allocation<Tag, Extra> {
     /// Creates a read-only allocation initialized by the given bytes
     pub fn from_bytes(slice: &[u8], align: Align) -> Self {
         let mut undef_mask = UndefMask::new(Size::ZERO);
@@ -554,6 +556,7 @@ impl<Tag> Allocation<Tag> {
             undef_mask,
             align,
             mutability: Mutability::Immutable,
+            extra: Extra::default(),
         }
     }
 
@@ -569,6 +572,7 @@ impl<Tag> Allocation<Tag> {
             undef_mask: UndefMask::new(size),
             align,
             mutability: Mutability::Mutable,
+            extra: Extra::default(),
         }
     }
 }
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 2cfd058831f..bc917140bbd 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -19,8 +19,8 @@ use std::collections::hash_map::Entry;
 use rustc::hir::{self, def_id::DefId};
 use rustc::mir::interpret::ConstEvalErr;
 use rustc::mir;
-use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt};
-use rustc::ty::layout::{self, LayoutOf, TyLayout};
+use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
+use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
 use rustc::ty::subst::Subst;
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashMap;
@@ -28,13 +28,10 @@ use rustc_data_structures::fx::FxHashMap;
 use syntax::ast::Mutability;
 use syntax::source_map::{Span, DUMMY_SP};
 
-use rustc::mir::interpret::{
-    EvalResult, EvalError, EvalErrorKind, GlobalId,
-    Scalar, Allocation, AllocId, ConstValue,
-};
 use interpret::{self,
-    PlaceTy, MemPlace, OpTy, Operand, Value,
-    EvalContext, StackPopCleanup, MemoryKind,
+    PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
+    EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
+    Allocation, AllocId, MemoryKind,
     snapshot,
 };
 
@@ -53,7 +50,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
 ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
     debug!("mk_borrowck_eval_cx: {:?}", instance);
     let param_env = tcx.param_env(instance.def_id());
-    let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
+    let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
     // insert a stack frame so any queries have the correct substs
     // cannot use `push_stack_frame`; if we do `const_prop` explodes
     ecx.stack.push(interpret::Frame {
@@ -76,7 +73,7 @@ pub fn mk_eval_cx<'a, 'tcx>(
 ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
     debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
     let span = tcx.def_span(instance.def_id());
-    let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
+    let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
     let mir = ecx.load_mir(instance.def)?;
     // insert a stack frame so any queries have the correct substs
     ecx.push_stack_frame(
@@ -155,7 +152,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
     // and try improving it down the road when more information is available
     let span = tcx.def_span(cid.instance.def_id());
     let span = mir.map(|mir| mir.span).unwrap_or(span);
-    let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
+    let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
     let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
     (r, ecx)
 }
@@ -333,16 +330,25 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
 type CompileTimeEvalContext<'a, 'mir, 'tcx> =
     EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>;
 
+impl interpret::MayLeak for ! {
+    #[inline(always)]
+    fn may_leak(self) -> bool {
+        // `self` is uninhabited
+        self
+    }
+}
+
 impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
     for CompileTimeInterpreter<'a, 'mir, 'tcx>
 {
-    type MemoryData = ();
     type MemoryKinds = !;
+    type AllocExtra = ();
     type PointerTag = ();
 
-    type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
+    type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
 
     const STATIC_KIND: Option<!> = None; // no copying of statics allowed
+    const ENABLE_PTR_TRACKING_HOOKS: bool = false; // we don't have no provenance
 
     #[inline(always)]
     fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
@@ -456,6 +462,26 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
             &ecx.stack[..],
         )
     }
+
+    #[inline(always)]
+    fn tag_reference(
+        _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
+        _ptr: Pointer<Self::PointerTag>,
+        _pointee_ty: Ty<'tcx>,
+        _pointee_size: Size,
+        _borrow_kind: Option<mir::BorrowKind>,
+    ) -> EvalResult<'tcx, Self::PointerTag> {
+        Ok(())
+    }
+
+    #[inline(always)]
+    fn tag_dereference(
+        _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
+        _ptr: Pointer<Self::PointerTag>,
+        _ptr_ty: Ty<'tcx>,
+    ) -> EvalResult<'tcx, Self::PointerTag> {
+        Ok(())
+    }
 }
 
 /// Project to a field of a (variant of a) const
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 1504b694be1..81e7a6e4373 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -37,8 +37,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         kind: CastKind,
         dest: PlaceTy<'tcx, M::PointerTag>,
     ) -> EvalResult<'tcx> {
-        let src_layout = src.layout;
-        let dst_layout = dest.layout;
         use rustc::mir::CastKind::*;
         match kind {
             Unsize => {
@@ -46,15 +44,28 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             }
 
             Misc => {
+                let src_layout = src.layout;
                 let src = self.read_value(src)?;
+
+                let src = if M::ENABLE_PTR_TRACKING_HOOKS && src_layout.ty.is_region_ptr() {
+                    // The only `Misc` casts on references are those creating raw pointers.
+                    assert!(dest.layout.ty.is_unsafe_ptr());
+                    // For the purpose of the "ptr tag hooks", treat this as creating
+                    // a new, raw reference.
+                    let place = self.ref_to_mplace(src)?;
+                    self.create_ref(place, None)?
+                } else {
+                    *src
+                };
+
                 if self.type_is_fat_ptr(src_layout.ty) {
-                    match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
+                    match (src, self.type_is_fat_ptr(dest.layout.ty)) {
                         // pointers to extern types
                         (Value::Scalar(_),_) |
                         // slices and trait objects to other slices/trait objects
                         (Value::ScalarPair(..), true) => {
                             // No change to value
-                            self.write_value(*src, dest)?;
+                            self.write_value(src, dest)?;
                         }
                         // slices and trait objects to thin pointers (dropping the metadata)
                         (Value::ScalarPair(data, _), false) => {
@@ -65,11 +76,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                     match src_layout.variants {
                         layout::Variants::Single { index } => {
                             if let Some(def) = src_layout.ty.ty_adt_def() {
+                                // Cast from a univariant enum
+                                assert!(src_layout.is_zst());
                                 let discr_val = def
                                     .discriminant_for_variant(*self.tcx, index)
                                     .val;
                                 return self.write_scalar(
-                                    Scalar::from_uint(discr_val, dst_layout.size),
+                                    Scalar::from_uint(discr_val, dest.layout.size),
                                     dest);
                             }
                         }
@@ -85,7 +98,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
 
             ReifyFnPointer => {
                 // The src operand does not matter, just its type
-                match src_layout.ty.sty {
+                match src.layout.ty.sty {
                     ty::FnDef(def_id, substs) => {
                         if self.tcx.has_attr(def_id, "rustc_args_required_const") {
                             bug!("reifying a fn ptr that requires \
@@ -117,7 +130,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
 
             ClosureFnPointer => {
                 // The src operand does not matter, just its type
-                match src_layout.ty.sty {
+                match src.layout.ty.sty {
                     ty::Closure(def_id, substs) => {
                         let substs = self.tcx.subst_and_normalize_erasing_regions(
                             self.substs(),
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index cf5358a9896..92cc09f4867 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -11,6 +11,7 @@
 use std::fmt::Write;
 use std::mem;
 
+use syntax::source_map::{self, Span, DUMMY_SP};
 use rustc::hir::def_id::DefId;
 use rustc::hir::def::Def;
 use rustc::hir::map::definitions::DefPathData;
@@ -29,8 +30,6 @@ use rustc::mir::interpret::{
 };
 use rustc_data_structures::fx::FxHashMap;
 
-use syntax::source_map::{self, Span};
-
 use super::{
     Value, Operand, MemPlace, MPlaceTy, Place, PlaceTy, ScalarMaybeUndef,
     Memory, Machine
@@ -205,63 +204,59 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         machine: M,
-        memory_data: M::MemoryData,
     ) -> Self {
         EvalContext {
             machine,
             tcx,
             param_env,
-            memory: Memory::new(tcx, memory_data),
+            memory: Memory::new(tcx),
             stack: Vec::new(),
             vtables: FxHashMap::default(),
         }
     }
 
+    #[inline(always)]
     pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
         &self.memory
     }
 
+    #[inline(always)]
     pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> {
         &mut self.memory
     }
 
+    #[inline(always)]
     pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag>] {
         &self.stack
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn cur_frame(&self) -> usize {
         assert!(self.stack.len() > 0);
         self.stack.len() - 1
     }
 
-    /// Mark a storage as live, killing the previous content and returning it.
-    /// Remember to deallocate that!
-    pub fn storage_live(
-        &mut self,
-        local: mir::Local
-    ) -> EvalResult<'tcx, LocalValue<M::PointerTag>> {
-        assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
-        trace!("{:?} is now live", local);
-
-        let layout = self.layout_of_local(self.cur_frame(), local)?;
-        let init = LocalValue::Live(self.uninit_operand(layout)?);
-        // StorageLive *always* kills the value that's currently stored
-        Ok(mem::replace(&mut self.frame_mut().locals[local], init))
+    #[inline(always)]
+    pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag> {
+        self.stack.last().expect("no call frames exist")
     }
 
-    /// Returns the old value of the local.
-    /// Remember to deallocate that!
-    pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue<M::PointerTag> {
-        assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
-        trace!("{:?} is now dead", local);
+    #[inline(always)]
+    pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag> {
+        self.stack.last_mut().expect("no call frames exist")
+    }
 
-        mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
+    #[inline(always)]
+    pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> {
+        self.frame().mir
     }
 
-    pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value<M::PointerTag>> {
-        let ptr = self.memory.allocate_static_bytes(s.as_bytes());
-        Ok(Value::new_slice(Scalar::Ptr(ptr), s.len() as u64, self.tcx.tcx))
+    pub fn substs(&self) -> &'tcx Substs<'tcx> {
+        if let Some(frame) = self.stack.last() {
+            frame.instance.substs
+        } else {
+            Substs::empty()
+        }
     }
 
     pub(super) fn resolve(
@@ -285,10 +280,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         ).ok_or_else(|| EvalErrorKind::TooGeneric.into())
     }
 
-    pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
+    pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
         ty.is_sized(self.tcx, self.param_env)
     }
 
+    pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
+        ty.is_freeze(*self.tcx, self.param_env, DUMMY_SP)
+    }
+
     pub fn load_mir(
         &self,
         instance: ty::InstanceDef<'tcx>,
@@ -336,6 +335,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         self.layout_of(local_ty)
     }
 
+    pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value<M::PointerTag>> {
+        let ptr = self.memory.allocate_static_bytes(s.as_bytes());
+        Ok(Value::new_slice(Scalar::Ptr(ptr), s.len() as u64, self.tcx.tcx))
+    }
+
     /// Return the actual dynamic size and alignment of the place at the given type.
     /// Only the "meta" (metadata) part of the place matters.
     /// This can fail to provide an answer for extern types.
@@ -354,11 +358,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
                 // and it also rounds up to alignment, which we want to avoid,
                 // as the unsized field's alignment could be smaller.
                 assert!(!layout.ty.is_simd());
-                debug!("DST layout: {:?}", layout);
+                trace!("DST layout: {:?}", layout);
 
                 let sized_size = layout.fields.offset(layout.fields.count() - 1);
                 let sized_align = layout.align;
-                debug!(
+                trace!(
                     "DST {} statically sized prefix size: {:?} align: {:?}",
                     layout.ty,
                     sized_size,
@@ -434,6 +438,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         return_place: Option<PlaceTy<'tcx, M::PointerTag>>,
         return_to_block: StackPopCleanup,
     ) -> EvalResult<'tcx> {
+        if self.stack.len() > 1 { // FIXME should be "> 0", printing topmost frame crashes rustc...
+            debug!("PAUSING({}) {}", self.cur_frame(), self.frame().instance);
+        }
         ::log_settings::settings().indentation += 1;
 
         // first push a stack frame so we have access to the local substs
@@ -498,6 +505,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
             self.frame_mut().locals = locals;
         }
 
+        if self.stack.len() > 1 { // FIXME no check should be needed, but some instances ICE
+            debug!("ENTERING({}) {}", self.cur_frame(), self.frame().instance);
+        }
+
         if self.stack.len() > self.tcx.sess.const_eval_stack_frame_limit {
             err!(StackFrameLimitReached)
         } else {
@@ -506,6 +517,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
     }
 
     pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> {
+        if self.stack.len() > 1 { // FIXME no check should be needed, but some instances ICE
+            debug!("LEAVING({}) {}", self.cur_frame(), self.frame().instance);
+        }
         ::log_settings::settings().indentation -= 1;
         let frame = self.stack.pop().expect(
             "tried to pop a stack frame, but there were none",
@@ -549,9 +563,37 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
             return err!(Unreachable);
         }
 
+        if self.stack.len() > 1 { // FIXME should be "> 0", printing topmost frame crashes rustc...
+            debug!("CONTINUING({}) {}", self.cur_frame(), self.frame().instance);
+        }
+
         Ok(())
     }
 
+    /// Mark a storage as live, killing the previous content and returning it.
+    /// Remember to deallocate that!
+    pub fn storage_live(
+        &mut self,
+        local: mir::Local
+    ) -> EvalResult<'tcx, LocalValue<M::PointerTag>> {
+        assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
+        trace!("{:?} is now live", local);
+
+        let layout = self.layout_of_local(self.cur_frame(), local)?;
+        let init = LocalValue::Live(self.uninit_operand(layout)?);
+        // StorageLive *always* kills the value that's currently stored
+        Ok(mem::replace(&mut self.frame_mut().locals[local], init))
+    }
+
+    /// Returns the old value of the local.
+    /// Remember to deallocate that!
+    pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue<M::PointerTag> {
+        assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
+        trace!("{:?} is now dead", local);
+
+        mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
+    }
+
     pub(super) fn deallocate_local(
         &mut self,
         local: LocalValue<M::PointerTag>,
@@ -576,28 +618,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
             .map_err(|err| EvalErrorKind::ReferencedConstant(err).into())
     }
 
-    #[inline(always)]
-    pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag> {
-        self.stack.last().expect("no call frames exist")
-    }
-
-    #[inline(always)]
-    pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag> {
-        self.stack.last_mut().expect("no call frames exist")
-    }
-
-    pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> {
-        self.frame().mir
-    }
-
-    pub fn substs(&self) -> &'tcx Substs<'tcx> {
-        if let Some(frame) = self.stack.last() {
-            frame.instance.substs
-        } else {
-            Substs::empty()
-        }
-    }
-
     pub fn dump_place(&self, place: Place<M::PointerTag>) {
         // Debug output
         if !log_enabled!(::log::Level::Trace) {
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 560698f3f57..1318bbe1c2b 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -16,11 +16,25 @@ use std::borrow::{Borrow, Cow};
 use std::hash::Hash;
 
 use rustc::hir::def_id::DefId;
-use rustc::mir::interpret::{Allocation, AllocId, EvalResult, Scalar};
 use rustc::mir;
-use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
+use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt};
+
+use super::{
+    Allocation, AllocId, EvalResult, Scalar,
+    EvalContext, PlaceTy, OpTy, Pointer, MemoryKind,
+};
+
+/// Classifying memory accesses
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum MemoryAccess {
+    Read,
+    Write,
+}
 
-use super::{EvalContext, PlaceTy, OpTy, MemoryKind};
+/// Whether this kind of memory is allowed to leak
+pub trait MayLeak: Copy {
+    fn may_leak(self) -> bool;
+}
 
 /// The functionality needed by memory to manage its allocations
 pub trait AllocMap<K: Hash + Eq, V> {
@@ -62,29 +76,36 @@ pub trait AllocMap<K: Hash + Eq, V> {
 /// Methods of this trait signifies a point where CTFE evaluation would fail
 /// and some use case dependent behaviour can instead be applied.
 pub trait Machine<'a, 'mir, 'tcx>: Sized {
-    /// Additional data that can be accessed via the Memory
-    type MemoryData;
-
     /// Additional memory kinds a machine wishes to distinguish from the builtin ones
-    type MemoryKinds: ::std::fmt::Debug + Copy + Eq;
+    type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
+
+    /// Tag tracked alongside every pointer.  This is used to implement "Stacked Borrows"
+    /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
+    type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static;
+
+    /// Extra data stored in every allocation.
+    type AllocExtra: ::std::fmt::Debug + Default + Clone;
 
     /// Memory's allocation map
     type MemoryMap:
-        AllocMap<AllocId, (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag>)> +
+        AllocMap<
+            AllocId,
+            (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
+        > +
         Default +
         Clone;
 
-    /// Tag tracked alongside every pointer.  This is inert for now, in preparation for
-    /// a future implementation of "Stacked Borrows"
-    /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
-    type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static;
-
     /// The memory kind to use for copied statics -- or None if those are not supported.
     /// Statics are copied under two circumstances: When they are mutated, and when
     /// `static_with_default_tag` or `find_foreign_static` (see below) returns an owned allocation
     /// that is added to the memory so that the work is not done twice.
     const STATIC_KIND: Option<Self::MemoryKinds>;
 
+    /// As an optimization, you can prevent the pointer tracking hooks from ever being
+    /// called.  You should only do this if you do not care about provenance tracking.
+    /// This controls the `tag_reference` and `tag_dereference` hooks.
+    const ENABLE_PTR_TRACKING_HOOKS: bool;
+
     /// Whether to enforce the validity invariant
     fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
 
@@ -127,7 +148,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
     fn find_foreign_static(
         tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
         def_id: DefId,
-    ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>>;
+    ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
 
     /// Called to turn an allocation obtained from the `tcx` into one that has
     /// the appropriate tags on each pointer.
@@ -138,7 +159,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
     /// owned allocation to the map even when the map is shared.)
     fn static_with_default_tag(
         alloc: &'_ Allocation
-    ) -> Cow<'_, Allocation<Self::PointerTag>>;
+    ) -> Cow<'_, Allocation<Self::PointerTag, Self::AllocExtra>>;
 
     /// Called for all binary operations on integer(-like) types when one operand is a pointer
     /// value, and for the `Offset` operation that is inherently about pointers.
@@ -153,15 +174,57 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
         right_layout: TyLayout<'tcx>,
     ) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
 
-    /// Heap allocations via the `box` keyword
-    ///
-    /// Returns a pointer to the allocated memory
+    /// Heap allocations via the `box` keyword.
     fn box_alloc(
         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
         dest: PlaceTy<'tcx, Self::PointerTag>,
     ) -> EvalResult<'tcx>;
 
+    /// Hook for performing extra checks on a memory access.
+    ///
+    /// Takes read-only access to the allocation so we can keep all the memory read
+    /// operations take `&self`.  Use a `RefCell` in `AllocExtra` if you
+    /// need to mutate.
+    #[inline]
+    fn memory_accessed(
+        _alloc: &Allocation<Self::PointerTag, Self::AllocExtra>,
+        _ptr: Pointer<Self::PointerTag>,
+        _size: Size,
+        _access: MemoryAccess,
+    ) -> EvalResult<'tcx> {
+        Ok(())
+    }
+
+    /// Hook for performing extra checks when memory gets deallocated.
+    #[inline]
+    fn memory_deallocated(
+        _alloc: &mut Allocation<Self::PointerTag, Self::AllocExtra>,
+        _ptr: Pointer<Self::PointerTag>,
+    ) -> EvalResult<'tcx> {
+        Ok(())
+    }
+
+    /// Executed when evaluating the `&` operator: Creating a new reference.
+    /// This has the chance to adjust the tag.
+    /// `borrow_kind` can be `None` in case a raw ptr is being created.
+    fn tag_reference(
+        ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
+        ptr: Pointer<Self::PointerTag>,
+        pointee_ty: Ty<'tcx>,
+        pointee_size: Size,
+        borrow_kind: Option<mir::BorrowKind>,
+    ) -> EvalResult<'tcx, Self::PointerTag>;
+
+    /// Executed when evaluating the `*` operator: Following a reference.
+    /// This has the change to adjust the tag.
+    fn tag_dereference(
+        ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
+        ptr: Pointer<Self::PointerTag>,
+        ptr_ty: Ty<'tcx>,
+    ) -> EvalResult<'tcx, Self::PointerTag>;
+
     /// Execute a validation operation
+    #[inline]
     fn validation_op(
         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
         _op: ::rustc::mir::ValidationOp,
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index 4b0c0c3ee61..9febcceae06 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -22,17 +22,16 @@ use std::borrow::Cow;
 
 use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt};
 use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout};
-use rustc::mir::interpret::{
-    Pointer, AllocId, Allocation, ConstValue, GlobalId,
-    EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
-    truncate
-};
-pub use rustc::mir::interpret::{write_target_uint, read_target_uint};
+pub use rustc::mir::interpret::{truncate, write_target_uint, read_target_uint};
 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
 
 use syntax::ast::Mutability;
 
-use super::{Machine, AllocMap, ScalarMaybeUndef};
+use super::{
+    Pointer, AllocId, Allocation, ConstValue, GlobalId,
+    EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
+    Machine, MemoryAccess, AllocMap, MayLeak, ScalarMaybeUndef,
+};
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
 pub enum MemoryKind<T> {
@@ -44,12 +43,20 @@ pub enum MemoryKind<T> {
     Machine(T),
 }
 
+impl<T: MayLeak> MayLeak for MemoryKind<T> {
+    #[inline]
+    fn may_leak(self) -> bool {
+        match self {
+            MemoryKind::Stack => false,
+            MemoryKind::Vtable => true,
+            MemoryKind::Machine(k) => k.may_leak()
+        }
+    }
+}
+
 // `Memory` has to depend on the `Machine` because some of its operations
 // (e.g. `get`) call a `Machine` hook.
 pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
-    /// Additional data required by the Machine
-    pub data: M::MemoryData,
-
     /// Allocations local to this instance of the miri engine.  The kind
     /// helps ensure that the same mechanism is used for allocation and
     /// deallocation.  When an allocation is not found here, it is a
@@ -91,11 +98,9 @@ impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
 // carefully copy only the reachable parts.
 impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>>
     Clone for Memory<'a, 'mir, 'tcx, M>
-    where M::MemoryData: Clone
 {
     fn clone(&self) -> Self {
         Memory {
-            data: self.data.clone(),
             alloc_map: self.alloc_map.clone(),
             dead_alloc_map: self.dead_alloc_map.clone(),
             tcx: self.tcx,
@@ -104,9 +109,8 @@ impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>>
 }
 
 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
-    pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
+    pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>) -> Self {
         Memory {
-            data,
             alloc_map: Default::default(),
             dead_alloc_map: FxHashMap::default(),
             tcx,
@@ -123,7 +127,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
 
     pub fn allocate_with(
         &mut self,
-        alloc: Allocation<M::PointerTag>,
+        alloc: Allocation<M::PointerTag, M::AllocExtra>,
         kind: MemoryKind<M::MemoryKinds>,
     ) -> EvalResult<'tcx, AllocId> {
         let id = self.tcx.alloc_map.lock().reserve();
@@ -186,13 +190,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
         size_and_align: Option<(Size, Align)>,
         kind: MemoryKind<M::MemoryKinds>,
     ) -> EvalResult<'tcx> {
-        debug!("deallocating: {}", ptr.alloc_id);
+        trace!("deallocating: {}", ptr.alloc_id);
 
         if ptr.offset.bytes() != 0 {
             return err!(DeallocateNonBasePtr);
         }
 
-        let (alloc_kind, alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
+        let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
             Some(alloc) => alloc,
             None => {
                 // Deallocating static memory -- always an error
@@ -227,6 +231,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
             }
         }
 
+        // Let the machine take some extra action
+        M::memory_deallocated(&mut alloc, ptr)?;
+
         // Don't forget to remember size and align of this now-dead allocation
         let old = self.dead_alloc_map.insert(
             ptr.alloc_id,
@@ -334,7 +341,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
     fn get_static_alloc(
         tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
         id: AllocId,
-    ) -> EvalResult<'tcx, Cow<'tcx, Allocation<M::PointerTag>>> {
+    ) -> EvalResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
         let alloc = tcx.alloc_map.lock().get(id);
         let def_id = match alloc {
             Some(AllocType::Memory(mem)) => {
@@ -376,7 +383,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
         })
     }
 
-    pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::PointerTag>> {
+    pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
         // The error type of the inner closure here is somewhat funny.  We have two
         // ways of "erroring": An actual error, or because we got a reference from
         // `get_static_alloc` that we can actually use directly without inserting anything anywhere.
@@ -409,7 +416,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
     pub fn get_mut(
         &mut self,
         id: AllocId,
-    ) -> EvalResult<'tcx, &mut Allocation<M::PointerTag>> {
+    ) -> EvalResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
         let tcx = self.tcx;
         let a = self.alloc_map.get_mut_or(id, || {
             // Need to make a copy, even if `get_static_alloc` is able
@@ -482,12 +489,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
         self.dump_allocs(vec![id]);
     }
 
-    fn dump_alloc_helper<Tag>(
+    fn dump_alloc_helper<Tag, Extra>(
         &self,
         allocs_seen: &mut FxHashSet<AllocId>,
         allocs_to_print: &mut VecDeque<AllocId>,
         mut msg: String,
-        alloc: &Allocation<Tag>,
+        alloc: &Allocation<Tag, Extra>,
         extra: String,
     ) {
         use std::fmt::Write;
@@ -590,13 +597,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
     pub fn leak_report(&self) -> usize {
         trace!("### LEAK REPORT ###");
         let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| {
-            // exclude statics and vtables
-            let exclude = match kind {
-                MemoryKind::Stack => false,
-                MemoryKind::Vtable => true,
-                MemoryKind::Machine(k) => Some(k) == M::STATIC_KIND,
-            };
-            if exclude { None } else { Some(id) }
+            if kind.may_leak() { None } else { Some(id) }
         });
         let n = leaks.len();
         self.dump_allocs(leaks);
@@ -633,6 +634,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
         }
 
         let alloc = self.get(ptr.alloc_id)?;
+        M::memory_accessed(alloc, ptr, size, MemoryAccess::Read)?;
+
         assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
         assert_eq!(size.bytes() as usize as u64, size.bytes());
         let offset = ptr.offset.bytes() as usize;
@@ -677,6 +680,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
         self.clear_relocations(ptr, size)?;
 
         let alloc = self.get_mut(ptr.alloc_id)?;
+        M::memory_accessed(alloc, ptr, size, MemoryAccess::Write)?;
+
         assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
         assert_eq!(size.bytes() as usize as u64, size.bytes());
         let offset = ptr.offset.bytes() as usize;
@@ -687,8 +692,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
 /// Interning (for CTFE)
 impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M>
 where
-    M: Machine<'a, 'mir, 'tcx, PointerTag=()>,
-    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<()>)>,
+    M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=()>,
+    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
 {
     /// mark an allocation as static and initialized, either mutable or not
     pub fn intern_static(
diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs
index 39628598ef3..55037a99e01 100644
--- a/src/librustc_mir/interpret/mod.rs
+++ b/src/librustc_mir/interpret/mod.rs
@@ -24,6 +24,8 @@ mod traits;
 mod validity;
 mod intrinsics;
 
+pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here
+
 pub use self::eval_context::{
     EvalContext, Frame, StackPopCleanup, LocalValue,
 };
@@ -32,7 +34,7 @@ pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
 
 pub use self::memory::{Memory, MemoryKind};
 
-pub use self::machine::{Machine, AllocMap};
+pub use self::machine::{Machine, AllocMap, MemoryAccess, MayLeak};
 
 pub use self::operand::{ScalarMaybeUndef, Value, ValTy, Operand, OpTy};
 
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index e4055947b64..af3d6948628 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -144,17 +144,6 @@ impl<Tag> MemPlace<Tag> {
         // it now must be aligned.
         self.to_scalar_ptr_align().0.to_ptr()
     }
-
-    /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
-    /// This is the inverse of `ref_to_mplace`.
-    pub fn to_ref(self) -> Value<Tag> {
-        // We ignore the alignment of the place here -- special handling for packed structs ends
-        // at the `&` operator.
-        match self.meta {
-            None => Value::Scalar(self.ptr.into()),
-            Some(meta) => Value::ScalarPair(self.ptr.into(), meta.into()),
-        }
-    }
 }
 
 impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
@@ -267,25 +256,59 @@ impl<'a, 'mir, 'tcx, Tag, M> EvalContext<'a, 'mir, 'tcx, M>
 where
     Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static,
     M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>,
-    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag>)>,
+    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
 {
     /// Take a value, which represents a (thin or fat) reference, and make it a place.
-    /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref`.
+    /// Alignment is just based on the type.  This is the inverse of `create_ref`.
     pub fn ref_to_mplace(
-        &self, val: ValTy<'tcx, M::PointerTag>
+        &self,
+        val: ValTy<'tcx, M::PointerTag>,
     ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        let ptr = match val.to_scalar_ptr()? {
+            Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
+                // Machine might want to track the `*` operator
+                let tag = M::tag_dereference(self, ptr, val.layout.ty)?;
+                Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
+            }
+            other => other,
+        };
+
         let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
         let layout = self.layout_of(pointee_type)?;
         let align = layout.align;
+
         let mplace = match *val {
-            Value::Scalar(ptr) =>
-                MemPlace { ptr: ptr.not_undef()?, align, meta: None },
-            Value::ScalarPair(ptr, meta) =>
-                MemPlace { ptr: ptr.not_undef()?, align, meta: Some(meta.not_undef()?) },
+            Value::Scalar(_) =>
+                MemPlace { ptr, align, meta: None },
+            Value::ScalarPair(_, meta) =>
+                MemPlace { ptr, align, meta: Some(meta.not_undef()?) },
         };
         Ok(MPlaceTy { mplace, layout })
     }
 
+    /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
+    /// This is the inverse of `ref_to_mplace`.
+    pub fn create_ref(
+        &mut self,
+        place: MPlaceTy<'tcx, M::PointerTag>,
+        borrow_kind: Option<mir::BorrowKind>,
+    ) -> EvalResult<'tcx, Value<M::PointerTag>> {
+        let ptr = match place.ptr {
+            Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
+                // Machine might want to track the `&` operator
+                let (size, _) = self.size_and_align_of_mplace(place)?
+                    .expect("create_ref cannot determine size");
+                let tag = M::tag_reference(self, ptr, place.layout.ty, size, borrow_kind)?;
+                Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
+            },
+            other => other,
+        };
+        Ok(match place.meta {
+            None => Value::Scalar(ptr.into()),
+            Some(meta) => Value::ScalarPair(ptr.into(), meta.into()),
+        })
+    }
+
     /// Offset a pointer to project to a field. Unlike place_field, this is always
     /// possible without allocating, so it can take &self. Also return the field's layout.
     /// This supports both struct and array fields.
diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs
index 11d5785bc56..047a0125f78 100644
--- a/src/librustc_mir/interpret/snapshot.rs
+++ b/src/librustc_mir/interpret/snapshot.rs
@@ -305,7 +305,7 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
     type Item = AllocationSnapshot<'a>;
 
     fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
-        let Allocation { bytes, relocations, undef_mask, align, mutability } = self;
+        let Allocation { bytes, relocations, undef_mask, align, mutability, extra: () } = self;
 
         AllocationSnapshot {
             bytes,
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index d15867eacdd..1bab536e3e0 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -248,9 +248,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                 )?;
             }
 
-            Ref(_, _, ref place) => {
+            Ref(_, borrow_kind, ref place) => {
                 let src = self.eval_place(place)?;
-                let val = self.force_allocation(src)?.to_ref();
+                let val = self.force_allocation(src)?;
+                let val = self.create_ref(val, Some(borrow_kind))?;
                 self.write_value(val, dest)?;
             }
 
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index a339fa34ae1..c759727f546 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -446,7 +446,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         };
 
         let arg = OpTy {
-            op: Operand::Immediate(place.to_ref()),
+            op: Operand::Immediate(self.create_ref(
+                place,
+                None // this is a "raw reference"
+            )?),
             layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
         };
 
diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs
index 2b0febc1ce7..a2d4eee2842 100644
--- a/src/librustc_mir/interpret/traits.rs
+++ b/src/librustc_mir/interpret/traits.rs
@@ -26,7 +26,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         ty: Ty<'tcx>,
         poly_trait_ref: ty::PolyExistentialTraitRef<'tcx>,
     ) -> EvalResult<'tcx, Pointer<M::PointerTag>> {
-        debug!("get_vtable(trait_ref={:?})", poly_trait_ref);
+        trace!("get_vtable(trait_ref={:?})", poly_trait_ref);
 
         let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref));
 
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index d7b2f67a581..38cf79d8fa0 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -163,6 +163,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                     scalar_format(value), path, "a valid unicode codepoint");
             },
             ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
+                // NOTE: Keep this in sync with the array optimization for int/float
+                // types below!
                 let size = value.layout.size;
                 let value = value.to_scalar_or_undef();
                 if const_mode {
@@ -511,6 +513,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                         // This is the size in bytes of the whole array.
                         let size = Size::from_bytes(ty_size * len);
 
+                        // NOTE: Keep this in sync with the handling of integer and float
+                        // types above, in `validate_primitive_type`.
                         // In run-time mode, we accept pointers in here.  This is actually more
                         // permissive than a per-element check would be, e.g. we accept
                         // an &[u8] that contains a pointer even though bytewise checking would
diff --git a/src/tools/miri b/src/tools/miri
-Subproject 8b14b03368429e6ee2a8ac0e0c876505606ab1f
+Subproject bbb1d80703f272a5592ceeb3832a48977651225