about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-10-24 11:47:17 +0200
committerRalf Jung <post@ralfj.de>2018-10-29 09:05:18 +0100
commitaafcf2c9422e7e1de2ebefd51d78cf1d07d02cd6 (patch)
tree8a2704a03646d186d81c7de32e79a981f3567bd1
parent4e88b7363b7858960ccfd87326ece9d00bf4d973 (diff)
downloadrust-aafcf2c9422e7e1de2ebefd51d78cf1d07d02cd6.tar.gz
rust-aafcf2c9422e7e1de2ebefd51d78cf1d07d02cd6.zip
Emit Retag statements, kill Validate statements
Also "rename" -Zmir-emit-validate to -Zmir-emit-retag, which is just a boolean (yes or no).
-rw-r--r--src/bootstrap/bin/rustc.rs7
-rw-r--r--src/librustc/ich/impls_mir.rs23
-rw-r--r--src/librustc/mir/mod.rs74
-rw-r--r--src/librustc/mir/visit.rs20
-rw-r--r--src/librustc/session/config.rs5
-rw-r--r--src/librustc/ty/context.rs4
-rw-r--r--src/librustc_codegen_llvm/mir/analyze.rs3
-rw-r--r--src/librustc_codegen_llvm/mir/statement.rs2
-rw-r--r--src/librustc_mir/borrow_check/mod.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/invalidation.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs2
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs2
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs2
-rw-r--r--src/librustc_mir/interpret/machine.rs6
-rw-r--r--src/librustc_mir/interpret/step.rs9
-rw-r--r--src/librustc_mir/transform/add_retag.rs169
-rw-r--r--src/librustc_mir/transform/add_validation.rs395
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs2
-rw-r--r--src/librustc_mir/transform/erase_regions.rs19
-rw-r--r--src/librustc_mir/transform/mod.rs12
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs2
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs2
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs2
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs2
-rw-r--r--src/librustc_mir/util/liveness.rs2
-rw-r--r--src/librustc_passes/mir_stats.rs2
26 files changed, 233 insertions, 543 deletions
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index b6764c1aaea..07a05aeae5d 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -254,7 +254,12 @@ fn main() {
         // When running miri tests, we need to generate MIR for all libraries
         if env::var("TEST_MIRI").ok().map_or(false, |val| val == "true") {
             cmd.arg("-Zalways-encode-mir");
-            cmd.arg("-Zmir-emit-validate=1");
+            // These options are preferred by miri, to be able to perform better validation,
+            // but the bootstrap compiler might not understand them.
+            if stage != "0" {
+                cmd.arg("-Zmir-emit-retag");
+                cmd.arg("-Zmir-opt-level=0");
+            }
         }
 
         // Force all crates compiled by this compiler to (a) be unstable and (b)
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 274a2df283c..a73fe2b8a1a 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -257,9 +257,9 @@ for mir::StatementKind<'gcx> {
             mir::StatementKind::EndRegion(ref region_scope) => {
                 region_scope.hash_stable(hcx, hasher);
             }
-            mir::StatementKind::Validate(ref op, ref places) => {
-                op.hash_stable(hcx, hasher);
-                places.hash_stable(hcx, hasher);
+            mir::StatementKind::Retag { fn_entry, ref place } => {
+                fn_entry.hash_stable(hcx, hasher);
+                place.hash_stable(hcx, hasher);
             }
             mir::StatementKind::AscribeUserType(ref place, ref variance, ref c_ty) => {
                 place.hash_stable(hcx, hasher);
@@ -278,23 +278,6 @@ for mir::StatementKind<'gcx> {
 
 impl_stable_hash_for!(enum mir::FakeReadCause { ForMatchGuard, ForMatchedPlace, ForLet });
 
-impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
-    for mir::ValidationOperand<'gcx, T>
-    where T: HashStable<StableHashingContext<'a>>
-{
-    fn hash_stable<W: StableHasherResult>(&self,
-                                          hcx: &mut StableHashingContext<'a>,
-                                          hasher: &mut StableHasher<W>)
-    {
-        self.place.hash_stable(hcx, hasher);
-        self.ty.hash_stable(hcx, hasher);
-        self.re.hash_stable(hcx, hasher);
-        self.mutbl.hash_stable(hcx, hasher);
-    }
-}
-
-impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) });
-
 impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Place<'gcx> {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'a>,
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 4fea07011cc..4f6a8158933 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1754,10 +1754,13 @@ pub enum StatementKind<'tcx> {
         inputs: Box<[Operand<'tcx>]>,
     },
 
-    /// Assert the given places to be valid inhabitants of their type.  These statements are
-    /// currently only interpreted by miri and only generated when "-Z mir-emit-validate" is passed.
-    /// See <https://internals.rust-lang.org/t/types-as-contracts/5562/73> for more details.
-    Validate(ValidationOp, Vec<ValidationOperand<'tcx, Place<'tcx>>>),
+    /// Retag references in the given place, ensuring they got fresh tags.  This is
+    /// part of the Stacked Borrows model. `fn_entry` indicates whether this
+    /// is the initial retag that happens in the function prolog. 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.
+    Retag { fn_entry: bool, place: Place<'tcx> },
 
     /// Mark one terminating point of a region scope (i.e. static region).
     /// (The starting point(s) arise implicitly from borrows.)
@@ -1810,57 +1813,6 @@ pub enum FakeReadCause {
     ForLet,
 }
 
-/// The `ValidationOp` describes what happens with each of the operands of a
-/// `Validate` statement.
-#[derive(Copy, Clone, RustcEncodable, RustcDecodable, PartialEq, Eq)]
-pub enum ValidationOp {
-    /// Recursively traverse the place following the type and validate that all type
-    /// invariants are maintained.  Furthermore, acquire exclusive/read-only access to the
-    /// memory reachable from the place.
-    Acquire,
-    /// Recursive traverse the *mutable* part of the type and relinquish all exclusive
-    /// access.
-    Release,
-    /// Recursive traverse the *mutable* part of the type and relinquish all exclusive
-    /// access *until* the given region ends.  Then, access will be recovered.
-    Suspend(region::Scope),
-}
-
-impl Debug for ValidationOp {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        use self::ValidationOp::*;
-        match *self {
-            Acquire => write!(fmt, "Acquire"),
-            Release => write!(fmt, "Release"),
-            // (reuse lifetime rendering policy from ppaux.)
-            Suspend(ref ce) => write!(fmt, "Suspend({})", ty::ReScope(*ce)),
-        }
-    }
-}
-
-// This is generic so that it can be reused by miri
-#[derive(Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
-pub struct ValidationOperand<'tcx, T> {
-    pub place: T,
-    pub ty: Ty<'tcx>,
-    pub re: Option<region::Scope>,
-    pub mutbl: hir::Mutability,
-}
-
-impl<'tcx, T: Debug> Debug for ValidationOperand<'tcx, T> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        write!(fmt, "{:?}: {:?}", self.place, self.ty)?;
-        if let Some(ce) = self.re {
-            // (reuse lifetime rendering policy from ppaux.)
-            write!(fmt, "/{}", ty::ReScope(ce))?;
-        }
-        if let hir::MutImmutable = self.mutbl {
-            write!(fmt, " (imm)")?;
-        }
-        Ok(())
-    }
-}
-
 impl<'tcx> Debug for Statement<'tcx> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
         use self::StatementKind::*;
@@ -1869,7 +1821,8 @@ impl<'tcx> Debug for Statement<'tcx> {
             FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
             // (reuse lifetime rendering policy from ppaux.)
             EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
-            Validate(ref op, ref places) => write!(fmt, "Validate({:?}, {:?})", op, places),
+            Retag { fn_entry, ref place } =>
+                write!(fmt, "Retag({}{:?})", if fn_entry { "[fn entry]: " } else { "" }, place),
             StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place),
             StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place),
             SetDiscriminant {
@@ -2944,7 +2897,6 @@ CloneTypeFoldableAndLiftImpls! {
     SourceInfo,
     UpvarDecl,
     FakeReadCause,
-    ValidationOp,
     SourceScope,
     SourceScopeData,
     SourceScopeLocalData,
@@ -2998,12 +2950,6 @@ BraceStructTypeFoldableImpl! {
 }
 
 BraceStructTypeFoldableImpl! {
-    impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Place<'tcx>> {
-        place, ty, re, mutbl
-    }
-}
-
-BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
         source_info, kind
     }
@@ -3017,7 +2963,7 @@ EnumTypeFoldableImpl! {
         (StatementKind::StorageLive)(a),
         (StatementKind::StorageDead)(a),
         (StatementKind::InlineAsm) { asm, outputs, inputs },
-        (StatementKind::Validate)(a, b),
+        (StatementKind::Retag) { fn_entry, 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 caa627441ce..d60fff18b94 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -371,16 +371,12 @@ macro_rules! make_mir_visitor {
                         );
                     }
                     StatementKind::EndRegion(_) => {}
-                    StatementKind::Validate(_, ref $($mutability)* places) => {
-                        for operand in places {
-                            self.visit_place(
-                                & $($mutability)* operand.place,
-                                PlaceContext::NonUse(NonUseContext::Validate),
-                                location
-                            );
-                            self.visit_ty(& $($mutability)* operand.ty,
-                                          TyContext::Location(location));
-                        }
+                    StatementKind::Retag { fn_entry: _, ref $($mutability)* place } => {
+                        self.visit_place(
+                            place,
+                            PlaceContext::MutatingUse(MutatingUseContext::Retag),
+                            location,
+                        );
                     }
                     StatementKind::SetDiscriminant{ ref $($mutability)* place, .. } => {
                         self.visit_place(
@@ -1010,6 +1006,8 @@ pub enum MutatingUseContext<'tcx> {
     ///     f(&mut x.y);
     ///
     Projection,
+    /// Retagging (updating the "Stacked Borrows" tag)
+    Retag,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -1020,8 +1018,6 @@ pub enum NonUseContext {
     StorageDead,
     /// User type annotation assertions for NLL.
     AscribeUserTy,
-    /// Validation command.
-    Validate,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 569e7a24d23..76200777584 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1282,9 +1282,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "in addition to `.mir` files, create graphviz `.dot` files"),
     dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED],
         "if set, exclude the pass number when dumping MIR (used in tests)"),
-    mir_emit_validate: usize = (0, parse_uint, [TRACKED],
-        "emit Validate MIR statements, interpreted e.g. by miri (0: do not emit; 1: if function \
-         contains unsafe block, only validate arguments; 2: always emit full validation)"),
+    mir_emit_retag: bool = (false, parse_bool, [TRACKED],
+        "emit Retagging MIR statements, interpreted e.g. by miri; implies -Zmir-opt-level=0"),
     perf_stats: bool = (false, parse_bool, [UNTRACKED],
         "print some performance-related statistics"),
     hir_stats: bool = (false, parse_bool, [UNTRACKED],
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index d4b47db6081..409665e4777 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1547,11 +1547,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     }
 
     /// Should we emit EndRegion MIR statements? These are consumed by
-    /// MIR borrowck, but not when NLL is used. They are also consumed
-    /// by the validation stuff.
+    /// MIR borrowck, but not when NLL is used.
     pub fn emit_end_regions(self) -> bool {
         self.sess.opts.debugging_opts.emit_end_regions ||
-            self.sess.opts.debugging_opts.mir_emit_validate > 0 ||
             self.use_mir_borrowck()
     }
 
diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs
index a63cbe70df6..a93c6faaf7b 100644
--- a/src/librustc_codegen_llvm/mir/analyze.rs
+++ b/src/librustc_codegen_llvm/mir/analyze.rs
@@ -219,7 +219,8 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> {
                 self.assign(local, location);
             }
 
-            PlaceContext::NonUse(_) => {}
+            PlaceContext::NonUse(_) |
+            PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
 
             PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
             PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs
index 93be0074f6e..cc4e64e0788 100644
--- a/src/librustc_codegen_llvm/mir/statement.rs
+++ b/src/librustc_codegen_llvm/mir/statement.rs
@@ -109,7 +109,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
             }
             mir::StatementKind::FakeRead(..) |
             mir::StatementKind::EndRegion(_) |
-            mir::StatementKind::Validate(..) |
+            mir::StatementKind::Retag { .. } |
             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 6ef8b155458..25e8b4ba722 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -574,9 +574,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
             }
             StatementKind::Nop
             | StatementKind::AscribeUserType(..)
-            | StatementKind::Validate(..)
+            | StatementKind::Retag { .. }
             | StatementKind::StorageLive(..) => {
-                // `Nop`, `AscribeUserType`, `Validate`, and `StorageLive` are irrelevant
+                // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
                 // to borrow check.
             }
             StatementKind::StorageDead(local) => {
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index 002f35880ae..0d0f5a60ed4 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -136,9 +136,9 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
             StatementKind::EndRegion(..) |
             StatementKind::Nop |
             StatementKind::AscribeUserType(..) |
-            StatementKind::Validate(..) |
+            StatementKind::Retag { .. } |
             StatementKind::StorageLive(..) => {
-                // `Nop`, `AscribeUserType`, `Validate`, and `StorageLive` are irrelevant
+                // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
                 // to borrow check.
             }
             StatementKind::StorageDead(local) => {
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 3098acffa23..c4eac2c9341 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -1264,7 +1264,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             | StatementKind::StorageDead(_)
             | StatementKind::InlineAsm { .. }
             | StatementKind::EndRegion(_)
-            | StatementKind::Validate(..)
+            | StatementKind::Retag { .. }
             | StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index cfccb950e82..69d2a89b5f2 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -338,7 +338,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
             mir::StatementKind::FakeRead(..) |
             mir::StatementKind::SetDiscriminant { .. } |
             mir::StatementKind::StorageLive(..) |
-            mir::StatementKind::Validate(..) |
+            mir::StatementKind::Retag { .. } |
             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 08696dc098e..968e39cc3d1 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -302,7 +302,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
                           "SetDiscriminant should not exist during borrowck");
             }
             StatementKind::EndRegion(_) |
-            StatementKind::Validate(..) |
+            StatementKind::Retag { .. } |
             StatementKind::AscribeUserType(..) |
             StatementKind::Nop => {}
         }
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 7811dcb0663..5a5002dece5 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -242,10 +242,10 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
 
     /// Execute a validation operation
     #[inline]
-    fn validation_op(
+    fn retag(
         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
-        _op: ::rustc::mir::ValidationOp,
-        _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,
+        _fn_entry: bool,
+        _place: PlaceTy<'tcx, Self::PointerTag>,
     ) -> EvalResult<'tcx> {
         Ok(())
     }
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index 1bab536e3e0..80b9948f612 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -118,11 +118,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             // interpreter is solely intended for borrowck'ed code.
             FakeRead(..) => {}
 
-            // Validity checks.
-            Validate(op, ref places) => {
-                for operand in places {
-                    M::validation_op(self, op, operand)?;
-                }
+            // Retagging.
+            Retag { fn_entry, ref place } => {
+                let dest = self.eval_place(place)?;
+                M::retag(self, fn_entry, dest)?;
             }
 
             EndRegion(..) => {}
diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs
new file mode 100644
index 00000000000..0f16e29aae9
--- /dev/null
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -0,0 +1,169 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
+//! It has to be run really early, before transformations like inlining, because
+//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
+//! of MIR building, and only after this pass we think of the program has having the
+//! normal MIR semantics.
+
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::mir::*;
+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>(
+    place: &Place<'tcx>,
+) -> bool {
+    use rustc::mir::Place::*;
+
+    match *place {
+        Local { .. } => true,
+        Promoted(_) |
+        Static(_) => false,
+        Projection(ref proj) => {
+            match proj.elem {
+                ProjectionElem::Deref |
+                ProjectionElem::Index(_) =>
+                    // Which place these point to depends on external circumstances
+                    // (a local storing the array index, the current value of
+                    // the projection base), so we stop tracking here.
+                    false,
+                _ => is_local(&proj.base),
+            }
+        }
+    }
+}
+
+/// Determine whether this type has 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 {
+    match ty.sty {
+        // Primitive types that are not references
+        ty::Bool | ty::Char |
+        ty::Float(_) | ty::Int(_) | ty::Uint(_) |
+        ty::RawPtr(..) | ty::FnPtr(..) |
+        ty::Str | ty::FnDef(..) | ty::Never =>
+            false,
+        // References
+        ty::Ref(..) => true,
+        ty::Adt(..) if ty.is_box() => true,
+        // Compound types
+        ty::Array(ty, ..) | ty::Slice(ty) =>
+            has_reference(ty, tcx),
+        ty::Tuple(tys) =>
+            tys.iter().any(|ty| has_reference(ty, tcx)),
+        ty::Adt(adt, substs) =>
+            adt.variants.iter().any(|v| v.fields.iter().any(|f|
+                has_reference(f.ty(tcx, substs), tcx)
+            )),
+        // Conservative fallback
+        _ => true,
+    }
+}
+
+impl MirPass for AddRetag {
+    fn run_pass<'a, 'tcx>(&self,
+                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          _src: MirSource,
+                          mir: &mut Mir<'tcx>)
+    {
+        if !tcx.sess.opts.debugging_opts.mir_emit_retag {
+            return;
+        }
+        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)
+        };
+
+        // PART 1
+        // Retag arguments at the beginning of the start block.
+        {
+            let source_info = SourceInfo {
+                scope: OUTERMOST_SOURCE_SCOPE,
+                span: span, // FIXME: Consider using just the span covering the function
+                            // argument declaration.
+            };
+            // Gather all arguments, skip return value.
+            let places = local_decls.iter_enumerated().skip(1).take(arg_count)
+                    .map(|(local, _)| Place::Local(local))
+                    .filter(needs_retag)
+                    .collect::<Vec<_>>();
+            // Emit their retags.
+            basic_blocks[START_BLOCK].statements.splice(0..0,
+                places.into_iter().map(|place| Statement {
+                    source_info,
+                    kind: StatementKind::Retag { fn_entry: true, place },
+                })
+            );
+        }
+
+        // PART 2
+        // Retag return values of functions.
+        // 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 }) => {
+                    // 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));
+                        }
+                    }
+                }
+                _ => {
+                    // Not a block ending in a Call -> ignore.
+                    // `Drop` is also a call, but it doesn't return anything so we are good.
+                }
+            }
+        }
+        // Now we go over the returns we collected to retag the return values.
+        for (source_info, dest_place, dest_block) in returns {
+            basic_blocks[dest_block].statements.insert(0, Statement {
+                source_info,
+                kind: StatementKind::Retag { fn_entry: false, place: dest_place },
+            });
+        }
+
+        // PART 3
+        // Add retag after assignment.
+        for block_data in basic_blocks {
+            // We want to insert statements as we iterate.  To this end, we
+            // 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) => {
+                        // Insert a retag after the assignment.
+                        let source_info = block_data.statements[i].source_info;
+                        block_data.statements.insert(i+1,Statement {
+                            source_info,
+                            kind: StatementKind::Retag { fn_entry: false, place: place.clone() },
+                        });
+                    }
+                    _ => {},
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs
deleted file mode 100644
index 5b489b5db94..00000000000
--- a/src/librustc_mir/transform/add_validation.rs
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
-//! It has to be run really early, before transformations like inlining, because
-//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
-//! of MIR building, and only after this pass we think of the program has having the
-//! normal MIR semantics.
-
-use rustc::ty::{self, TyCtxt, RegionKind};
-use rustc::hir;
-use rustc::mir::*;
-use rustc::middle::region;
-use transform::{MirPass, MirSource};
-
-pub struct AddValidation;
-
-/// Determine the "context" of the place: Mutability and region.
-fn place_context<'a, 'tcx, D>(
-    place: &Place<'tcx>,
-    local_decls: &D,
-    tcx: TyCtxt<'a, 'tcx, 'tcx>
-) -> (Option<region::Scope>, hir::Mutability)
-    where D: HasLocalDecls<'tcx>
-{
-    use rustc::mir::Place::*;
-
-    match *place {
-        Local { .. } => (None, hir::MutMutable),
-        Promoted(_) |
-        Static(_) => (None, hir::MutImmutable),
-        Projection(ref proj) => {
-            match proj.elem {
-                ProjectionElem::Deref => {
-                    // Computing the inside the recursion makes this quadratic.
-                    // We don't expect deep paths though.
-                    let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
-                    // A Deref projection may restrict the context, this depends on the type
-                    // being deref'd.
-                    let context = match ty.sty {
-                        ty::Ref(re, _, mutbl) => {
-                            let re = match re {
-                                &RegionKind::ReScope(ce) => Some(ce),
-                                &RegionKind::ReErased =>
-                                    bug!("AddValidation pass must be run before erasing lifetimes"),
-                                _ => None
-                            };
-                            (re, mutbl)
-                        }
-                        ty::RawPtr(_) =>
-                            // There is no guarantee behind even a mutable raw pointer,
-                            // no write locks are acquired there, so we also don't want to
-                            // release any.
-                            (None, hir::MutImmutable),
-                        ty::Adt(adt, _) if adt.is_box() => (None, hir::MutMutable),
-                        _ => bug!("Deref on a non-pointer type {:?}", ty),
-                    };
-                    // "Intersect" this restriction with proj.base.
-                    if let (Some(_), hir::MutImmutable) = context {
-                        // This is already as restricted as it gets, no need to even recurse
-                        context
-                    } else {
-                        let base_context = place_context(&proj.base, local_decls, tcx);
-                        // The region of the outermost Deref is always most restrictive.
-                        let re = context.0.or(base_context.0);
-                        let mutbl = context.1.and(base_context.1);
-                        (re, mutbl)
-                    }
-
-                }
-                _ => place_context(&proj.base, local_decls, tcx),
-            }
-        }
-    }
-}
-
-/// Check if this function contains an unsafe block or is an unsafe function.
-fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> bool {
-    use rustc::hir::intravisit::{self, Visitor, FnKind};
-    use rustc::hir::map::blocks::FnLikeNode;
-    use rustc::hir::Node;
-
-    /// Decide if this is an unsafe block
-    fn block_is_unsafe(block: &hir::Block) -> bool {
-        use rustc::hir::BlockCheckMode::*;
-
-        match block.rules {
-            UnsafeBlock(_) | PushUnsafeBlock(_) => true,
-            // For PopUnsafeBlock, we don't actually know -- but we will always also check all
-            // parent blocks, so we can safely declare the PopUnsafeBlock to not be unsafe.
-            DefaultBlock | PopUnsafeBlock(_) => false,
-        }
-    }
-
-    /// Decide if this FnLike is a closure
-    fn fn_is_closure<'a>(fn_like: FnLikeNode<'a>) -> bool {
-        match fn_like.kind() {
-            FnKind::Closure(_) => true,
-            FnKind::Method(..) | FnKind::ItemFn(..) => false,
-        }
-    }
-
-    let node_id = tcx.hir.as_local_node_id(src.def_id).unwrap();
-    let fn_like = match tcx.hir.body_owner_kind(node_id) {
-        hir::BodyOwnerKind::Fn => {
-            match FnLikeNode::from_node(tcx.hir.get(node_id)) {
-                Some(fn_like) => fn_like,
-                None => return false, // e.g. struct ctor shims -- such auto-generated code cannot
-                                      // contain unsafe.
-            }
-        },
-        _ => return false, // only functions can have unsafe
-    };
-
-    // Test if the function is marked unsafe.
-    if fn_like.unsafety() == hir::Unsafety::Unsafe {
-        return true;
-    }
-
-    // For closures, we need to walk up the parents and see if we are inside an unsafe fn or
-    // unsafe block.
-    if fn_is_closure(fn_like) {
-        let mut cur = fn_like.id();
-        loop {
-            // Go further upwards.
-            cur = tcx.hir.get_parent_node(cur);
-            let node = tcx.hir.get(cur);
-            // Check if this is an unsafe function
-            if let Some(fn_like) = FnLikeNode::from_node(node) {
-                if !fn_is_closure(fn_like) {
-                    if fn_like.unsafety() == hir::Unsafety::Unsafe {
-                        return true;
-                    }
-                }
-            }
-            // Check if this is an unsafe block, or an item
-            match node {
-                Node::Expr(&hir::Expr { node: hir::ExprKind::Block(ref block, _), ..}) => {
-                    if block_is_unsafe(&*block) {
-                        // Found an unsafe block, we can bail out here.
-                        return true;
-                    }
-                }
-                Node::Item(..) => {
-                    // No walking up beyond items.  This makes sure the loop always terminates.
-                    break;
-                }
-                _ => {},
-            }
-        }
-    }
-
-    // Visit the entire body of the function and check for unsafe blocks in there
-    struct FindUnsafe {
-        found_unsafe: bool,
-    }
-    let mut finder = FindUnsafe { found_unsafe: false };
-    // Run the visitor on the NodeId we got.  Seems like there is no uniform way to do that.
-    finder.visit_body(tcx.hir.body(fn_like.body()));
-
-    impl<'tcx> Visitor<'tcx> for FindUnsafe {
-        fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
-            intravisit::NestedVisitorMap::None
-        }
-
-        fn visit_block(&mut self, b: &'tcx hir::Block) {
-            if self.found_unsafe { return; } // short-circuit
-
-            if block_is_unsafe(b) {
-                // We found an unsafe block.  We can stop searching.
-                self.found_unsafe = true;
-            } else {
-                // No unsafe block here, go on searching.
-                intravisit::walk_block(self, b);
-            }
-        }
-    }
-
-    finder.found_unsafe
-}
-
-impl MirPass for AddValidation {
-    fn run_pass<'a, 'tcx>(&self,
-                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          src: MirSource,
-                          mir: &mut Mir<'tcx>)
-    {
-        let emit_validate = tcx.sess.opts.debugging_opts.mir_emit_validate;
-        if emit_validate == 0 {
-            return;
-        }
-        let restricted_validation = emit_validate == 1 && fn_contains_unsafe(tcx, src);
-        let (span, arg_count) = (mir.span, mir.arg_count);
-        let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
-
-        // Convert a place to a validation operand.
-        let place_to_operand = |place: Place<'tcx>| -> ValidationOperand<'tcx, Place<'tcx>> {
-            let (re, mutbl) = place_context(&place, local_decls, tcx);
-            let ty = place.ty(local_decls, tcx).to_ty(tcx);
-            ValidationOperand { place, ty, re, mutbl }
-        };
-
-        // Emit an Acquire at the beginning of the given block.  If we are in restricted emission
-        // mode (mir_emit_validate=1), also emit a Release immediately after the Acquire.
-        let emit_acquire = |block: &mut BasicBlockData<'tcx>, source_info, operands: Vec<_>| {
-            if operands.len() == 0 {
-                return; // Nothing to do
-            }
-            // Emit the release first, to avoid cloning if we do not emit it
-            if restricted_validation {
-                let release_stmt = Statement {
-                    source_info,
-                    kind: StatementKind::Validate(ValidationOp::Release, operands.clone()),
-                };
-                block.statements.insert(0, release_stmt);
-            }
-            // Now, the acquire
-            let acquire_stmt = Statement {
-                source_info,
-                kind: StatementKind::Validate(ValidationOp::Acquire, operands),
-            };
-            block.statements.insert(0, acquire_stmt);
-        };
-
-        // PART 1
-        // Add an AcquireValid at the beginning of the start block.
-        {
-            let source_info = SourceInfo {
-                scope: OUTERMOST_SOURCE_SCOPE,
-                span: span, // FIXME: Consider using just the span covering the function
-                            // argument declaration.
-            };
-            // Gather all arguments, skip return value.
-            let operands = local_decls.iter_enumerated().skip(1).take(arg_count)
-                    .map(|(local, _)| place_to_operand(Place::Local(local))).collect();
-            emit_acquire(&mut basic_blocks[START_BLOCK], source_info, operands);
-        }
-
-        // PART 2
-        // Add ReleaseValid/AcquireValid around function call terminators.  We don't use a visitor
-        // because we need to access the block that a Call jumps to.
-        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 args, ref destination, .. },
-                                  source_info }) => {
-                    // Before the call: Release all arguments *and* the return value.
-                    // The callee may write into the return value!  Note that this relies
-                    // on "release of uninitialized" to be a NOP.
-                    if !restricted_validation {
-                        let release_stmt = Statement {
-                            source_info,
-                            kind: StatementKind::Validate(ValidationOp::Release,
-                                destination.iter().map(|dest| place_to_operand(dest.0.clone()))
-                                .chain(
-                                    args.iter().filter_map(|op| {
-                                        match op {
-                                            &Operand::Copy(ref place) |
-                                            &Operand::Move(ref place) =>
-                                                Some(place_to_operand(place.clone())),
-                                            &Operand::Constant(..) => { None },
-                                        }
-                                    })
-                                ).collect())
-                        };
-                        block_data.statements.push(release_stmt);
-                    }
-                    // Remember the return destination for later
-                    if let &Some(ref destination) = destination {
-                        returns.push((source_info, destination.0.clone(), destination.1));
-                    }
-                }
-                Some(Terminator { kind: TerminatorKind::Drop { location: ref place, .. },
-                                  source_info }) |
-                Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref place, .. },
-                                  source_info }) => {
-                    // Before the call: Release all arguments
-                    if !restricted_validation {
-                        let release_stmt = Statement {
-                            source_info,
-                            kind: StatementKind::Validate(ValidationOp::Release,
-                                    vec![place_to_operand(place.clone())]),
-                        };
-                        block_data.statements.push(release_stmt);
-                    }
-                    // drop doesn't return anything, so we need no acquire.
-                }
-                _ => {
-                    // Not a block ending in a Call -> ignore.
-                }
-            }
-        }
-        // Now we go over the returns we collected to acquire the return values.
-        for (source_info, dest_place, dest_block) in returns {
-            emit_acquire(
-                &mut basic_blocks[dest_block],
-                source_info,
-                vec![place_to_operand(dest_place)]
-            );
-        }
-
-        if restricted_validation {
-            // No part 3 for us.
-            return;
-        }
-
-        // PART 3
-        // Add ReleaseValid/AcquireValid around Ref and Cast.  Again an iterator does not seem very
-        // suited as we need to add new statements before and after each Ref.
-        for block_data in basic_blocks {
-            // We want to insert statements around Ref commands as we iterate.  To this end, we
-            // iterate backwards using indices.
-            for i in (0..block_data.statements.len()).rev() {
-                match block_data.statements[i].kind {
-                    // When the borrow of this ref expires, we need to recover validation.
-                    StatementKind::Assign(_, box Rvalue::Ref(_, _, _)) => {
-                        // Due to a lack of NLL; we can't capture anything directly here.
-                        // Instead, we have to re-match and clone there.
-                        let (dest_place, re, src_place) = match block_data.statements[i].kind {
-                            StatementKind::Assign(ref dest_place,
-                                                  box Rvalue::Ref(re, _, ref src_place)) => {
-                                (dest_place.clone(), re, src_place.clone())
-                            },
-                            _ => bug!("We already matched this."),
-                        };
-                        // So this is a ref, and we got all the data we wanted.
-                        // Do an acquire of the result -- but only what it points to, so add a Deref
-                        // projection.
-                        let acquire_stmt = Statement {
-                            source_info: block_data.statements[i].source_info,
-                            kind: StatementKind::Validate(ValidationOp::Acquire,
-                                    vec![place_to_operand(dest_place.deref())]),
-                        };
-                        block_data.statements.insert(i+1, acquire_stmt);
-
-                        // The source is released until the region of the borrow ends.
-                        let op = match re {
-                            &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
-                            &RegionKind::ReErased =>
-                                bug!("AddValidation pass must be run before erasing lifetimes"),
-                            _ => ValidationOp::Release,
-                        };
-                        let release_stmt = Statement {
-                            source_info: block_data.statements[i].source_info,
-                            kind: StatementKind::Validate(op, vec![place_to_operand(src_place)]),
-                        };
-                        block_data.statements.insert(i, release_stmt);
-                    }
-                    // Casts can change what validation does (e.g. unsizing)
-                    StatementKind::Assign(_, box Rvalue::Cast(kind, Operand::Copy(_), _)) |
-                    StatementKind::Assign(_, box Rvalue::Cast(kind, Operand::Move(_), _))
-                        if kind != CastKind::Misc =>
-                    {
-                        // Due to a lack of NLL; we can't capture anything directly here.
-                        // Instead, we have to re-match and clone there.
-                        let (dest_place, src_place) = match block_data.statements[i].kind {
-                            StatementKind::Assign(ref dest_place,
-                                    box Rvalue::Cast(_, Operand::Copy(ref src_place), _)) |
-                            StatementKind::Assign(ref dest_place,
-                                    box Rvalue::Cast(_, Operand::Move(ref src_place), _)) =>
-                            {
-                                (dest_place.clone(), src_place.clone())
-                            },
-                            _ => bug!("We already matched this."),
-                        };
-
-                        // Acquire of the result
-                        let acquire_stmt = Statement {
-                            source_info: block_data.statements[i].source_info,
-                            kind: StatementKind::Validate(ValidationOp::Acquire,
-                                    vec![place_to_operand(dest_place)]),
-                        };
-                        block_data.statements.insert(i+1, acquire_stmt);
-
-                        // Release of the input
-                        let release_stmt = Statement {
-                            source_info: block_data.statements[i].source_info,
-                            kind: StatementKind::Validate(ValidationOp::Release,
-                                                            vec![place_to_operand(src_place)]),
-                        };
-                        block_data.statements.insert(i, release_stmt);
-                    }
-                    _ => {},
-                }
-            }
-        }
-    }
-}
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index edd15c39fed..c28bb0ca357 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -113,7 +113,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
             StatementKind::StorageLive(..) |
             StatementKind::StorageDead(..) |
             StatementKind::EndRegion(..) |
-            StatementKind::Validate(..) |
+            StatementKind::Retag { .. } |
             StatementKind::AscribeUserType(..) |
             StatementKind::Nop => {
                 // safe (at least as emitted during MIR construction)
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index c697391d867..6351a6b40cb 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -22,23 +22,19 @@ use transform::{MirPass, MirSource};
 
 struct EraseRegionsVisitor<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    in_validation_statement: bool,
 }
 
 impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
         EraseRegionsVisitor {
             tcx,
-            in_validation_statement: false,
         }
     }
 }
 
 impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
     fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) {
-        if !self.in_validation_statement {
-            *ty = self.tcx.erase_regions(ty);
-        }
+        *ty = self.tcx.erase_regions(ty);
         self.super_ty(ty);
     }
 
@@ -58,20 +54,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
                        block: BasicBlock,
                        statement: &mut Statement<'tcx>,
                        location: Location) {
-        // Do NOT delete EndRegion if validation statements are emitted.
-        // Validation needs EndRegion.
-        if self.tcx.sess.opts.debugging_opts.mir_emit_validate == 0 {
-            if let StatementKind::EndRegion(_) = statement.kind {
-                statement.kind = StatementKind::Nop;
-            }
+        if let StatementKind::EndRegion(_) = statement.kind {
+            statement.kind = StatementKind::Nop;
         }
 
-        self.in_validation_statement = match statement.kind {
-            StatementKind::Validate(..) => true,
-            _ => false,
-        };
         self.super_statement(block, statement, location);
-        self.in_validation_statement = false;
     }
 }
 
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 46c73c27fe1..92cfcb3fd56 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -23,7 +23,7 @@ use std::borrow::Cow;
 use syntax::ast;
 use syntax_pos::Span;
 
-pub mod add_validation;
+pub mod add_retag;
 pub mod add_moves_for_packed_drops;
 pub mod cleanup_post_borrowck;
 pub mod check_unsafety;
@@ -258,19 +258,21 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
         // Remove all `FakeRead` statements and the borrows that are only
         // used for checking matches
         &cleanup_post_borrowck::CleanFakeReadsAndBorrows,
+
         &simplify::SimplifyCfg::new("early-opt"),
 
         // These next passes must be executed together
         &add_call_guards::CriticalCallEdges,
         &elaborate_drops::ElaborateDrops,
         &no_landing_pads::NoLandingPads,
-        // AddValidation needs to run after ElaborateDrops and before EraseRegions, and it needs
-        // an AllCallEdges pass right before it.
-        &add_call_guards::AllCallEdges,
-        &add_validation::AddValidation,
         // AddMovesForPackedDrops needs to run after drop
         // elaboration.
         &add_moves_for_packed_drops::AddMovesForPackedDrops,
+        // AddRetag needs to run after ElaborateDrops, and it needs
+        // an AllCallEdges pass right before it.  Otherwise it should
+        // run fairly late, but before optimizations begin.
+        &add_call_guards::AllCallEdges,
+        &add_retag::AddRetag,
 
         &simplify::SimplifyCfg::new("elaborate-drops"),
 
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index a232176eacc..ca9c4eb9b8b 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -1167,7 +1167,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 StatementKind::StorageDead(_) |
                 StatementKind::InlineAsm {..} |
                 StatementKind::EndRegion(_) |
-                StatementKind::Validate(..) |
+                StatementKind::Retag { .. } |
                 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 6ab68789c02..1e193485950 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -241,7 +241,7 @@ fn check_statement(
         // These are all NOPs
         | StatementKind::StorageLive(_)
         | StatementKind::StorageDead(_)
-        | StatementKind::Validate(..)
+        | StatementKind::Retag { .. }
         | 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 4b4b284b02c..c1c127fa8d6 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -68,7 +68,7 @@ impl RemoveNoopLandingPads {
                 StatementKind::Assign(_, _) |
                 StatementKind::SetDiscriminant { .. } |
                 StatementKind::InlineAsm { .. } |
-                StatementKind::Validate { .. } => {
+                StatementKind::Retag { .. } => {
                     return false;
                 }
             }
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index 05044574e5c..a5a19f04b7e 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -162,7 +162,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             mir::StatementKind::StorageDead(_) |
             mir::StatementKind::InlineAsm { .. } |
             mir::StatementKind::EndRegion(_) |
-            mir::StatementKind::Validate(..) |
+            mir::StatementKind::Retag { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => continue,
             mir::StatementKind::SetDiscriminant{ .. } =>
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
index d16094e8238..12c13b8f815 100644
--- a/src/librustc_mir/util/liveness.rs
+++ b/src/librustc_mir/util/liveness.rs
@@ -204,7 +204,7 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>) -> Option<DefUse> {
         PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
         PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) |
         PlaceContext::NonUse(NonUseContext::AscribeUserTy) |
-        PlaceContext::NonUse(NonUseContext::Validate) =>
+        PlaceContext::MutatingUse(MutatingUseContext::Retag) =>
             Some(DefUse::Use),
 
         ///////////////////////////////////////////////////////////////////////////
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index 06c8545aacf..ecfe7d13782 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -84,7 +84,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
             StatementKind::Assign(..) => "StatementKind::Assign",
             StatementKind::FakeRead(..) => "StatementKind::FakeRead",
             StatementKind::EndRegion(..) => "StatementKind::EndRegion",
-            StatementKind::Validate(..) => "StatementKind::Validate",
+            StatementKind::Retag { .. } => "StatementKind::Retag",
             StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
             StatementKind::StorageLive(..) => "StatementKind::StorageLive",
             StatementKind::StorageDead(..) => "StatementKind::StorageDead",