about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-11-06 11:04:10 +0100
committerRalf Jung <post@ralfj.de>2018-11-07 16:54:31 +0100
commitf27cd60ae1819b3b23fc8c3cb21d5f7d457014a1 (patch)
treef548de29565ba1739536057f2e2f362e79f71eef
parent8315b11b6352cbd91ee096571c31ae7d3ac9613d (diff)
downloadrust-f27cd60ae1819b3b23fc8c3cb21d5f7d457014a1.tar.gz
rust-f27cd60ae1819b3b23fc8c3cb21d5f7d457014a1.zip
no more action on ref or cast, but add new MIR statement for escaping a ptr to raw
-rw-r--r--src/librustc/ich/impls_mir.rs3
-rw-r--r--src/librustc/mir/mod.rs9
-rw-r--r--src/librustc/mir/visit.rs5
-rw-r--r--src/librustc_codegen_llvm/mir/statement.rs3
-rw-r--r--src/librustc_mir/borrow_check/mod.rs1
-rw-r--r--src/librustc_mir/borrow_check/nll/invalidation.rs1
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs5
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs1
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs3
-rw-r--r--src/librustc_mir/interpret/cast.rs27
-rw-r--r--src/librustc_mir/interpret/machine.rs23
-rw-r--r--src/librustc_mir/interpret/place.rs30
-rw-r--r--src/librustc_mir/interpret/step.rs22
-rw-r--r--src/librustc_mir/interpret/terminator.rs5
-rw-r--r--src/librustc_mir/transform/add_retag.rs92
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs1
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs1
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs1
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs5
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs1
-rw-r--r--src/librustc_passes/mir_stats.rs1
-rw-r--r--src/test/mir-opt/inline-retag.rs2
-rw-r--r--src/test/mir-opt/retag.rs27
23 files changed, 159 insertions, 110 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index a73fe2b8a1a..1bd02dfeaca 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -257,6 +257,9 @@ for mir::StatementKind<'gcx> {
             mir::StatementKind::EndRegion(ref region_scope) => {
                 region_scope.hash_stable(hcx, hasher);
             }
+            mir::StatementKind::EscapeToRaw(ref place) => {
+                place.hash_stable(hcx, hasher);
+            }
             mir::StatementKind::Retag { fn_entry, ref place } => {
                 fn_entry.hash_stable(hcx, hasher);
                 place.hash_stable(hcx, hasher);
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 36bc2edcf58..0817c2e8bf2 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1766,6 +1766,13 @@ pub enum StatementKind<'tcx> {
         place: Place<'tcx>,
     },
 
+    /// Escape the given reference to a raw pointer, so that it can be accessed
+    /// without precise provenance tracking. These statements are currently only interpreted
+    /// by miri and only generated when "-Z mir-emit-retag" is passed.
+    /// See <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/>
+    /// for more details.
+    EscapeToRaw(Operand<'tcx>),
+
     /// Mark one terminating point of a region scope (i.e. static region).
     /// (The starting point(s) arise implicitly from borrows.)
     EndRegion(region::Scope),
@@ -1827,6 +1834,7 @@ impl<'tcx> Debug for Statement<'tcx> {
             EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
             Retag { fn_entry, ref place } =>
                 write!(fmt, "Retag({}{:?})", if fn_entry { "[fn entry] " } else { "" }, place),
+            EscapeToRaw(ref place) => write!(fmt, "EscapeToRaw({:?})", place),
             StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place),
             StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place),
             SetDiscriminant {
@@ -2968,6 +2976,7 @@ EnumTypeFoldableImpl! {
         (StatementKind::StorageDead)(a),
         (StatementKind::InlineAsm) { asm, outputs, inputs },
         (StatementKind::Retag) { fn_entry, place },
+        (StatementKind::EscapeToRaw)(place),
         (StatementKind::EndRegion)(a),
         (StatementKind::AscribeUserType)(a, v, b),
         (StatementKind::Nop),
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index c130e047e47..2a994ee0509 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -385,6 +385,9 @@ macro_rules! make_mir_visitor {
                             location
                         );
                     }
+                    StatementKind::EscapeToRaw(ref $($mutability)* op) => {
+                        self.visit_operand(op, location);
+                    }
                     StatementKind::StorageLive(ref $($mutability)* local) => {
                         self.visit_local(
                             local,
@@ -1022,7 +1025,7 @@ pub enum MutatingUseContext<'tcx> {
     ///     f(&mut x.y);
     ///
     Projection,
-    /// Retagging (updating the "Stacked Borrows" tag)
+    /// Retagging, a "Stacked Borrows" shadow state operation
     Retag,
 }
 
diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs
index c8c8e02bf05..8bda2c98594 100644
--- a/src/librustc_codegen_llvm/mir/statement.rs
+++ b/src/librustc_codegen_llvm/mir/statement.rs
@@ -105,8 +105,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
                 bx
             }
             mir::StatementKind::FakeRead(..) |
-            mir::StatementKind::EndRegion(_) |
+            mir::StatementKind::EndRegion(..) |
             mir::StatementKind::Retag { .. } |
+            mir::StatementKind::EscapeToRaw { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => bx,
         }
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index d4f00ab3bb9..533b777d67d 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -601,6 +601,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
             StatementKind::Nop
             | StatementKind::AscribeUserType(..)
             | StatementKind::Retag { .. }
+            | StatementKind::EscapeToRaw { .. }
             | StatementKind::StorageLive(..) => {
                 // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
                 // to borrow check.
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index cfe03c2d1c7..576509c0fdd 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -137,6 +137,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
             StatementKind::Nop |
             StatementKind::AscribeUserType(..) |
             StatementKind::Retag { .. } |
+            StatementKind::EscapeToRaw { .. } |
             StatementKind::StorageLive(..) => {
                 // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
                 // to borrow check.
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 734ddbc3ab9..3c2301ba4a7 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -1290,11 +1290,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 }
             }
             StatementKind::FakeRead(..)
-            | StatementKind::StorageLive(_)
-            | StatementKind::StorageDead(_)
+            | StatementKind::StorageLive(..)
+            | StatementKind::StorageDead(..)
             | StatementKind::InlineAsm { .. }
             | StatementKind::EndRegion(_)
             | StatementKind::Retag { .. }
+            | StatementKind::EscapeToRaw { .. }
             | StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 69d2a89b5f2..811da9e1acc 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -339,6 +339,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
             mir::StatementKind::SetDiscriminant { .. } |
             mir::StatementKind::StorageLive(..) |
             mir::StatementKind::Retag { .. } |
+            mir::StatementKind::EscapeToRaw { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => {}
 
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index 874e862de23..e6e165ef3de 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -301,8 +301,9 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
                 span_bug!(stmt.source_info.span,
                           "SetDiscriminant should not exist during borrowck");
             }
-            StatementKind::EndRegion(_) |
+            StatementKind::EndRegion(..) |
             StatementKind::Retag { .. } |
+            StatementKind::EscapeToRaw { .. } |
             StatementKind::AscribeUserType(..) |
             StatementKind::Nop => {}
         }
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 06748d60e45..118539fc58e 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -44,28 +44,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             }
 
             Misc => {
-                let src_layout = src.layout;
                 let src = self.read_immediate(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)) {
+                if self.type_is_fat_ptr(src.layout.ty) {
+                    match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
                         // pointers to extern types
                         (Immediate::Scalar(_),_) |
                         // slices and trait objects to other slices/trait objects
                         (Immediate::ScalarPair(..), true) => {
                             // No change to immediate
-                            self.write_immediate(src, dest)?;
+                            self.write_immediate(*src, dest)?;
                         }
                         // slices and trait objects to thin pointers (dropping the metadata)
                         (Immediate::ScalarPair(data, _), false) => {
@@ -73,11 +61,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                         }
                     }
                 } else {
-                    match src_layout.variants {
+                    match src.layout.variants {
                         layout::Variants::Single { index } => {
-                            if let Some(def) = src_layout.ty.ty_adt_def() {
+                            if let Some(def) = src.layout.ty.ty_adt_def() {
                                 // Cast from a univariant enum
-                                assert!(src_layout.is_zst());
+                                assert!(src.layout.is_zst());
                                 let discr_val = def
                                     .discriminant_for_variant(*self.tcx, index)
                                     .val;
@@ -90,8 +78,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                         layout::Variants::NicheFilling { .. } => {},
                     }
 
-                    let src = src.to_scalar()?;
-                    let dest_val = self.cast_scalar(src, src_layout, dest.layout)?;
+                    let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
                     self.write_scalar(dest_val, dest)?;
                 }
             }
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 27cf28ef41e..214ffd071cc 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -211,18 +211,6 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
         kind: MemoryKind<Self::MemoryKinds>,
     ) -> EvalResult<'tcx, Pointer<Self::PointerTag>>;
 
-    /// Executed when evaluating the `&` operator: Creating a new reference.
-    /// This has the chance to adjust the tag.  It should not change anything else!
-    /// `mutability` can be `None` in case a raw ptr is being created.
-    #[inline]
-    fn tag_reference(
-        _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
-        place: MPlaceTy<'tcx, Self::PointerTag>,
-        _mutability: Option<hir::Mutability>,
-    ) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
-        Ok(place.ptr)
-    }
-
     /// Executed when evaluating the `*` operator: Following a reference.
     /// This has the chance to adjust the tag.  It should not change anything else!
     /// `mutability` can be `None` in case a raw ptr is being dereferenced.
@@ -235,7 +223,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
         Ok(place.ptr)
     }
 
-    /// Execute a validation operation
+    /// Execute a retagging operation
     #[inline]
     fn retag(
         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
@@ -244,4 +232,13 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
     ) -> EvalResult<'tcx> {
         Ok(())
     }
+
+    /// Execute an escape-to-raw operation
+    #[inline]
+    fn escape_to_raw(
+        _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
+        _ptr: OpTy<'tcx, Self::PointerTag>,
+    ) -> EvalResult<'tcx> {
+        Ok(())
+    }
 }
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 19430c85cf7..da62594cb22 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -151,6 +151,16 @@ 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`.
+    #[inline(always)]
+    pub fn to_ref(self) -> Immediate<Tag> {
+        match self.meta {
+            None => Immediate::Scalar(self.ptr.into()),
+            Some(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
+        }
+    }
 }
 
 impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
@@ -266,7 +276,7 @@ where
     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 `create_ref`.
+    /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
     pub fn ref_to_mplace(
         &self,
         val: ImmTy<'tcx, M::PointerTag>,
@@ -294,24 +304,6 @@ where
         Ok(mplace)
     }
 
-    /// 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`.
-    /// `mutbl` indicates whether we are create a shared or mutable ref, or a raw pointer (`None`).
-    pub fn create_ref(
-        &mut self,
-        mut place: MPlaceTy<'tcx, M::PointerTag>,
-        mutbl: Option<hir::Mutability>,
-    ) -> EvalResult<'tcx, Immediate<M::PointerTag>> {
-        // Pointer tag tracking might want to adjust the tag
-        if M::ENABLE_PTR_TRACKING_HOOKS {
-            place.mplace.ptr = M::tag_reference(self, place, mutbl)?
-        }
-        Ok(match place.meta {
-            None => Immediate::Scalar(place.ptr.into()),
-            Some(meta) => Immediate::ScalarPair(place.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/step.rs b/src/librustc_mir/interpret/step.rs
index db055204c0a..ac13e5982da 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -12,7 +12,7 @@
 //!
 //! The main entry point is the `step` method.
 
-use rustc::{hir, mir};
+use rustc::mir;
 use rustc::ty::layout::LayoutOf;
 use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};
 
@@ -118,12 +118,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             // interpreter is solely intended for borrowck'ed code.
             FakeRead(..) => {}
 
-            // Retagging.
+            // Stacked Borrows.
             Retag { fn_entry, ref place } => {
                 let dest = self.eval_place(place)?;
                 M::retag(self, fn_entry, dest)?;
             }
+            EscapeToRaw(ref op) => {
+                let op = self.eval_operand(op, None)?;
+                M::escape_to_raw(self, op)?;
+            }
 
+            // Statements we do not track.
             EndRegion(..) => {}
             AscribeUserType(..) => {}
 
@@ -247,19 +252,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                 )?;
             }
 
-            Ref(_, borrow_kind, ref place) => {
+            Ref(_, _, ref place) => {
                 let src = self.eval_place(place)?;
                 let val = self.force_allocation(src)?;
-                let mutbl = match borrow_kind {
-                    mir::BorrowKind::Mut { .. } |
-                    mir::BorrowKind::Unique =>
-                        hir::MutMutable,
-                    mir::BorrowKind::Shared |
-                    mir::BorrowKind::Shallow =>
-                        hir::MutImmutable,
-                };
-                let val = self.create_ref(val, Some(mutbl))?;
-                self.write_immediate(val, dest)?;
+                self.write_immediate(val.to_ref(), dest)?;
             }
 
             NullaryOp(mir::NullOp::Box, _) => {
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index b5df625b302..4cd7ca8182e 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -447,10 +447,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         };
 
         let arg = OpTy {
-            op: Operand::Immediate(self.create_ref(
-                place,
-                None // this is a "raw reference"
-            )?),
+            op: Operand::Immediate(place.to_ref()),
             layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
         };
 
diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs
index a50011cf5a1..0be91c3c339 100644
--- a/src/librustc_mir/transform/add_retag.rs
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -20,20 +20,22 @@ use transform::{MirPass, MirSource};
 
 pub struct AddRetag;
 
-/// Determines whether this place is local: If it is part of a local variable.
-/// We do not consider writes to pointers local, only writes that immediately assign
-/// to a local variable.
-/// One important property here is that evaluating the place immediately after
-/// the assignment must produce the same place as what was used during the assignment.
-fn is_local<'tcx>(
+/// Determines whether this place is "stable": Whether, if we evaluate it again
+/// after the assignment, we can be sure to obtain the same place value.
+/// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
+/// copies.  Data races are UB.)
+fn is_stable<'tcx>(
     place: &Place<'tcx>,
 ) -> bool {
     use rustc::mir::Place::*;
 
     match *place {
-        Local { .. } => true,
-        Promoted(_) |
-        Static(_) => false,
+        // Locals and statics have stable addresses, for sure
+        Local { .. } |
+        Promoted { .. } |
+        Static { .. } =>
+            true,
+        // Recurse for projections
         Projection(ref proj) => {
             match proj.elem {
                 ProjectionElem::Deref |
@@ -47,15 +49,15 @@ fn is_local<'tcx>(
                 ProjectionElem::Subslice { .. } |
                 ProjectionElem::Downcast { .. } =>
                     // These just offset by a constant, entirely independent of everything else.
-                    is_local(&proj.base),
+                    is_stable(&proj.base),
             }
         }
     }
 }
 
-/// Determine whether this type has a reference in it, recursing below compound types but
+/// Determine whether this type may have a reference in it, recursing below compound types but
 /// not below references.
-fn has_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
+fn may_have_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
     match ty.sty {
         // Primitive types that are not references
         ty::Bool | ty::Char |
@@ -68,12 +70,12 @@ fn has_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> b
         ty::Adt(..) if ty.is_box() => true,
         // Compound types
         ty::Array(ty, ..) | ty::Slice(ty) =>
-            has_reference(ty, tcx),
+            may_have_reference(ty, tcx),
         ty::Tuple(tys) =>
-            tys.iter().any(|ty| has_reference(ty, tcx)),
+            tys.iter().any(|ty| may_have_reference(ty, tcx)),
         ty::Adt(adt, substs) =>
             adt.variants.iter().any(|v| v.fields.iter().any(|f|
-                has_reference(f.ty(tcx, substs), tcx)
+                may_have_reference(f.ty(tcx, substs), tcx)
             )),
         // Conservative fallback
         _ => true,
@@ -92,7 +94,9 @@ impl MirPass for AddRetag {
         let (span, arg_count) = (mir.span, mir.arg_count);
         let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
         let needs_retag = |place: &Place<'tcx>| {
-            is_local(place) && has_reference(place.ty(&*local_decls, tcx).to_ty(tcx), tcx)
+            // FIXME: Instead of giving up for unstable places, we should introduce
+            // a temporary and retag on that.
+            is_stable(place) && may_have_reference(place.ty(&*local_decls, tcx).to_ty(tcx), tcx)
         };
 
         // PART 1
@@ -118,23 +122,29 @@ impl MirPass for AddRetag {
         }
 
         // PART 2
-        // Retag return values of functions.
+        // Retag return values of functions.  Also escape-to-raw the argument of `drop`.
         // We collect the return destinations because we cannot mutate while iterating.
         let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
         for block_data in basic_blocks.iter_mut() {
-            match block_data.terminator {
-                Some(Terminator { kind: TerminatorKind::Call { ref destination, .. },
-                                  source_info }) => {
+            match block_data.terminator().kind {
+                TerminatorKind::Call { ref destination, .. } => {
                     // Remember the return destination for later
                     if let Some(ref destination) = destination {
                         if needs_retag(&destination.0) {
-                            returns.push((source_info, destination.0.clone(), destination.1));
+                            returns.push((
+                                block_data.terminator().source_info,
+                                destination.0.clone(),
+                                destination.1,
+                            ));
                         }
                     }
                 }
+                TerminatorKind::Drop { .. } |
+                TerminatorKind::DropAndReplace { .. } => {
+                    // `Drop` is also a call, but it doesn't return anything so we are good.
+                }
                 _ => {
                     // Not a block ending in a Call -> ignore.
-                    // `Drop` is also a call, but it doesn't return anything so we are good.
                 }
             }
         }
@@ -153,21 +163,43 @@ impl MirPass for AddRetag {
             // iterate backwards using indices.
             for i in (0..block_data.statements.len()).rev() {
                 match block_data.statements[i].kind {
-                    // Assignments can make values obtained elsewhere "local".
-                    // We could try to be smart here and e.g. only retag if the assignment
-                    // loaded from memory, but that seems risky: We might miss a subtle corner
-                    // case.
-                    StatementKind::Assign(ref place, box Rvalue::Use(..))
-                    if needs_retag(place) => {
+                    // If we are casting *from* a reference, we may have to escape-to-raw.
+                    StatementKind::Assign(_, box Rvalue::Cast(
+                        CastKind::Misc,
+                        ref src,
+                        dest_ty,
+                    )) => {
+                        let src_ty = src.ty(&*local_decls, tcx);
+                        if src_ty.is_region_ptr() {
+                            // The only `Misc` casts on references are those creating raw pointers.
+                            assert!(dest_ty.is_unsafe_ptr());
+                            // Insert escape-to-raw before the cast.  We are not concerned
+                            // with stability here: Our EscapeToRaw will not change the value
+                            // that the cast will then use.
+                            // `src` might be a "move", but we rely on this not actually moving
+                            // but just doing a memcpy.  It is crucial that we do EscapeToRaw
+                            // on the src because we need it with its original type.
+                            let source_info = block_data.statements[i].source_info;
+                            block_data.statements.insert(i, Statement {
+                                source_info,
+                                kind: StatementKind::EscapeToRaw(src.clone()),
+                            });
+                        }
+                    }
+                    // Assignments of reference or ptr type are the ones where we may have
+                    // to update tags.  This includes `x = &[mut] ...` and hence
+                    // we also retag after taking a reference!
+                    StatementKind::Assign(ref place, _) if needs_retag(place) => {
                         // Insert a retag after the assignment.
                         let source_info = block_data.statements[i].source_info;
-                        block_data.statements.insert(i+1,Statement {
+                        block_data.statements.insert(i+1, Statement {
                             source_info,
                             kind: StatementKind::Retag { fn_entry: false, place: place.clone() },
                         });
                     }
+                    // Do nothing for the rest
                     _ => {},
-                }
+                };
             }
         }
     }
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index c28bb0ca357..4ebeebca227 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -114,6 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
             StatementKind::StorageDead(..) |
             StatementKind::EndRegion(..) |
             StatementKind::Retag { .. } |
+            StatementKind::EscapeToRaw { .. } |
             StatementKind::AscribeUserType(..) |
             StatementKind::Nop => {
                 // safe (at least as emitted during MIR construction)
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index ca9c4eb9b8b..5fd83321296 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -1168,6 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 StatementKind::InlineAsm {..} |
                 StatementKind::EndRegion(_) |
                 StatementKind::Retag { .. } |
+                StatementKind::EscapeToRaw { .. } |
                 StatementKind::AscribeUserType(..) |
                 StatementKind::Nop => {}
             }
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 1e193485950..ed13063cfdf 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -242,6 +242,7 @@ fn check_statement(
         | StatementKind::StorageLive(_)
         | StatementKind::StorageDead(_)
         | StatementKind::Retag { .. }
+        | StatementKind::EscapeToRaw { .. }
         | StatementKind::EndRegion(_)
         | StatementKind::AscribeUserType(..)
         | StatementKind::Nop => Ok(()),
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index c1c127fa8d6..445ffbbcf34 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -65,10 +65,11 @@ impl RemoveNoopLandingPads {
                     // turn a landing pad to a non-nop
                 }
 
-                StatementKind::Assign(_, _) |
+                StatementKind::Assign { .. } |
                 StatementKind::SetDiscriminant { .. } |
                 StatementKind::InlineAsm { .. } |
-                StatementKind::Retag { .. } => {
+                StatementKind::Retag { .. } |
+                StatementKind::EscapeToRaw { .. } => {
                     return false;
                 }
             }
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index a5a19f04b7e..8f026c706fd 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -163,6 +163,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             mir::StatementKind::InlineAsm { .. } |
             mir::StatementKind::EndRegion(_) |
             mir::StatementKind::Retag { .. } |
+            mir::StatementKind::EscapeToRaw { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => continue,
             mir::StatementKind::SetDiscriminant{ .. } =>
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index ecfe7d13782..68840ed4a48 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -85,6 +85,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
             StatementKind::FakeRead(..) => "StatementKind::FakeRead",
             StatementKind::EndRegion(..) => "StatementKind::EndRegion",
             StatementKind::Retag { .. } => "StatementKind::Retag",
+            StatementKind::EscapeToRaw { .. } => "StatementKind::EscapeToRaw",
             StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
             StatementKind::StorageLive(..) => "StatementKind::StorageLive",
             StatementKind::StorageDead(..) => "StatementKind::StorageDead",
diff --git a/src/test/mir-opt/inline-retag.rs b/src/test/mir-opt/inline-retag.rs
index 4b3280ee561..1e5e1ad5ed1 100644
--- a/src/test/mir-opt/inline-retag.rs
+++ b/src/test/mir-opt/inline-retag.rs
@@ -32,6 +32,8 @@ fn bar() -> bool {
 //     bb0: {
 //         ...
 //         Retag(_3);
+//         ...
+//         Retag(_3);
 //         Retag(_6);
 //         StorageLive(_9);
 //         _9 = (*_3);
diff --git a/src/test/mir-opt/retag.rs b/src/test/mir-opt/retag.rs
index 9c013008ab2..7da55c0868c 100644
--- a/src/test/mir-opt/retag.rs
+++ b/src/test/mir-opt/retag.rs
@@ -26,7 +26,9 @@ fn main() {
     {
         let v = Test(0).foo(&mut x); // just making sure we do not panic when there is a tuple struct ctor
         let w = { v }; // assignment
-        let _w = w; // reborrow
+        let w = w; // reborrow
+        // escape-to-raw (mut)
+        let _w = w as *mut _;
     }
 
     // Also test closures
@@ -35,6 +37,9 @@ fn main() {
 
     // need to call `foo_shr` or it doesn't even get generated
     Test(0).foo_shr(&0);
+
+    // escape-to-raw (shr)
+    let _w = _w as *const _;
 }
 
 // END RUST SOURCE
@@ -44,6 +49,7 @@ fn main() {
 //         Retag([fn entry] _2);
 //         ...
 //         _0 = &mut (*_3);
+//         Retag(_0);
 //         ...
 //         return;
 //     }
@@ -73,23 +79,36 @@ fn main() {
 //         _9 = move _3;
 //         Retag(_9);
 //         _8 = &mut (*_9);
+//         Retag(_8);
 //         StorageDead(_9);
 //         StorageLive(_10);
 //         _10 = move _8;
 //         Retag(_10);
 //         ...
-//         _13 = move _14(move _15) -> bb2;
+//         _14 = &mut (*_10);
+//         Retag(_14);
+//         EscapeToRaw(move _14);
+//         _13 = move _14 as *mut i32 (Misc);
+//         ...
+//         _17 = move _18(move _19) -> bb2;
 //     }
 //
 //     bb2: {
-//         Retag(_13);
+//         Retag(_17);
+//         ...
+//         _21 = const Test::foo_shr(move _22, move _24) -> bb3;
+//     }
+//
+//     bb3: {
 //         ...
+//         return;
 //     }
+//
 //     ...
 // }
 // END rustc.main.EraseRegions.after.mir
 // START rustc.main-{{closure}}.EraseRegions.after.mir
-// fn main::{{closure}}(_1: &[closure@NodeId(117)], _2: &i32) -> &i32 {
+// fn main::{{closure}}(_1: &[closure@NodeId(124)], _2: &i32) -> &i32 {
 //     ...
 //     bb0: {
 //         Retag([fn entry] _1);