diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs
index 661798a8250..1b7eb158567 100644
--- a/src/librustc/hir/map/blocks.rs
+++ b/src/librustc/hir/map/blocks.rs
@@ -192,6 +192,18 @@ impl<'a> FnLikeNode<'a> {
         }
     }
 
+    pub fn unsafety(self) -> ast::Unsafety {
+        match self.kind() {
+            FnKind::ItemFn(_, _, unsafety, ..) => {
+                unsafety
+            }
+            FnKind::Method(_, m, ..) => {
+                m.unsafety
+            }
+            _ => ast::Unsafety::Normal
+        }
+    }
+
     pub fn kind(self) -> FnKind<'a> {
         let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
             FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis, p.attrs)
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 7f1d1480d46..efe0504aa18 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -48,7 +48,7 @@ use rustc_data_structures::indexed_vec;
 use std::collections::BTreeMap;
 use std::fmt;
 
-/// HIR doesn't commit to a concrete storage type and have its own alias for a vector.
+/// HIR doesn't commit to a concrete storage type and has its own alias for a vector.
 /// It can be `Vec`, `P<[T]>` or potentially `Box<[T]>`, or some other container with similar
 /// behavior. Unlike AST, HIR is mostly a static structure, so we can use an owned slice instead
 /// of `Vec` to avoid keeping extra capacity.
@@ -75,14 +75,14 @@ pub mod pat_util;
 pub mod print;
 pub mod svh;
 
-/// A HirId uniquely identifies a node in the HIR of then current crate. It is
+/// A HirId uniquely identifies a node in the HIR of the current crate. It is
 /// composed of the `owner`, which is the DefIndex of the directly enclosing
 /// hir::Item, hir::TraitItem, or hir::ImplItem (i.e. the closest "item-like"),
 /// and the `local_id` which is unique within the given owner.
 ///
 /// This two-level structure makes for more stable values: One can move an item
 /// around within the source code, or add or remove stuff before it, without
-/// the local_id part of the HirId changing, which is a very useful property
+/// the local_id part of the HirId changing, which is a very useful property in
 /// incremental compilation where we have to persist things through changes to
 /// the code base.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug,
@@ -701,6 +701,16 @@ pub enum Mutability {
     MutImmutable,
 }
 
+impl Mutability {
+    /// Return MutMutable only if both arguments are mutable.
+    pub fn and(self, other: Self) -> Self {
+        match self {
+            MutMutable => other,
+            MutImmutable => MutImmutable,
+        }
+    }
+}
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 pub enum BinOp_ {
     /// The `+` operator (addition)
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 6dadb702b9f..c20864183f4 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -226,8 +226,12 @@ for mir::StatementKind<'tcx> {
             mir::StatementKind::StorageDead(ref lvalue) => {
                 lvalue.hash_stable(hcx, hasher);
             }
-            mir::StatementKind::EndRegion(ref extents) => {
-                extents.hash_stable(hcx, hasher);
+            mir::StatementKind::EndRegion(ref extent) => {
+                extent.hash_stable(hcx, hasher);
+            }
+            mir::StatementKind::Validate(ref op, ref lvalues) => {
+                op.hash_stable(hcx, hasher);
+                lvalues.hash_stable(hcx, hasher);
             }
             mir::StatementKind::Nop => {}
             mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
@@ -239,6 +243,23 @@ for mir::StatementKind<'tcx> {
     }
 }
 
+impl<'a, 'gcx, 'tcx, T> HashStable<StableHashingContext<'a, 'gcx, 'tcx>>
+    for mir::ValidationOperand<'tcx, T>
+    where T: HashStable<StableHashingContext<'a, 'gcx, 'tcx>>
+{
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
+                                          hasher: &mut StableHasher<W>)
+    {
+        self.lval.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(extent) });
+
 impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for mir::Lvalue<'tcx> {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 3dcd64af2ed..1e8dda0addf 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -25,7 +25,7 @@ use ty::{self, AdtDef, ClosureSubsts, Region, Ty};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use util::ppaux;
 use rustc_back::slice;
-use hir::InlineAsm;
+use hir::{self, InlineAsm};
 use std::ascii;
 use std::borrow::{Cow};
 use std::cell::Ref;
@@ -818,12 +818,18 @@ pub enum StatementKind<'tcx> {
     /// End the current live range for the storage of the local.
     StorageDead(Lvalue<'tcx>),
 
+    /// Execute a piece of inline Assembly.
     InlineAsm {
         asm: Box<InlineAsm>,
         outputs: Vec<Lvalue<'tcx>>,
         inputs: Vec<Operand<'tcx>>
     },
 
+    /// Assert the given lvalues 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, Lvalue<'tcx>>>),
+
     /// Mark one terminating point of an extent (i.e. static region).
     /// (The starting point(s) arise implicitly from borrows.)
     EndRegion(CodeExtent),
@@ -832,6 +838,57 @@ pub enum StatementKind<'tcx> {
     Nop,
 }
 
+/// 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 lvalue following the type and validate that all type
+    /// invariants are maintained.  Furthermore, acquire exclusive/read-only access to the
+    /// memory reachable from the lvalue.
+    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(CodeExtent),
+}
+
+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, RustcEncodable, RustcDecodable)]
+pub struct ValidationOperand<'tcx, T> {
+    pub lval: T,
+    pub ty: Ty<'tcx>,
+    pub re: Option<CodeExtent>,
+    pub mutbl: hir::Mutability,
+}
+
+impl<'tcx, T: Debug> Debug for ValidationOperand<'tcx, T> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+        write!(fmt, "{:?}: {:?}", self.lval, 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::*;
@@ -839,6 +896,7 @@ impl<'tcx> Debug for Statement<'tcx> {
             Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
             // (reuse lifetime rendering policy from ppaux.)
             EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
+            Validate(ref op, ref lvalues) => write!(fmt, "Validate({:?}, {:?})", op, lvalues),
             StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv),
             StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
             SetDiscriminant{lvalue: ref lv, variant_index: index} => {
@@ -1481,6 +1539,21 @@ impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Lvalue<'tcx>> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        ValidationOperand {
+            lval: self.lval.fold_with(folder),
+            ty: self.ty.fold_with(folder),
+            re: self.re,
+            mutbl: self.mutbl,
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.lval.visit_with(visitor) || self.ty.visit_with(visitor)
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         use mir::StatementKind::*;
@@ -1505,6 +1578,10 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
             // trait with a `fn fold_extent`.
             EndRegion(ref extent) => EndRegion(extent.clone()),
 
+            Validate(ref op, ref lvals) =>
+                Validate(op.clone(),
+                         lvals.iter().map(|operand| operand.fold_with(folder)).collect()),
+
             Nop => Nop,
         };
         Statement {
@@ -1530,6 +1607,9 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
             // trait with a `fn visit_extent`.
             EndRegion(ref _extent) => false,
 
+            Validate(ref _op, ref lvalues) =>
+                lvalues.iter().any(|ty_and_lvalue| ty_and_lvalue.visit_with(visitor)),
+
             Nop => false,
         }
     }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index a24b2ad0e43..5e860c63285 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -338,6 +338,13 @@ macro_rules! make_mir_visitor {
                         self.visit_assign(block, lvalue, rvalue, location);
                     }
                     StatementKind::EndRegion(_) => {}
+                    StatementKind::Validate(_, ref $($mutability)* lvalues) => {
+                        for operand in lvalues {
+                            self.visit_lvalue(& $($mutability)* operand.lval,
+                                              LvalueContext::Validate, location);
+                            self.visit_ty(& $($mutability)* operand.ty, Lookup::Loc(location));
+                        }
+                    }
                     StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
                         self.visit_lvalue(lvalue, LvalueContext::Store, location);
                     }
@@ -789,6 +796,9 @@ pub enum LvalueContext<'tcx> {
     // Starting and ending a storage live range
     StorageLive,
     StorageDead,
+
+    // Validation command
+    Validate,
 }
 
 impl<'tcx> LvalueContext<'tcx> {
@@ -835,7 +845,8 @@ impl<'tcx> LvalueContext<'tcx> {
             LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
             LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
             LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume |
-            LvalueContext::StorageLive | LvalueContext::StorageDead => false,
+            LvalueContext::StorageLive | LvalueContext::StorageDead |
+            LvalueContext::Validate => false,
         }
     }
 
@@ -847,7 +858,8 @@ impl<'tcx> LvalueContext<'tcx> {
             LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume => true,
             LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | LvalueContext::Store |
             LvalueContext::Call | LvalueContext::Projection(Mutability::Mut) |
-            LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead => false,
+            LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead |
+            LvalueContext::Validate => false,
         }
     }
 
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 4a9fbbe6f15..6995f099677 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1025,6 +1025,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "the directory the MIR is dumped into"),
     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)"),
     perf_stats: bool = (false, parse_bool, [UNTRACKED],
           "print some performance-related statistics"),
     hir_stats: bool = (false, parse_bool, [UNTRACKED],
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index ee9d30b58fe..12cb556afda 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -929,6 +929,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
     passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir);
     passes.push_pass(MIR_CONST, mir::transform::rustc_peek::SanityCheck);
 
+    // We compute "constant qualifications" betwen MIR_CONST and MIR_VALIDATED.
+
     // What we need to run borrowck etc.
     passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
     passes.push_pass(MIR_VALIDATED,
@@ -936,18 +938,23 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
     passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
     passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL);
 
-    // Optimizations begin.
-    passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
-    passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("no-landing-pads"));
+    // borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.
 
-    // From here on out, regions are gone.
-    passes.push_pass(MIR_OPTIMIZED, mir::transform::erase_regions::EraseRegions);
+    // These next passes must be executed together
+    passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::AddCallGuards);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::elaborate_drops::ElaborateDrops);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("elaborate-drops"));
-
     // No lifetime analysis based on borrowing can be done from here on out.
+
+    // AddValidation needs to run after ElaborateDrops and before EraseRegions.
+    passes.push_pass(MIR_OPTIMIZED, mir::transform::add_validation::AddValidation);
+
+    // From here on out, regions are gone.
+    passes.push_pass(MIR_OPTIMIZED, mir::transform::erase_regions::EraseRegions);
+
+    // Optimizations begin.
     passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::instcombine::InstCombine);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::deaggregator::Deaggregator);
diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs
index daafbecc5df..24d5aa9e46b 100644
--- a/src/librustc_mir/dataflow/drop_flag_effects.rs
+++ b/src/librustc_mir/dataflow/drop_flag_effects.rs
@@ -289,6 +289,7 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
             mir::StatementKind::StorageDead(_) |
             mir::StatementKind::InlineAsm { .. } |
             mir::StatementKind::EndRegion(_) |
+            mir::StatementKind::Validate(..) |
             mir::StatementKind::Nop => {}
         },
         None => {
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index 97c996dea68..d5bdc71a705 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -486,6 +486,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
             mir::StatementKind::StorageDead(_) |
             mir::StatementKind::InlineAsm { .. } |
             mir::StatementKind::EndRegion(_) |
+            mir::StatementKind::Validate(..) |
             mir::StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs
index fbf977b98f9..c2945d46592 100644
--- a/src/librustc_mir/dataflow/move_paths/mod.rs
+++ b/src/librustc_mir/dataflow/move_paths/mod.rs
@@ -416,6 +416,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
             }
             StatementKind::InlineAsm { .. } |
             StatementKind::EndRegion(_) |
+            StatementKind::Validate(..) |
             StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs
new file mode 100644
index 00000000000..52c2eaa7cb6
--- /dev/null
+++ b/src/librustc_mir/transform/add_validation.rs
@@ -0,0 +1,390 @@
+// 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::mir::transform::{MirPass, MirSource};
+use rustc::middle::region::CodeExtent;
+
+pub struct AddValidation;
+
+/// Determine the "context" of the lval: Mutability and region.
+fn lval_context<'a, 'tcx, D>(
+    lval: &Lvalue<'tcx>,
+    local_decls: &D,
+    tcx: TyCtxt<'a, 'tcx, 'tcx>
+) -> (Option<CodeExtent>, hir::Mutability)
+    where D: HasLocalDecls<'tcx>
+{
+    use rustc::mir::Lvalue::*;
+
+    match *lval {
+        Local { .. } => (None, hir::MutMutable),
+        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::TyRef(re, tam) => {
+                            let re = match re {
+                                &RegionKind::ReScope(ce) => Some(ce),
+                                &RegionKind::ReErased =>
+                                    bug!("AddValidation pass must be run before erasing lifetimes"),
+                                _ => None
+                            };
+                            (re, tam.mutbl)
+                        }
+                        ty::TyRawPtr(_) =>
+                            // 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::TyAdt(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 = lval_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)
+                    }
+
+                }
+                _ => lval_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::map::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 fn_like = match src {
+        MirSource::Fn(node_id) => {
+            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::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..}) => {
+                    if block_is_unsafe(&*block) {
+                        // Found an unsafe block, we can bail out here.
+                        return true;
+                    }
+                }
+                Node::NodeItem(..) => {
+                    // 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 local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone.
+
+        // Convert an lvalue to a validation operand.
+        let lval_to_operand = |lval: Lvalue<'tcx>| -> ValidationOperand<'tcx, Lvalue<'tcx>> {
+            let (re, mutbl) = lval_context(&lval, &local_decls, tcx);
+            let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
+            ValidationOperand { lval, 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: ARGUMENT_VISIBILITY_SCOPE,
+                span: mir.span, // FIXME: Consider using just the span covering the function
+                                // argument declaration.
+            };
+            // Gather all arguments, skip return value.
+            let operands = mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
+                    .map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect();
+            emit_acquire(&mut mir.basic_blocks_mut()[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, Lvalue<'tcx>, BasicBlock)> = Vec::new();
+        for block_data in mir.basic_blocks_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| lval_to_operand(dest.0.clone()))
+                                .chain(
+                                    args.iter().filter_map(|op| {
+                                        match op {
+                                            &Operand::Consume(ref lval) =>
+                                                Some(lval_to_operand(lval.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 lval, .. },
+                                  source_info }) |
+                Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. },
+                                  source_info }) => {
+                    // Before the call: Release all arguments
+                    if !restricted_validation {
+                        let release_stmt = Statement {
+                            source_info,
+                            kind: StatementKind::Validate(ValidationOp::Release,
+                                    vec![lval_to_operand(lval.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_lval, dest_block) in returns {
+            emit_acquire(
+                &mut mir.basic_blocks_mut()[dest_block],
+                source_info,
+                vec![lval_to_operand(dest_lval)]
+            );
+        }
+
+        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 mir.basic_blocks_mut() {
+            // 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(_, 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_lval, re, src_lval) = match block_data.statements[i].kind {
+                            StatementKind::Assign(ref dest_lval,
+                                                  Rvalue::Ref(re, _, ref src_lval)) => {
+                                (dest_lval.clone(), re, src_lval.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 dest_lval = Projection { base: dest_lval, elem: ProjectionElem::Deref };
+                        let dest_lval = Lvalue::Projection(Box::new(dest_lval));
+                        let acquire_stmt = Statement {
+                            source_info: block_data.statements[i].source_info,
+                            kind: StatementKind::Validate(ValidationOp::Acquire,
+                                    vec![lval_to_operand(dest_lval)]),
+                        };
+                        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![lval_to_operand(src_lval)]),
+                        };
+                        block_data.statements.insert(i, release_stmt);
+                    }
+                    // Casts can change what validation does (e.g. unsizing)
+                    StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Consume(_), _))
+                        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_lval, src_lval) = match block_data.statements[i].kind {
+                            StatementKind::Assign(ref dest_lval,
+                                    Rvalue::Cast(_, Operand::Consume(ref src_lval), _)) =>
+                            {
+                                (dest_lval.clone(), src_lval.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![lval_to_operand(dest_lval)]),
+                        };
+                        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![lval_to_operand(src_lval)]),
+                        };
+                        block_data.statements.insert(i, release_stmt);
+                    }
+                    _ => {},
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/clean_end_regions.rs b/src/librustc_mir/transform/clean_end_regions.rs
index 36125f94543..f06b88551d1 100644
--- a/src/librustc_mir/transform/clean_end_regions.rs
+++ b/src/librustc_mir/transform/clean_end_regions.rs
@@ -24,8 +24,8 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc::middle::region::CodeExtent;
 use rustc::mir::transform::{MirPass, MirSource};
 use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind};
-use rustc::mir::visit::{MutVisitor, Visitor};
-use rustc::ty::{RegionKind, TyCtxt};
+use rustc::mir::visit::{MutVisitor, Visitor, Lookup};
+use rustc::ty::{Ty, RegionKind, TyCtxt};
 
 pub struct CleanEndRegions;
 
@@ -42,7 +42,9 @@ impl MirPass for CleanEndRegions {
                           _tcx: TyCtxt<'a, 'tcx, 'tcx>,
                           _source: MirSource,
                           mir: &mut Mir<'tcx>) {
-        let mut gather = GatherBorrowedRegions { seen_regions: FxHashSet() };
+        let mut gather = GatherBorrowedRegions {
+            seen_regions: FxHashSet()
+        };
         gather.visit_mir(mir);
 
         let mut delete = DeleteTrivialEndRegions { seen_regions: &mut gather.seen_regions };
@@ -54,6 +56,7 @@ impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
     fn visit_rvalue(&mut self,
                     rvalue: &Rvalue<'tcx>,
                     location: Location) {
+        // Gather regions that are used for borrows
         if let Rvalue::Ref(r, _, _) = *rvalue {
             if let RegionKind::ReScope(ce) = *r {
                 self.seen_regions.insert(ce);
@@ -61,6 +64,17 @@ impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
         }
         self.super_rvalue(rvalue, location);
     }
+
+    fn visit_ty(&mut self, ty: &Ty<'tcx>, _: Lookup) {
+        // Gather regions that occur in types
+        for re in ty.walk().flat_map(|t| t.regions()) {
+            match *re {
+                RegionKind::ReScope(ce) => { self.seen_regions.insert(ce); }
+                _ => {},
+            }
+        }
+        self.super_ty(ty);
+    }
 }
 
 impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> {
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index da9032685e0..baf0522896c 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -11,6 +11,8 @@
 //! This pass erases all early-bound regions from the types occuring in the MIR.
 //! We want to do this once just before trans, so trans does not have to take
 //! care erasing regions all over the place.
+//! NOTE:  We do NOT erase regions of statements that are relevant for
+//! "types-as-contracts"-validation, namely, AcquireValid, ReleaseValid, and EndRegion.
 
 use rustc::ty::subst::Substs;
 use rustc::ty::{Ty, TyCtxt, ClosureSubsts};
@@ -20,20 +22,24 @@ use rustc::mir::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: tcx
+            tcx: tcx,
+            in_validation_statement: false,
         }
     }
 }
 
 impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
     fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) {
-        let old_ty = *ty;
-        *ty = self.tcx.erase_regions(&old_ty);
+        if !self.in_validation_statement {
+            *ty = self.tcx.erase_regions(&{*ty});
+        }
+        self.super_ty(ty);
     }
 
     fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, _: Location) {
@@ -71,10 +77,20 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
                        block: BasicBlock,
                        statement: &mut Statement<'tcx>,
                        location: Location) {
-        if let StatementKind::EndRegion(_) = statement.kind {
-            statement.kind = StatementKind::Nop;
+        // 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;
+            }
         }
+
+        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 c9c8ad0e0eb..a247ce2231e 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -24,6 +24,7 @@ use syntax::ast;
 use syntax_pos::{DUMMY_SP, Span};
 use transform;
 
+pub mod add_validation;
 pub mod clean_end_regions;
 pub mod simplify_branches;
 pub mod simplify;
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 9bb0f07aa68..9d01f8294e4 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -908,6 +908,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 StatementKind::StorageDead(_) |
                 StatementKind::InlineAsm {..} |
                 StatementKind::EndRegion(_) |
+                StatementKind::Validate(..) |
                 StatementKind::Nop => {}
             }
         });
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index 5918de0c688..268e7a4c185 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -161,6 +161,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::Nop => continue,
             mir::StatementKind::SetDiscriminant{ .. } =>
                 span_bug!(stmt.source_info.span,
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 7e6fccf3019..1c7899a46d1 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -414,6 +414,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             }
             StatementKind::InlineAsm { .. } |
             StatementKind::EndRegion(_) |
+            StatementKind::Validate(..) |
             StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index 9895802700e..d5e477ff0c7 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -126,6 +126,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
         self.record(match statement.kind {
             StatementKind::Assign(..) => "StatementKind::Assign",
             StatementKind::EndRegion(..) => "StatementKind::EndRegion",
+            StatementKind::Validate(..) => "StatementKind::Validate",
             StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
             StatementKind::StorageLive(..) => "StatementKind::StorageLive",
             StatementKind::StorageDead(..) => "StatementKind::StorageDead",
diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs
index 45afcf51b52..598af1cda91 100644
--- a/src/librustc_trans/mir/analyze.rs
+++ b/src/librustc_trans/mir/analyze.rs
@@ -158,6 +158,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
 
                 LvalueContext::StorageLive |
                 LvalueContext::StorageDead |
+                LvalueContext::Validate |
                 LvalueContext::Inspect |
                 LvalueContext::Consume => {}
 
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 393fa9c0c8e..cbe0e259b4a 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -293,6 +293,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                     }
                     mir::StatementKind::StorageLive(_) |
                     mir::StatementKind::StorageDead(_) |
+                    mir::StatementKind::Validate(..) |
                     mir::StatementKind::EndRegion(_) |
                     mir::StatementKind::Nop => {}
                     mir::StatementKind::InlineAsm { .. } |
diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs
index 170a76a4949..52dfc8dc4de 100644
--- a/src/librustc_trans/mir/statement.rs
+++ b/src/librustc_trans/mir/statement.rs
@@ -87,6 +87,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                 bcx
             }
             mir::StatementKind::EndRegion(_) |
+            mir::StatementKind::Validate(..) |
             mir::StatementKind::Nop => bcx,
         }
     }
diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md
index 28a124e3c61..d999ff97551 100644
--- a/src/test/mir-opt/README.md
+++ b/src/test/mir-opt/README.md
@@ -57,13 +57,6 @@ the lines being too long.
 
 compiletest handles dumping the MIR before and after every pass for you.  The
 test writer only has to specify the file names of the dumped files (not the
-full path to the file) and what lines to expect.  I added an option to rustc
+full path to the file) and what lines to expect.  There is an option to rustc
 that tells it to dump the mir into some directly (rather then always dumping to
-the current directory).  
-
-Lines match ignoring whitespace, and the prefix "//" is removed of course.
-
-It also currently strips trailing comments -- partly because the full file path
-in "scope comments" is unpredictable and partly because tidy complains about
-the lines being too long.
-
+the current directory).
diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs
new file mode 100644
index 00000000000..9ac76a5f4ea
--- /dev/null
+++ b/src/test/mir-opt/validate_1.rs
@@ -0,0 +1,59 @@
+// Copyright 2017 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.
+
+// ignore-tidy-linelength
+// compile-flags: -Z verbose -Z mir-emit-validate=1
+
+struct Test(i32);
+
+impl Test {
+    // Make sure we run the pass on a method, not just on bare functions.
+    fn foo(&self, _x: &mut i32) {}
+}
+
+fn main() {
+    let mut x = 0;
+    Test(0).foo(&mut x);
+
+    // Also test closures
+    let c = |x: &mut i32| { let y = &*x; *y };
+    c(&mut x);
+}
+
+// FIXME: Also test code generated inside the closure, make sure it has validation.  Unfortunately,
+// the interesting lines of code also contain name of the source file, so we cannot test for it.
+
+// END RUST SOURCE
+// START rustc.node12.EraseRegions.after.mir
+//     bb0: {
+//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(5) => validate_1/8cd878b::{{impl}}[0]::foo[0] }, BrAnon(0)) Test, _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(5) => validate_1/8cd878b::{{impl}}[0]::foo[0] }, BrAnon(1)) mut i32]);
+//         return;
+//     }
+// END rustc.node12.EraseRegions.after.mir
+// START rustc.node23.EraseRegions.after.mir
+// fn main() -> () {
+//     bb0: {
+//         Validate(Suspend(ReScope(Misc(NodeId(34)))), [_1: i32]);
+//         _6 = &ReErased mut _1;
+//         Validate(Acquire, [(*_6): i32/ReScope(Misc(NodeId(34)))]);
+//         Validate(Suspend(ReScope(Misc(NodeId(34)))), [(*_6): i32/ReScope(Misc(NodeId(34)))]);
+//         _5 = &ReErased mut (*_6);
+//         Validate(Acquire, [(*_5): i32/ReScope(Misc(NodeId(34)))]);
+//         Validate(Release, [_2: (), _3: &ReScope(Misc(NodeId(34))) Test, _5: &ReScope(Misc(NodeId(34))) mut i32]);
+//         _2 = const Test::foo(_3, _5) -> bb1;
+//     }
+//
+//     bb1: {
+//         Validate(Acquire, [_2: ()]);
+//         EndRegion(ReScope(Misc(NodeId(34))));
+//         return;
+//     }
+// }
+// END rustc.node23.EraseRegions.after.mir
diff --git a/src/test/mir-opt/validate_2.rs b/src/test/mir-opt/validate_2.rs
new file mode 100644
index 00000000000..37ebd720d52
--- /dev/null
+++ b/src/test/mir-opt/validate_2.rs
@@ -0,0 +1,27 @@
+// Copyright 2017 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.
+
+// ignore-tidy-linelength
+// compile-flags: -Z verbose -Z mir-emit-validate=1
+
+fn main() {
+    let _x : Box<[i32]> = Box::new([1, 2, 3]);
+}
+
+// END RUST SOURCE
+// START rustc.node4.EraseRegions.after.mir
+// fn main() -> () {
+//     bb1: {
+//         Validate(Release, [_2: std::boxed::Box<[i32; 3]>]);
+//         _1 = _2 as std::boxed::Box<[i32]> (Unsize);
+//         Validate(Acquire, [_1: std::boxed::Box<[i32]>]);
+//     }
+// }
+// END rustc.node4.EraseRegions.after.mir
diff --git a/src/test/mir-opt/validate_3.rs b/src/test/mir-opt/validate_3.rs
new file mode 100644
index 00000000000..9140cf5768f
--- /dev/null
+++ b/src/test/mir-opt/validate_3.rs
@@ -0,0 +1,50 @@
+// Copyright 2017 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.
+
+// ignore-tidy-linelength
+// compile-flags: -Z verbose -Z mir-emit-validate=1
+
+struct Test {
+    x: i32
+}
+
+fn foo(_x: &i32) {}
+
+fn main() {
+    // These internal unsafe functions should have no effect on the code generation.
+    unsafe fn _unused1() {}
+    fn _unused2(x: *const i32) -> i32 { unsafe { *x }}
+
+    let t = Test { x: 0 };
+    let t = &t;
+    foo(&t.x);
+}
+
+// END RUST SOURCE
+// START rustc.node16.EraseRegions.after.mir
+// fn main() -> () {
+//     let mut _5: &ReErased i32;
+//     bb0: {
+//         Validate(Suspend(ReScope(Misc(NodeId(46)))), [((*_2).0: i32): i32/ReScope(Remainder(BlockRemainder { block: NodeId(18), first_statement_index: 3 })) (imm)]);
+//         _5 = &ReErased ((*_2).0: i32);
+//         Validate(Acquire, [(*_5): i32/ReScope(Misc(NodeId(46))) (imm)]);
+//         Validate(Suspend(ReScope(Misc(NodeId(46)))), [(*_5): i32/ReScope(Misc(NodeId(46))) (imm)]);
+//         _4 = &ReErased (*_5);
+//         Validate(Acquire, [(*_4): i32/ReScope(Misc(NodeId(46))) (imm)]);
+//         Validate(Release, [_3: (), _4: &ReScope(Misc(NodeId(46))) i32]);
+//         _3 = const foo(_4) -> bb1;
+//     }
+//     bb1: {
+//         EndRegion(ReScope(Misc(NodeId(46))));
+//         EndRegion(ReScope(Remainder(BlockRemainder { block: NodeId(18), first_statement_index: 3 })));
+//         return;
+//     }
+// }
+// END rustc.node16.EraseRegions.after.mir
diff --git a/src/test/mir-opt/validate_4.rs b/src/test/mir-opt/validate_4.rs
new file mode 100644
index 00000000000..591de975740
--- /dev/null
+++ b/src/test/mir-opt/validate_4.rs
@@ -0,0 +1,60 @@
+// Copyright 2017 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.
+
+// ignore-tidy-linelength
+// compile-flags: -Z verbose -Z mir-emit-validate=1
+
+// Make sure unsafe fns and fns with an unsafe block only get restricted validation.
+
+unsafe fn write_42(x: *mut i32) -> bool {
+    let test_closure = |x: *mut i32| *x = 23;
+    test_closure(x);
+    *x = 42;
+    true
+}
+
+fn test(x: &mut i32) {
+    unsafe { write_42(x) };
+}
+
+fn main() {
+    test(&mut 0);
+
+    let test_closure = unsafe { |x: &mut i32| write_42(x) };
+    test_closure(&mut 0);
+}
+
+// FIXME: Also test code generated inside the closure, make sure it only does restricted validation
+// because it is entirely inside an unsafe block.  Unfortunately, the interesting lines of code also
+// contain name of the source file, so we cannot test for it.
+
+// END RUST SOURCE
+// START rustc.node4.EraseRegions.after.mir
+// fn write_42(_1: *mut i32) -> bool {
+//     bb0: {
+//         Validate(Acquire, [_1: *mut i32]);
+//         Validate(Release, [_1: *mut i32]);
+//         return;
+//     }
+// }
+// END rustc.node4.EraseRegions.after.mir
+// START rustc.node31.EraseRegions.after.mir
+// fn test(_1: &ReErased mut i32) -> () {
+//     bb0: {
+//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]);
+//         Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]);
+//         _3 = const write_42(_4) -> bb1;
+//     }
+//     bb1: {
+//         Validate(Acquire, [_3: bool]);
+//         Validate(Release, [_3: bool]);
+//     }
+// }
+// END rustc.node31.EraseRegions.after.mir
diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs
new file mode 100644
index 00000000000..e9919af9fd3
--- /dev/null
+++ b/src/test/mir-opt/validate_5.rs
@@ -0,0 +1,44 @@
+// Copyright 2017 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.
+
+// ignore-tidy-linelength
+// compile-flags: -Z verbose -Z mir-emit-validate=2
+
+// Make sure unsafe fns and fns with an unsafe block only get full validation.
+
+unsafe fn write_42(x: *mut i32) -> bool {
+    *x = 42;
+    true
+}
+
+fn test(x: &mut i32) {
+    unsafe { write_42(x) };
+}
+
+fn main() {
+    test(&mut 0);
+
+    let test_closure = unsafe { |x: &mut i32| write_42(x) };
+    test_closure(&mut 0);
+}
+
+// FIXME: Also test code generated inside the closure, make sure it has validation.  Unfortunately,
+// the interesting lines of code also contain name of the source file, so we cannot test for it.
+
+// END RUST SOURCE
+// START rustc.node17.EraseRegions.after.mir
+// fn test(_1: &ReErased mut i32) -> () {
+//     bb0: {
+//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(4) => validate_5/8cd878b::test[0] }, BrAnon(0)) mut i32]);
+//         Validate(Release, [_3: bool, _4: *mut i32]);
+//         _3 = const write_42(_4) -> bb1;
+//     }
+// }
+// END rustc.node17.EraseRegions.after.mir