about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-06-19 13:01:27 +0000
committerbors <bors@rust-lang.org>2017-06-19 13:01:27 +0000
commit04145943a25c3b8c7e7d7fe8c2efb04f259c25fb (patch)
tree1c3a1dcc8f0cb6181c6bbaa8e7e6d2903be99e83 /src
parent5ce5126199d0edf278ef0d9bb60534770d96f3f6 (diff)
parent11f4968bd7372ed3986ff7d83fb14218ef0f2f20 (diff)
downloadrust-04145943a25c3b8c7e7d7fe8c2efb04f259c25fb.tar.gz
rust-04145943a25c3b8c7e7d7fe8c2efb04f259c25fb.zip
Auto merge of #39409 - pnkfelix:mir-borrowck2, r=nikomatsakis
MIR EndRegion Statements (was MIR dataflow for Borrows)

This PR adds an `EndRegion` statement to MIR (where the `EndRegion` statement is what terminates a borrow).

An earlier version of the PR implemented a dataflow analysis on borrow expressions, but I am now factoring that into a follow-up PR so that reviewing this one is easier. (And also because there are some revisions I want to make to that dataflow code, but I want this PR to get out of WIP status...)

This is a baby step towards MIR borrowck. I just want to get the review process going while I independently work on the remaining steps.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ich/impls_mir.rs3
-rw-r--r--src/librustc/mir/mod.rs41
-rw-r--r--src/librustc/mir/visit.rs1
-rw-r--r--src/librustc/session/config.rs4
-rw-r--r--src/librustc/util/ppaux.rs29
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/impls.rs1
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs1
-rw-r--r--src/librustc_borrowck/borrowck/mir/elaborate_drops.rs5
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs1
-rw-r--r--src/librustc_borrowck/borrowck/mir/mod.rs1
-rw-r--r--src/librustc_driver/driver.rs3
-rw-r--r--src/librustc_mir/build/block.rs59
-rw-r--r--src/librustc_mir/build/cfg.rs11
-rw-r--r--src/librustc_mir/build/expr/as_lvalue.rs2
-rw-r--r--src/librustc_mir/build/expr/as_operand.rs2
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs3
-rw-r--r--src/librustc_mir/build/expr/as_temp.rs6
-rw-r--r--src/librustc_mir/build/expr/into.rs3
-rw-r--r--src/librustc_mir/build/expr/stmt.rs8
-rw-r--r--src/librustc_mir/build/matches/test.rs2
-rw-r--r--src/librustc_mir/build/mod.rs5
-rw-r--r--src/librustc_mir/build/scope.rs86
-rw-r--r--src/librustc_mir/hair/cx/block.rs11
-rw-r--r--src/librustc_mir/hair/mod.rs2
-rw-r--r--src/librustc_mir/transform/clean_end_regions.rs84
-rw-r--r--src/librustc_mir/transform/erase_regions.rs9
-rw-r--r--src/librustc_mir/transform/mod.rs1
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs1
-rw-r--r--src/librustc_mir/transform/type_check.rs1
-rw-r--r--src/librustc_mir/util/patch.rs1
-rw-r--r--src/librustc_passes/mir_stats.rs1
-rw-r--r--src/librustc_trans/mir/constant.rs1
-rw-r--r--src/librustc_trans/mir/statement.rs1
-rw-r--r--src/test/codegen/drop.rs2
-rw-r--r--src/test/mir-opt/README.md27
-rw-r--r--src/test/mir-opt/basic_assignment.rs22
-rw-r--r--src/test/mir-opt/end_region_1.rs38
-rw-r--r--src/test/mir-opt/end_region_2.rs66
-rw-r--r--src/test/mir-opt/end_region_3.rs69
-rw-r--r--src/test/mir-opt/end_region_4.rs75
-rw-r--r--src/test/mir-opt/end_region_5.rs80
-rw-r--r--src/test/mir-opt/end_region_6.rs83
-rw-r--r--src/test/mir-opt/end_region_7.rs97
-rw-r--r--src/test/mir-opt/end_region_8.rs86
-rw-r--r--src/test/mir-opt/end_region_9.rs85
45 files changed, 1047 insertions, 73 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index ae2bea3027d..cb017b7f886 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -226,6 +226,9 @@ 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::Nop => {}
             mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
                 asm.hash_stable(hcx, hasher);
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 80c42917196..c8d03e7b305 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -12,6 +12,7 @@
 
 use graphviz::IntoCow;
 use middle::const_val::ConstVal;
+use middle::region::CodeExtent;
 use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
@@ -804,6 +805,10 @@ pub enum StatementKind<'tcx> {
         inputs: Vec<Operand<'tcx>>
     },
 
+    /// Mark one terminating point of an extent (i.e. static region).
+    /// (The starting point(s) arise implicitly from borrows.)
+    EndRegion(CodeExtent),
+
     /// No-op. Useful for deleting instructions without affecting statement indices.
     Nop,
 }
@@ -813,6 +818,8 @@ impl<'tcx> Debug for Statement<'tcx> {
         use self::StatementKind::*;
         match self.kind {
             Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
+            // (reuse lifetime rendering policy from ppaux.)
+            EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
             StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv),
             StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
             SetDiscriminant{lvalue: ref lv, variant_index: index} => {
@@ -1176,12 +1183,22 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
             Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval),
             NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t),
-            Ref(_, borrow_kind, ref lv) => {
+            Ref(region, borrow_kind, ref lv) => {
                 let kind_str = match borrow_kind {
                     BorrowKind::Shared => "",
                     BorrowKind::Mut | BorrowKind::Unique => "mut ",
                 };
-                write!(fmt, "&{}{:?}", kind_str, lv)
+
+                // When identifying regions, add trailing space if
+                // necessary.
+                let region = if ppaux::identify_regions() {
+                    let mut region = format!("{}", region);
+                    if region.len() > 0 { region.push(' '); }
+                    region
+                } else {
+                    "".to_owned()
+                };
+                write!(fmt, "&{}{}{:?}", region, kind_str, lv)
             }
 
             Aggregate(ref kind, ref lvs) => {
@@ -1224,7 +1241,11 @@ impl<'tcx> Debug for Rvalue<'tcx> {
 
                     AggregateKind::Closure(def_id, _) => ty::tls::with(|tcx| {
                         if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
-                            let name = format!("[closure@{:?}]", tcx.hir.span(node_id));
+                            let name = if tcx.sess.opts.debugging_opts.span_free_formats {
+                                format!("[closure@{:?}]", node_id)
+                            } else {
+                                format!("[closure@{:?}]", tcx.hir.span(node_id))
+                            };
                             let mut struct_fmt = fmt.debug_struct(&name);
 
                             tcx.with_freevars(node_id, |freevars| {
@@ -1458,6 +1479,13 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
                 outputs: outputs.fold_with(folder),
                 inputs: inputs.fold_with(folder)
             },
+
+            // Note for future: If we want to expose the extents
+            // during the fold, we need to either generalize EndRegion
+            // to carry `[ty::Region]`, or extend the `TypeFolder`
+            // trait with a `fn fold_extent`.
+            EndRegion(ref extent) => EndRegion(extent.clone()),
+
             Nop => Nop,
         };
         Statement {
@@ -1476,6 +1504,13 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
             StorageDead(ref lvalue) => lvalue.visit_with(visitor),
             InlineAsm { ref outputs, ref inputs, .. } =>
                 outputs.visit_with(visitor) || inputs.visit_with(visitor),
+
+            // Note for future: If we want to expose the extents
+            // during the visit, we need to either generalize EndRegion
+            // to carry `[ty::Region]`, or extend the `TypeVisitor`
+            // trait with a `fn visit_extent`.
+            EndRegion(ref _extent) => false,
+
             Nop => false,
         }
     }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 780ce736bfd..ac1c0306f70 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -325,6 +325,7 @@ macro_rules! make_mir_visitor {
                                           ref $($mutability)* rvalue) => {
                         self.visit_assign(block, lvalue, rvalue, location);
                     }
+                    StatementKind::EndRegion(_) => {}
                     StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
                         self.visit_lvalue(lvalue, LvalueContext::Store, location);
                     }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 4d6579cb204..7b4394f2ac9 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -893,6 +893,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
          DB_OPTIONS, db_type_desc, dbsetters,
     verbose: bool = (false, parse_bool, [UNTRACKED],
         "in general, enable more debug printouts"),
+    span_free_formats: bool = (false, parse_bool, [UNTRACKED],
+        "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path
+    identify_regions: bool = (false, parse_bool, [UNTRACKED],
+        "make unnamed regions display as '# (where # is some non-ident unique id)"),
     time_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each rustc pass"),
     count_llvm_insns: bool = (false, parse_bool,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 15fbeb5108f..1fa63577196 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -8,8 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use hir::BodyId;
 use hir::def_id::DefId;
 use hir::map::definitions::DefPathData;
+use middle::region::{CodeExtent, BlockRemainder};
 use ty::subst::{self, Subst};
 use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
 use ty::{TyBool, TyChar, TyAdt};
@@ -32,6 +34,10 @@ pub fn verbose() -> bool {
     ty::tls::with(|tcx| tcx.sess.verbose())
 }
 
+pub fn identify_regions() -> bool {
+    ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.identify_regions)
+}
+
 fn fn_sig(f: &mut fmt::Formatter,
           inputs: &[Ty],
           variadic: bool,
@@ -519,6 +525,23 @@ impl fmt::Display for ty::RegionKind {
             ty::ReSkolemized(_, br) => {
                 write!(f, "{}", br)
             }
+            ty::ReScope(code_extent) if identify_regions() => {
+                match code_extent {
+                    CodeExtent::Misc(node_id) =>
+                        write!(f, "'{}mce", node_id.as_u32()),
+                    CodeExtent::CallSiteScope(BodyId { node_id }) =>
+                        write!(f, "'{}cce", node_id.as_u32()),
+                    CodeExtent::ParameterScope(BodyId { node_id }) =>
+                        write!(f, "'{}pce", node_id.as_u32()),
+                    CodeExtent::DestructionScope(node_id) =>
+                        write!(f, "'{}dce", node_id.as_u32()),
+                    CodeExtent::Remainder(BlockRemainder { block, first_statement_index }) =>
+                        write!(f, "'{}_{}rce", block, first_statement_index),
+                }
+            }
+            ty::ReVar(region_vid) if identify_regions() => {
+                write!(f, "'{}rv", region_vid.index)
+            }
             ty::ReScope(_) |
             ty::ReVar(_) |
             ty::ReErased => Ok(()),
@@ -789,7 +812,11 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                 write!(f, "[closure")?;
 
                 if let Some(node_id) = tcx.hir.as_local_node_id(did) {
-                    write!(f, "@{:?}", tcx.hir.span(node_id))?;
+                    if tcx.sess.opts.debugging_opts.span_free_formats {
+                        write!(f, "@{:?}", node_id)?;
+                    } else {
+                        write!(f, "@{:?}", tcx.hir.span(node_id))?;
+                    }
                     let mut sep = " ";
                     tcx.with_freevars(node_id, |freevars| {
                         for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) {
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
index da8aa231ccf..1a1ac7f9c74 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
@@ -474,6 +474,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
             mir::StatementKind::StorageLive(_) |
             mir::StatementKind::StorageDead(_) |
             mir::StatementKind::InlineAsm { .. } |
+            mir::StatementKind::EndRegion(_) |
             mir::StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
index 44e3b38ea38..2c55460fb30 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
@@ -105,6 +105,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             mir::StatementKind::StorageLive(_) |
             mir::StatementKind::StorageDead(_) |
             mir::StatementKind::InlineAsm { .. } |
+            mir::StatementKind::EndRegion(_) |
             mir::StatementKind::Nop => continue,
             mir::StatementKind::SetDiscriminant{ .. } =>
                 span_bug!(stmt.source_info.span,
diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
index b03d34819f6..83369772608 100644
--- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
+++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
@@ -594,6 +594,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                             assert!(self.patch.is_patched(bb));
                             allow_initializations = false;
                         }
+                        TerminatorKind::Resume => {
+                            // It is possible for `Resume` to be patched
+                            // (in particular it can be patched to be replaced with
+                            // a Goto; see `MirPatch::new`).
+                        }
                         _ => {
                             assert!(!self.patch.is_patched(bb));
                         }
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index b03d2a775df..a0ecdcc8e2f 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -413,6 +413,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
                           "SetDiscriminant should not exist during borrowck");
             }
             StatementKind::InlineAsm { .. } |
+            StatementKind::EndRegion(_) |
             StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs
index 2b39d2a256e..e3b99b9d4bd 100644
--- a/src/librustc_borrowck/borrowck/mir/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/mod.rs
@@ -394,6 +394,7 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
             mir::StatementKind::StorageLive(_) |
             mir::StatementKind::StorageDead(_) |
             mir::StatementKind::InlineAsm { .. } |
+            mir::StatementKind::EndRegion(_) |
             mir::StatementKind::Nop => {}
         },
         None => {
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 792be8bc6df..159fee6aa4c 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -914,6 +914,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
     let mut passes = Passes::new();
     passes.push_hook(mir::transform::dump_mir::DumpMir);
 
+    // Remove all `EndRegion` statements that are not involved in borrows.
+    passes.push_pass(MIR_CONST, mir::transform::clean_end_regions::CleanEndRegions);
+
     // What we need to do constant evaluation.
     passes.push_pass(MIR_CONST, mir::transform::simplify::SimplifyCfg::new("initial"));
     passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir);
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index d81de954dbf..865174aa272 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -21,21 +21,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                      ast_block: &'tcx hir::Block,
                      source_info: SourceInfo)
                      -> BlockAnd<()> {
-        let Block { extent, span, stmts, expr, targeted_by_break } = self.hir.mirror(ast_block);
-        self.in_scope(extent, block, move |this| {
-            if targeted_by_break {
-                // This is a `break`-able block (currently only `catch { ... }`)
-                let exit_block = this.cfg.start_new_block();
-                let block_exit = this.in_breakable_scope(None, exit_block,
-                                                         destination.clone(), |this| {
+        let Block { extent, opt_destruction_extent, span, stmts, expr, targeted_by_break } =
+            self.hir.mirror(ast_block);
+        self.in_opt_scope(opt_destruction_extent.map(|de|(de, source_info)), block, move |this| {
+            this.in_scope((extent, source_info), block, move |this| {
+                if targeted_by_break {
+                    // This is a `break`-able block (currently only `catch { ... }`)
+                    let exit_block = this.cfg.start_new_block();
+                    let block_exit = this.in_breakable_scope(
+                        None, exit_block, destination.clone(), |this| {
+                            this.ast_block_stmts(destination, block, span, stmts, expr)
+                        });
+                    this.cfg.terminate(unpack!(block_exit), source_info,
+                                       TerminatorKind::Goto { target: exit_block });
+                    exit_block.unit()
+                } else {
                     this.ast_block_stmts(destination, block, span, stmts, expr)
-                });
-                this.cfg.terminate(unpack!(block_exit), source_info,
-                                   TerminatorKind::Goto { target: exit_block });
-                exit_block.unit()
-            } else {
-                this.ast_block_stmts(destination, block, span, stmts, expr)
-            }
+                }
+            })
         })
     }
 
@@ -66,14 +69,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // First we build all the statements in the block.
         let mut let_extent_stack = Vec::with_capacity(8);
         let outer_visibility_scope = this.visibility_scope;
+        let source_info = this.source_info(span);
         for stmt in stmts {
-            let Stmt { span: _, kind } = this.hir.mirror(stmt);
+            let Stmt { span, kind, opt_destruction_extent } = this.hir.mirror(stmt);
             match kind {
                 StmtKind::Expr { scope, expr } => {
-                    unpack!(block = this.in_scope(scope, block, |this| {
-                        let expr = this.hir.mirror(expr);
-                        this.stmt_expr(block, expr)
-                    }));
+                    unpack!(block = this.in_opt_scope(
+                        opt_destruction_extent.map(|de|(de, source_info)), block, |this| {
+                            this.in_scope((scope, source_info), block, |this| {
+                                let expr = this.hir.mirror(expr);
+                                this.stmt_expr(block, expr)
+                            })
+                        }));
                 }
                 StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
                     let tcx = this.hir.tcx();
@@ -89,10 +96,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                     // Evaluate the initializer, if present.
                     if let Some(init) = initializer {
-                        unpack!(block = this.in_scope(init_scope, block, move |this| {
-                            // FIXME #30046                              ^~~~
-                            this.expr_into_pattern(block, pattern, init)
-                        }));
+                        unpack!(block = this.in_opt_scope(
+                            opt_destruction_extent.map(|de|(de, source_info)), block, move |this| {
+                                this.in_scope((init_scope, source_info), block, move |this| {
+                                    // FIXME #30046                             ^~~~
+                                    this.expr_into_pattern(block, pattern, init)
+                                })
+                            }));
                     } else {
                         this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
                             this.storage_live_binding(block, node, span);
@@ -112,13 +122,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         if let Some(expr) = expr {
             unpack!(block = this.into(destination, block, expr));
         } else {
-            let source_info = this.source_info(span);
             this.cfg.push_assign_unit(block, source_info, destination);
         }
         // Finally, we pop all the let scopes before exiting out from the scope of block
         // itself.
         for extent in let_extent_stack.into_iter().rev() {
-            unpack!(block = this.pop_scope(extent, block));
+            unpack!(block = this.pop_scope((extent, source_info), block));
         }
         // Restore the original visibility scope.
         this.visibility_scope = outer_visibility_scope;
diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs
index 40a78933aad..c20f8bde783 100644
--- a/src/librustc_mir/build/cfg.rs
+++ b/src/librustc_mir/build/cfg.rs
@@ -14,6 +14,7 @@
 //! Routines for manipulating the control-flow graph.
 
 use build::CFG;
+use rustc::middle::region::CodeExtent;
 use rustc::mir::*;
 
 impl<'tcx> CFG<'tcx> {
@@ -43,6 +44,16 @@ impl<'tcx> CFG<'tcx> {
         self.block_data_mut(block).statements.push(statement);
     }
 
+    pub fn push_end_region(&mut self,
+                           block: BasicBlock,
+                           source_info: SourceInfo,
+                           extent: CodeExtent) {
+        self.push(block, Statement {
+            source_info: source_info,
+            kind: StatementKind::EndRegion(extent),
+        });
+    }
+
     pub fn push_assign(&mut self,
                        block: BasicBlock,
                        source_info: SourceInfo,
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index df2841a6682..04c23215463 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -40,7 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let source_info = this.source_info(expr_span);
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this| this.as_lvalue(block, value))
+                this.in_scope((extent, source_info), block, |this| this.as_lvalue(block, value))
             }
             ExprKind::Field { lhs, name } => {
                 let lvalue = unpack!(block = this.as_lvalue(block, lhs));
diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs
index 5178963179d..4679e0bb0a5 100644
--- a/src/librustc_mir/build/expr/as_operand.rs
+++ b/src/librustc_mir/build/expr/as_operand.rs
@@ -56,6 +56,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let this = self;
 
         if let ExprKind::Scope { extent, value } = expr.kind {
+            let source_info = this.source_info(expr.span);
+            let extent = (extent, source_info);
             return this.in_scope(extent, block, |this| {
                 this.as_operand(block, scope, value)
             });
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 2884b60fdd8..2512291f1a4 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -59,6 +59,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
+                let extent = (extent, source_info);
                 this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value))
             }
             ExprKind::Repeat { value, count } => {
@@ -99,7 +100,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // to start, malloc some memory of suitable type (thus far, uninitialized):
                 let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
                 this.cfg.push_assign(block, source_info, &result, box_);
-                this.in_scope(value_extents, block, |this| {
+                this.in_scope((value_extents, source_info), block, |this| {
                     // schedule a shallow free of that memory, lest we unwind:
                     this.schedule_box_free(expr_span, value_extents, &result, value.ty);
                     // initialize the box contents:
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index 17d74571ce4..9be306d2848 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -39,16 +39,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                block, temp_lifetime, expr);
         let this = self;
 
+        let expr_span = expr.span;
+        let source_info = this.source_info(expr_span);
         if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this| {
+            return this.in_scope((extent, source_info), block, |this| {
                 this.as_temp(block, temp_lifetime, value)
             });
         }
 
         let expr_ty = expr.ty.clone();
-        let expr_span = expr.span;
         let temp = this.temp(expr_ty.clone(), expr_span);
-        let source_info = this.source_info(expr_span);
 
         if !expr_ty.is_never() && temp_lifetime.is_some() {
             this.cfg.push(block, Statement {
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index d456bc3ded3..b7abc707a38 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -39,6 +39,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
+                let extent = (extent, source_info);
                 this.in_scope(extent, block, |this| this.into(destination, block, value))
             }
             ExprKind::Block { body: ast_block } => {
@@ -233,7 +234,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             .collect();
 
                     let success = this.cfg.start_new_block();
-                    let cleanup = this.diverge_cleanup();
+                    let cleanup = this.diverge_cleanup(expr_span);
                     this.cfg.terminate(block, source_info, TerminatorKind::Call {
                         func: fun,
                         args: args,
diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs
index 3c7ab373651..3120ac21908 100644
--- a/src/librustc_mir/build/expr/stmt.rs
+++ b/src/librustc_mir/build/expr/stmt.rs
@@ -24,7 +24,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         match expr.kind {
             ExprKind::Scope { extent, value } => {
                 let value = this.hir.mirror(value);
-                this.in_scope(extent, block, |this| this.stmt_expr(block, value))
+                this.in_scope((extent, source_info), block, |this| this.stmt_expr(block, value))
             }
             ExprKind::Assign { lhs, rhs } => {
                 let lhs = this.hir.mirror(lhs);
@@ -81,7 +81,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     *this.find_breakable_scope(expr_span, label);
                 let continue_block = continue_block.expect(
                     "Attempted to continue in non-continuable breakable block");
-                this.exit_scope(expr_span, extent, block, continue_block);
+                this.exit_scope(expr_span, (extent, source_info), block, continue_block);
                 this.cfg.start_new_block().unit()
             }
             ExprKind::Break { label, value } => {
@@ -99,7 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 } else {
                     this.cfg.push_assign_unit(block, source_info, &destination)
                 }
-                this.exit_scope(expr_span, extent, block, break_block);
+                this.exit_scope(expr_span, (extent, source_info), block, break_block);
                 this.cfg.start_new_block().unit()
             }
             ExprKind::Return { value } => {
@@ -116,7 +116,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 };
                 let extent = this.extent_of_return_scope();
                 let return_block = this.return_block();
-                this.exit_scope(expr_span, extent, block, return_block);
+                this.exit_scope(expr_span, (extent, source_info), block, return_block);
                 this.cfg.start_new_block().unit()
             }
             ExprKind::InlineAsm { asm, outputs, inputs } => {
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index 28386fa598c..f4d43e041ae 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -306,7 +306,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     let bool_ty = self.hir.bool_ty();
                     let eq_result = self.temp(bool_ty, test.span);
                     let eq_block = self.cfg.start_new_block();
-                    let cleanup = self.diverge_cleanup();
+                    let cleanup = self.diverge_cleanup(test.span);
                     self.cfg.terminate(block, source_info, TerminatorKind::Call {
                         func: Operand::Constant(box Constant {
                             span: test.span,
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 56c0e18d6f9..eb1414d42e1 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -339,8 +339,9 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
     let call_site_extent = CodeExtent::CallSiteScope(body.id());
     let arg_extent = CodeExtent::ParameterScope(body.id());
     let mut block = START_BLOCK;
-    unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
-        unpack!(block = builder.in_scope(arg_extent, block, |builder| {
+    let source_info = builder.source_info(span);
+    unpack!(block = builder.in_scope((call_site_extent, source_info), block, |builder| {
+        unpack!(block = builder.in_scope((arg_extent, source_info), block, |builder| {
             builder.args_and_body(block, &arguments, arg_extent, &body.value)
         }));
         // Attribute epilogue to function's closing brace
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index a99e7b4be57..469fd5750a2 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -94,10 +94,11 @@ use rustc::ty::subst::{Kind, Subst};
 use rustc::ty::{Ty, TyCtxt};
 use rustc::mir::*;
 use rustc::mir::transform::MirSource;
-use syntax_pos::Span;
+use syntax_pos::{Span};
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::fx::FxHashMap;
 
+#[derive(Debug)]
 pub struct Scope<'tcx> {
     /// The visibility scope this scope was created in.
     visibility_scope: VisibilityScope,
@@ -114,7 +115,7 @@ pub struct Scope<'tcx> {
     ///  * pollutting the cleanup MIR with StorageDead creates
     ///    landing pads even though there's no actual destructors
     ///  * freeing up stack space has no effect during unwinding
-    needs_cleanup: bool,
+    pub(super) needs_cleanup: bool,
 
     /// set of lvalues to drop when exiting this scope. This starts
     /// out empty but grows as variables are declared during the
@@ -141,6 +142,7 @@ pub struct Scope<'tcx> {
     cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
 }
 
+#[derive(Debug)]
 struct DropData<'tcx> {
     /// span where drop obligation was incurred (typically where lvalue was declared)
     span: Span,
@@ -152,6 +154,7 @@ struct DropData<'tcx> {
     kind: DropKind
 }
 
+#[derive(Debug)]
 enum DropKind {
     Value {
         /// The cached block for the cleanups-on-diverge path. This block
@@ -163,6 +166,7 @@ enum DropKind {
     Storage
 }
 
+#[derive(Debug)]
 struct FreeData<'tcx> {
     /// span where free obligation was incurred
     span: Span,
@@ -269,17 +273,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         res
     }
 
+    pub fn in_opt_scope<F, R>(&mut self,
+                              opt_extent: Option<(CodeExtent, SourceInfo)>,
+                              mut block: BasicBlock,
+                              f: F)
+                              -> BlockAnd<R>
+        where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
+    {
+        debug!("in_opt_scope(opt_extent={:?}, block={:?})", opt_extent, block);
+        if let Some(extent) = opt_extent { self.push_scope(extent.0); }
+        let rv = unpack!(block = f(self));
+        if let Some(extent) = opt_extent {
+            unpack!(block = self.pop_scope(extent, block));
+        }
+        debug!("in_scope: exiting opt_extent={:?} block={:?}", opt_extent, block);
+        block.and(rv)
+    }
+
     /// Convenience wrapper that pushes a scope and then executes `f`
     /// to build its contents, popping the scope afterwards.
     pub fn in_scope<F, R>(&mut self,
-                          extent: CodeExtent,
+                          extent: (CodeExtent, SourceInfo),
                           mut block: BasicBlock,
                           f: F)
                           -> BlockAnd<R>
         where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
     {
         debug!("in_scope(extent={:?}, block={:?})", extent, block);
-        self.push_scope(extent);
+        self.push_scope(extent.0);
         let rv = unpack!(block = f(self));
         unpack!(block = self.pop_scope(extent, block));
         debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
@@ -307,20 +328,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// drops onto the end of `block` that are needed.  This must
     /// match 1-to-1 with `push_scope`.
     pub fn pop_scope(&mut self,
-                     extent: CodeExtent,
+                     extent: (CodeExtent, SourceInfo),
                      mut block: BasicBlock)
                      -> BlockAnd<()> {
         debug!("pop_scope({:?}, {:?})", extent, block);
         // We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
         // to make sure all the `cached_block`s are filled in.
-        self.diverge_cleanup();
+        self.diverge_cleanup(extent.1.span);
         let scope = self.scopes.pop().unwrap();
-        assert_eq!(scope.extent, extent);
+        assert_eq!(scope.extent, extent.0);
         unpack!(block = build_scope_drops(&mut self.cfg,
                                           &scope,
                                           &self.scopes,
                                           block,
                                           self.arg_count));
+
+        self.cfg.push_end_region(block, extent.1, scope.extent);
         block.unit()
     }
 
@@ -331,11 +354,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// module comment for details.
     pub fn exit_scope(&mut self,
                       span: Span,
-                      extent: CodeExtent,
+                      extent: (CodeExtent, SourceInfo),
                       mut block: BasicBlock,
                       target: BasicBlock) {
         debug!("exit_scope(extent={:?}, block={:?}, target={:?})", extent, block, target);
-        let scope_count = 1 + self.scopes.iter().rev().position(|scope| scope.extent == extent)
+        let scope_count = 1 + self.scopes.iter().rev().position(|scope| scope.extent == extent.0)
                                                       .unwrap_or_else(||{
             span_bug!(span, "extent {:?} does not enclose", extent)
         });
@@ -346,7 +369,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let mut rest = &mut self.scopes[(len - scope_count)..];
         while let Some((scope, rest_)) = {rest}.split_last_mut() {
             rest = rest_;
-            block = if let Some(&e) = scope.cached_exits.get(&(target, extent)) {
+            block = if let Some(&e) = scope.cached_exits.get(&(target, extent.0)) {
                 self.cfg.terminate(block, scope.source_info(span),
                                    TerminatorKind::Goto { target: e });
                 return;
@@ -354,7 +377,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let b = self.cfg.start_new_block();
                 self.cfg.terminate(block, scope.source_info(span),
                                    TerminatorKind::Goto { target: b });
-                scope.cached_exits.insert((target, extent), b);
+                scope.cached_exits.insert((target, extent.0), b);
                 b
             };
             unpack!(block = build_scope_drops(&mut self.cfg,
@@ -362,6 +385,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                               rest,
                                               block,
                                               self.arg_count));
+
+            // End all regions for scopes out of which we are breaking.
+            self.cfg.push_end_region(block, extent.1, scope.extent);
+
             if let Some(ref free_data) = scope.free {
                 let next = self.cfg.start_new_block();
                 let free = build_free(self.hir.tcx(), &tmp, free_data, next);
@@ -590,7 +617,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// This path terminates in Resume. Returns the start of the path.
     /// See module comment for more details. None indicates there’s no
     /// cleanup to do at this point.
-    pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
+    pub fn diverge_cleanup(&mut self, span: Span) -> Option<BasicBlock> {
         if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
             return None;
         }
@@ -623,8 +650,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             resumeblk
         };
 
-        for scope in scopes.iter_mut().filter(|s| s.needs_cleanup) {
-            target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
+        for scope in scopes.iter_mut() {
+            target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, span, scope, target);
         }
         Some(target)
     }
@@ -640,7 +667,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }
         let source_info = self.source_info(span);
         let next_target = self.cfg.start_new_block();
-        let diverge_target = self.diverge_cleanup();
+        let diverge_target = self.diverge_cleanup(span);
         self.cfg.terminate(block, source_info,
                            TerminatorKind::Drop {
                                location: location,
@@ -658,7 +685,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                   value: Operand<'tcx>) -> BlockAnd<()> {
         let source_info = self.source_info(span);
         let next_target = self.cfg.start_new_block();
-        let diverge_target = self.diverge_cleanup();
+        let diverge_target = self.diverge_cleanup(span);
         self.cfg.terminate(block, source_info,
                            TerminatorKind::DropAndReplace {
                                location: location,
@@ -681,7 +708,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let source_info = self.source_info(span);
 
         let success_block = self.cfg.start_new_block();
-        let cleanup = self.diverge_cleanup();
+        let cleanup = self.diverge_cleanup(span);
 
         self.cfg.terminate(block, source_info,
                            TerminatorKind::Assert {
@@ -750,6 +777,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
 fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                        cfg: &mut CFG<'tcx>,
                                        unit_temp: &Lvalue<'tcx>,
+                                       span: Span,
                                        scope: &mut Scope<'tcx>,
                                        mut target: BasicBlock)
                                        -> BasicBlock
@@ -757,9 +785,9 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     // Build up the drops in **reverse** order. The end result will
     // look like:
     //
-    //    [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
-    //    |                                    |
-    //    +------------------------------------+
+    //    [EndRegion Block] -> [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
+    //    |                                                         |
+    //    +---------------------------------------------------------+
     //     code for scope
     //
     // The code in this function reads from right to left. At each
@@ -789,9 +817,16 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     // Next, build up the drops. Here we iterate the vector in
     // *forward* order, so that we generate drops[0] first (right to
     // left in diagram above).
-    for drop_data in &mut scope.drops {
+    for (j, drop_data) in scope.drops.iter_mut().enumerate() {
+        debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data);
         // Only full value drops are emitted in the diverging path,
         // not StorageDead.
+        //
+        // Note: This may not actually be what we desire (are we
+        // "freeing" stack storage as we unwind, or merely observing a
+        // frozen stack)? In particular, the intent may have been to
+        // match the behavior of clang, but on inspection eddyb says
+        // this is not what clang does.
         let cached_block = match drop_data.kind {
             DropKind::Value { ref mut cached_block } => cached_block,
             DropKind::Storage => continue
@@ -811,6 +846,15 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         };
     }
 
+    // Finally, push the EndRegion block, used by mir-borrowck. (Block
+    // becomes trivial goto after pass that removes all EndRegions.)
+    {
+        let block = cfg.start_new_cleanup_block();
+        cfg.push_end_region(block, source_info(span), scope.extent);
+        cfg.terminate(block, source_info(span), TerminatorKind::Goto { target: target });
+        target = block
+    }
+
     target
 }
 
diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs
index 47c50b78f3a..fad070ca8d8 100644
--- a/src/librustc_mir/hair/cx/block.rs
+++ b/src/librustc_mir/hair/cx/block.rs
@@ -22,9 +22,14 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
         // We have to eagerly translate the "spine" of the statements
         // in order to get the lexical scoping correctly.
         let stmts = mirror_stmts(cx, self.id, &*self.stmts);
+        let opt_def_id = cx.tcx.hir.opt_local_def_id(self.id);
+        let opt_destruction_extent = opt_def_id.and_then(|def_id| {
+            cx.tcx.region_maps(def_id).opt_destruction_extent(self.id)
+        });
         Block {
             targeted_by_break: self.targeted_by_break,
             extent: CodeExtent::Misc(self.id),
+            opt_destruction_extent: opt_destruction_extent,
             span: self.span,
             stmts: stmts,
             expr: self.expr.to_ref(),
@@ -37,7 +42,11 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                 stmts: &'tcx [hir::Stmt])
                                 -> Vec<StmtRef<'tcx>> {
     let mut result = vec![];
+    let opt_def_id = cx.tcx.hir.opt_local_def_id(block_id);
     for (index, stmt) in stmts.iter().enumerate() {
+        let opt_dxn_ext = opt_def_id.and_then(|def_id| {
+            cx.tcx.region_maps(def_id).opt_destruction_extent(stmt.node.id())
+        });
         match stmt.node {
             hir::StmtExpr(ref expr, id) |
             hir::StmtSemi(ref expr, id) => {
@@ -47,6 +56,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                         scope: CodeExtent::Misc(id),
                         expr: expr.to_ref(),
                     },
+                    opt_destruction_extent: opt_dxn_ext,
                 })))
             }
             hir::StmtDecl(ref decl, id) => {
@@ -69,6 +79,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                 pattern: pattern,
                                 initializer: local.init.to_ref(),
                             },
+                            opt_destruction_extent: opt_dxn_ext,
                         })));
                     }
                 }
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 044096699b1..bb11cce7487 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -33,6 +33,7 @@ pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPatt
 pub struct Block<'tcx> {
     pub targeted_by_break: bool,
     pub extent: CodeExtent,
+    pub opt_destruction_extent: Option<CodeExtent>,
     pub span: Span,
     pub stmts: Vec<StmtRef<'tcx>>,
     pub expr: Option<ExprRef<'tcx>>,
@@ -47,6 +48,7 @@ pub enum StmtRef<'tcx> {
 pub struct Stmt<'tcx> {
     pub span: Span,
     pub kind: StmtKind<'tcx>,
+    pub opt_destruction_extent: Option<CodeExtent>,
 }
 
 #[derive(Clone, Debug)]
diff --git a/src/librustc_mir/transform/clean_end_regions.rs b/src/librustc_mir/transform/clean_end_regions.rs
new file mode 100644
index 00000000000..36125f94543
--- /dev/null
+++ b/src/librustc_mir/transform/clean_end_regions.rs
@@ -0,0 +1,84 @@
+// 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.
+
+//! This module provides one pass, `CleanEndRegions`, that reduces the
+//! set of `EndRegion` statements in the MIR.
+//!
+//! The "pass" is actually implemented as two traversals (aka visits)
+//! of the input MIR. The first traversal, `GatherBorrowedRegions`,
+//! finds all of the regions in the MIR that are involved in a borrow.
+//!
+//! The second traversal, `DeleteTrivialEndRegions`, walks over the
+//! MIR and removes any `EndRegion` that is applied to a region that
+//! was not seen in the previous pass.
+
+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};
+
+pub struct CleanEndRegions;
+
+struct GatherBorrowedRegions {
+    seen_regions: FxHashSet<CodeExtent>,
+}
+
+struct DeleteTrivialEndRegions<'a> {
+    seen_regions: &'a FxHashSet<CodeExtent>,
+}
+
+impl MirPass for CleanEndRegions {
+    fn run_pass<'a, 'tcx>(&self,
+                          _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          _source: MirSource,
+                          mir: &mut Mir<'tcx>) {
+        let mut gather = GatherBorrowedRegions { seen_regions: FxHashSet() };
+        gather.visit_mir(mir);
+
+        let mut delete = DeleteTrivialEndRegions { seen_regions: &mut gather.seen_regions };
+        delete.visit_mir(mir);
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
+    fn visit_rvalue(&mut self,
+                    rvalue: &Rvalue<'tcx>,
+                    location: Location) {
+        if let Rvalue::Ref(r, _, _) = *rvalue {
+            if let RegionKind::ReScope(ce) = *r {
+                self.seen_regions.insert(ce);
+            }
+        }
+        self.super_rvalue(rvalue, location);
+    }
+}
+
+impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> {
+    fn visit_statement(&mut self,
+                       block: BasicBlock,
+                       statement: &mut Statement<'tcx>,
+                       location: Location) {
+        let mut delete_it = false;
+
+        if let StatementKind::EndRegion(ref extent) = statement.kind {
+            if !self.seen_regions.contains(extent) {
+                delete_it = true;
+            }
+        }
+
+        if delete_it {
+            statement.kind = StatementKind::Nop;
+        }
+        self.super_statement(block, statement, location);
+    }
+}
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index fa88eca6ec3..e809695c180 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -65,6 +65,15 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
                             substs: &mut ClosureSubsts<'tcx>) {
         *substs = self.tcx.erase_regions(substs);
     }
+
+    fn visit_statement(&mut self,
+                       _block: BasicBlock,
+                       statement: &mut Statement<'tcx>,
+                       _location: Location) {
+        if let StatementKind::EndRegion(_) = statement.kind {
+            statement.kind = StatementKind::Nop;
+        }
+    }
 }
 
 pub struct EraseRegions;
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index fcea5d4c860..4594c611d59 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 clean_end_regions;
 pub mod simplify_branches;
 pub mod simplify;
 pub mod erase_regions;
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 8342d0ff491..05a6cdd57ff 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -894,6 +894,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 StatementKind::StorageLive(_) |
                 StatementKind::StorageDead(_) |
                 StatementKind::InlineAsm {..} |
+                StatementKind::EndRegion(_) |
                 StatementKind::Nop => {}
             }
         });
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index e23f0705b6a..efde39ad6a4 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -413,6 +413,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 }
             }
             StatementKind::InlineAsm { .. } |
+            StatementKind::EndRegion(_) |
             StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_mir/util/patch.rs b/src/librustc_mir/util/patch.rs
index 7898d93c22e..ac121131eb9 100644
--- a/src/librustc_mir/util/patch.rs
+++ b/src/librustc_mir/util/patch.rs
@@ -46,6 +46,7 @@ impl<'tcx> MirPatch<'tcx> {
         for (bb, block) in mir.basic_blocks().iter_enumerated() {
             if let TerminatorKind::Resume = block.terminator().kind {
                 if block.statements.len() > 0 {
+                    assert!(resume_stmt_block.is_none());
                     resume_stmt_block = Some(bb);
                 } else {
                     resume_block = Some(bb);
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index e29da3a6496..4dd38cc515c 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -125,6 +125,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
         self.record("Statement", statement);
         self.record(match statement.kind {
             StatementKind::Assign(..) => "StatementKind::Assign",
+            StatementKind::EndRegion(..) => "StatementKind::EndRegion",
             StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
             StatementKind::StorageLive(..) => "StatementKind::StorageLive",
             StatementKind::StorageDead(..) => "StatementKind::StorageDead",
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 4967ef2f790..16ef32ccf57 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -284,6 +284,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                     }
                     mir::StatementKind::StorageLive(_) |
                     mir::StatementKind::StorageDead(_) |
+                    mir::StatementKind::EndRegion(_) |
                     mir::StatementKind::Nop => {}
                     mir::StatementKind::InlineAsm { .. } |
                     mir::StatementKind::SetDiscriminant{ .. } => {
diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs
index 52c2afca474..170a76a4949 100644
--- a/src/librustc_trans/mir/statement.rs
+++ b/src/librustc_trans/mir/statement.rs
@@ -86,6 +86,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
                 asm::trans_inline_asm(&bcx, asm, outputs, input_vals);
                 bcx
             }
+            mir::StatementKind::EndRegion(_) |
             mir::StatementKind::Nop => bcx,
         }
     }
diff --git a/src/test/codegen/drop.rs b/src/test/codegen/drop.rs
index d7e2cb6d9a5..1961060c2c2 100644
--- a/src/test/codegen/drop.rs
+++ b/src/test/codegen/drop.rs
@@ -32,9 +32,9 @@ pub fn droppy() {
 // CHECK-NOT: invoke{{.*}}drop{{.*}}SomeUniqueName
 // CHECK: call{{.*}}drop{{.*}}SomeUniqueName
 // CHECK: call{{.*}}drop{{.*}}SomeUniqueName
-// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
 // CHECK-NOT: call{{.*}}drop{{.*}}SomeUniqueName
 // CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
+// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
 // CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
 // CHECK: call{{.*}}drop{{.*}}SomeUniqueName
 // CHECK-NOT: {{(call|invoke).*}}drop{{.*}}SomeUniqueName
diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md
index 9144e9757f6..28a124e3c61 100644
--- a/src/test/mir-opt/README.md
+++ b/src/test/mir-opt/README.md
@@ -22,7 +22,32 @@ All the test information is in comments so the test is runnable.
 
 For each $file_name, compiletest expects [$expected_line_0, ...,
 $expected_line_N] to appear in the dumped MIR in order.  Currently it allows
-other non-matched lines before, after and in-between.  
+other non-matched lines before, after and in-between. Note that this includes
+lines that end basic blocks or begin new ones; it is good practice
+in your tests to include the terminator for each of your basic blocks as an
+internal sanity check guarding against a test like:
+
+```
+bb0: {
+    StorageLive(_1);
+    _1 = const true;
+    StorageDead(_1);
+}
+```
+
+that will inadvertantly pattern-matching against:
+
+```
+bb0: {
+    StorageLive(_1);
+    _1 = const true;
+    goto -> bb1
+}
+bb1: {
+    StorageDead(_1);
+    return;
+}
+```
 
 Lines match ignoring whitespace, and the prefix "//" is removed.
 
diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs
index e4eb1aeaf9b..ef5158a403a 100644
--- a/src/test/mir-opt/basic_assignment.rs
+++ b/src/test/mir-opt/basic_assignment.rs
@@ -50,7 +50,7 @@ fn main() {
 //         StorageLive(_6);
 //         StorageLive(_7);
 //         _7 = _4;
-//         replace(_6 <- _7) -> [return: bb5, unwind: bb4];
+//         replace(_6 <- _7) -> [return: bb6, unwind: bb7];
 //     }
 //     bb1: {
 //         resume;
@@ -59,24 +59,30 @@ fn main() {
 //         drop(_4) -> bb1;
 //     }
 //     bb3: {
-//         drop(_6) -> bb2;
+//         goto -> bb2;
 //     }
 //     bb4: {
-//         drop(_7) -> bb3;
+//         drop(_6) -> bb3;
 //     }
 //     bb5: {
-//         drop(_7) -> [return: bb6, unwind: bb3];
+//         goto -> bb4;
 //     }
 //     bb6: {
+//         drop(_7) -> [return: bb8, unwind: bb4];
+//     }
+//     bb7: {
+//         drop(_7) -> bb5;
+//     }
+//     bb8: {
 //         StorageDead(_7);
 //         _0 = ();
-//         drop(_6) -> [return: bb7, unwind: bb2];
+//         drop(_6) -> [return: bb9, unwind: bb2];
 //     }
-//     bb7: {
+//     bb9: {
 //         StorageDead(_6);
-//         drop(_4) -> bb8;
+//         drop(_4) -> bb10;
 //     }
-//     bb8: {
+//     bb10: {
 //         StorageDead(_4);
 //         StorageDead(_2);
 //         StorageDead(_1);
diff --git a/src/test/mir-opt/end_region_1.rs b/src/test/mir-opt/end_region_1.rs
new file mode 100644
index 00000000000..55dac444027
--- /dev/null
+++ b/src/test/mir-opt/end_region_1.rs
@@ -0,0 +1,38 @@
+// 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.
+
+// compile-flags: -Z identify_regions
+// ignore-tidy-linelength
+
+// This is just about the simplest program that exhibits an EndRegion.
+
+fn main() {
+    let a = 3;
+    let b = &a;
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+//     let mut _0: ();
+//     let _1: i32;
+//     let _2: &'6_1rce i32;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = const 3i32;
+//         StorageLive(_2);
+//         _2 = &'6_1rce _1;
+//         _0 = ();
+//         StorageDead(_2);
+//         EndRegion('6_1rce);
+//         StorageDead(_1);
+//         return;
+//     }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs
new file mode 100644
index 00000000000..a1386ec47a1
--- /dev/null
+++ b/src/test/mir-opt/end_region_2.rs
@@ -0,0 +1,66 @@
+// 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.
+
+// compile-flags: -Z identify_regions
+// ignore-tidy-linelength
+
+// We will EndRegion for borrows in a loop that occur before break but
+// not those after break.
+
+fn main() {
+    loop {
+        let a = true;
+        let b = &a;
+        if a { break; }
+        let c = &a;
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+//     let mut _0: ();
+//     let _2: bool;
+//     let _3: &'7_1rce bool;
+//     let _7: &'7_3rce bool;
+//     let mut _4: ();
+//     let mut _5: bool;
+//     bb0: {
+//         goto -> bb1;
+//     }
+//     bb1: {
+//         StorageLive(_2);
+//         _2 = const true;
+//         StorageLive(_3);
+//         _3 = &'7_1rce _2;
+//         StorageLive(_5);
+//         _5 = _2;
+//         switchInt(_5) -> [0u8: bb3, otherwise: bb2];
+//     }
+//     bb2: {
+//         _0 = ();
+//         StorageDead(_5);
+//         StorageDead(_3);
+//         EndRegion('7_1rce);
+//         StorageDead(_2);
+//         return;
+//     }
+//     bb3: {
+//         StorageDead(_5);
+//         StorageLive(_7);
+//         _7 = &'7_3rce _2;
+//         _1 = ();
+//         StorageDead(_7);
+//         EndRegion('7_3rce);
+//         StorageDead(_3);
+//         EndRegion('7_1rce);
+//         StorageDead(_2);
+//         goto -> bb1;
+//     }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs
new file mode 100644
index 00000000000..b3d2809e76c
--- /dev/null
+++ b/src/test/mir-opt/end_region_3.rs
@@ -0,0 +1,69 @@
+// 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.
+
+// compile-flags: -Z identify_regions
+// ignore-tidy-linelength
+
+// Binding the borrow's subject outside the loop does not increase the
+// scope of the borrow.
+
+fn main() {
+    let mut a;
+    loop {
+        a = true;
+        let b = &a;
+        if a { break; }
+        let c = &a;
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+//     let mut _0: ();
+//     let mut _1: bool;
+//     let _3: &'9_1rce bool;
+//     let _7: &'9_3rce bool;
+//     let mut _2: ();
+//     let mut _4: ();
+//     let mut _5: bool;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         goto -> bb1;
+//     }
+//     bb1: {
+//         _1 = const true;
+//         StorageLive(_3);
+//         _3 = &'9_1rce _1;
+//         StorageLive(_5);
+//         _5 = _1;
+//         switchInt(_5) -> [0u8: bb3, otherwise: bb2];
+//     }
+//     bb2: {
+//         _0 = ();
+//         StorageDead(_5);
+//         StorageDead(_3);
+//         EndRegion('9_1rce);
+//         StorageDead(_1);
+//         return;
+//     }
+//     bb3: {
+//         _4 = ();
+//         StorageDead(_5);
+//         StorageLive(_7);
+//         _7 = &'9_3rce _1;
+//         _2 = ();
+//         StorageDead(_7);
+//         EndRegion('9_3rce);
+//         StorageDead(_3);
+//         EndRegion('9_1rce);
+//         goto -> bb1;
+//     }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_4.rs b/src/test/mir-opt/end_region_4.rs
new file mode 100644
index 00000000000..16ade9f96fd
--- /dev/null
+++ b/src/test/mir-opt/end_region_4.rs
@@ -0,0 +1,75 @@
+// 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.
+
+// compile-flags: -Z identify_regions
+// ignore-tidy-linelength
+
+// Unwinding should EndRegion for in-scope borrows: Direct borrows.
+
+fn main() {
+    let d = D(0);
+    let a = 0;
+    let b = &a;
+    foo(*b);
+    let c = &a;
+}
+
+struct D(i32);
+impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
+
+fn foo(i: i32) {
+    if i > 0 { panic!("im positive"); }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+//     let mut _0: ();
+//     let _1: D;
+//     let _3: i32;
+//     let _4: &'6_2rce i32;
+//     let _7: &'6_4rce i32;
+//     let mut _5: ();
+//     let mut _6: i32;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = D::{{constructor}}(const 0i32,);
+//         StorageLive(_3);
+//         _3 = const 0i32;
+//         StorageLive(_4);
+//         _4 = &'6_2rce _3;
+//         StorageLive(_6);
+//         _6 = (*_4);
+//         _5 = const foo(_6) -> [return: bb2, unwind: bb3];
+//     }
+//     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         StorageDead(_6);
+//         StorageLive(_7);
+//         _7 = &'6_4rce _3;
+//         _0 = ();
+//         StorageDead(_7);
+//         EndRegion('6_4rce);
+//         StorageDead(_4);
+//         EndRegion('6_2rce);
+//         StorageDead(_3);
+//         drop(_1) -> bb4;
+//     }
+//     bb3: {
+//         EndRegion('6_2rce);
+//         drop(_1) -> bb1;
+//     }
+//     bb4: {
+//         StorageDead(_1);
+//         return;
+//     }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_5.rs b/src/test/mir-opt/end_region_5.rs
new file mode 100644
index 00000000000..513632a4cdf
--- /dev/null
+++ b/src/test/mir-opt/end_region_5.rs
@@ -0,0 +1,80 @@
+// 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.
+
+// compile-flags: -Z identify_regions -Z span_free_formats
+// ignore-tidy-linelength
+
+// Unwinding should EndRegion for in-scope borrows: Borrowing via by-ref closure.
+
+fn main() {
+    let d = D(0);
+    foo(|| -> i32 { d.0 });
+}
+
+struct D(i32);
+impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
+
+fn foo<F>(f: F) where F: FnOnce() -> i32 {
+    if f() > 0 { panic!("im positive"); }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+// fn main() -> () {
+//     let mut _0: ();
+//     let _1: D;
+//     let mut _2: ();
+//     let mut _3: ();
+//     let mut _4: [closure@NodeId(18) d: &'19mce D];
+//     let mut _5: &'19mce D;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = D::{{constructor}}(const 0i32,);
+//         StorageLive(_4);
+//         StorageLive(_5);
+//         _5 = &'19mce _1;
+//         _4 = [closure@NodeId(18)] { d: _5 };
+//         StorageDead(_5);
+//         _3 = const foo(_4) -> [return: bb2, unwind: bb3];
+//     }
+//     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         StorageDead(_4);
+//         EndRegion('19mce);
+//         _0 = ();
+//         drop(_1) -> bb4;
+//     }
+//     bb3: {
+//         EndRegion('19mce);
+//         drop(_1) -> bb1;
+//     }
+//     bb4: {
+//         StorageDead(_1);
+//         return;
+//     }
+// }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
+
+// START rustc.node18.SimplifyCfg-qualify-consts.after.mir
+// fn main::{{closure}}(_1: [closure@NodeId(18) d:&'19mce D]) -> i32 {
+//    let mut _0: i32;
+//    let mut _2: i32;
+//
+//    bb0: {
+//        StorageLive(_2);
+//        _2 = ((*(_1.0: &'19mce D)).0: i32);
+//        _0 = _2;
+//        StorageDead(_2);
+//        return;
+//    }
+// END rustc.node18.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs
new file mode 100644
index 00000000000..e82556f3ce4
--- /dev/null
+++ b/src/test/mir-opt/end_region_6.rs
@@ -0,0 +1,83 @@
+// 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.
+
+// compile-flags: -Z identify_regions -Z span_free_formats
+// ignore-tidy-linelength
+
+// Unwinding should EndRegion for in-scope borrows: 2nd borrow within by-ref closure.
+
+fn main() {
+    let d = D(0);
+    foo(|| -> i32 { let r = &d; r.0 });
+}
+
+struct D(i32);
+impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
+
+fn foo<F>(f: F) where F: FnOnce() -> i32 {
+    if f() > 0 { panic!("im positive"); }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+//     let mut _0: ();
+//     let _1: D;
+//     let mut _2: ();
+//     let mut _3: ();
+//     let mut _4: [closure@NodeId(22) d:&'23mce D];
+//     let mut _5: &'23mce D;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = D::{{constructor}}(const 0i32,);
+//         StorageLive(_4);
+//         StorageLive(_5);
+//         _5 = &'23mce _1;
+//         _4 = [closure@NodeId(22)] { d: _5 };
+//         StorageDead(_5);
+//         _3 = const foo(_4) -> [return: bb2, unwind: bb3];
+//     }
+//     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         StorageDead(_4);
+//         EndRegion('23mce);
+//         _0 = ();
+//         drop(_1) -> bb4;
+//     }
+//     bb3: {
+//         EndRegion('23mce);
+//         drop(_1) -> bb1;
+//     }
+//     bb4: {
+//         StorageDead(_1);
+//         return;
+//     }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
+
+// START rustc.node22.SimplifyCfg-qualify-consts.after.mir
+// fn main::{{closure}}(_1: [closure@NodeId(22) d:&'23mce D]) -> i32 {
+//     let mut _0: i32;
+//     let _2: &'14_0rce D;
+//     let mut _3: i32;
+//
+//     bb0: {
+//         StorageLive(_2);
+//         _2 = &'14_0rce (*(_1.0: &'23mce D));
+//         StorageLive(_3);
+//         _3 = ((*_2).0: i32);
+//         _0 = _3;
+//         StorageDead(_3);
+//         StorageDead(_2);
+//         EndRegion('14_0rce);
+//         return;
+//     }
+// END rustc.node22.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs
new file mode 100644
index 00000000000..3fbd3f36865
--- /dev/null
+++ b/src/test/mir-opt/end_region_7.rs
@@ -0,0 +1,97 @@
+// 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.
+
+// compile-flags: -Z identify_regions -Z span_free_formats
+// ignore-tidy-linelength
+
+// Unwinding should EndRegion for in-scope borrows: Borrow of moved data.
+
+fn main() {
+    let d = D(0);
+    foo(move || -> i32 { let r = &d; r.0 });
+}
+
+struct D(i32);
+impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
+
+fn foo<F>(f: F) where F: FnOnce() -> i32 {
+    if f() > 0 { panic!("im positive"); }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+// fn main() -> () {
+//     let mut _0: ();
+//     let _1: D;
+//     let mut _2: ();
+//     let mut _3: ();
+//     let mut _4: [closure@NodeId(22) d:D];
+//     let mut _5: D;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = D::{{constructor}}(const 0i32,);
+//         StorageLive(_4);
+//         StorageLive(_5);
+//         _5 = _1;
+//         _4 = [closure@NodeId(22)] { d: _5 };
+//         drop(_5) -> [return: bb4, unwind: bb3];
+//     }
+//     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         drop(_1) -> bb1;
+//     }
+//     bb3: {
+//         drop(_4) -> bb2;
+//     }
+//     bb4: {
+//         StorageDead(_5);
+//         _3 = const foo(_4) -> [return: bb5, unwind: bb3];
+//     }
+//     bb5: {
+//         drop(_4) -> [return: bb6, unwind: bb2];
+//     }
+//     bb6: {
+//         StorageDead(_4);
+//         _0 = ();
+//         drop(_1) -> bb7;
+//     }
+//     bb7: {
+//         StorageDead(_1);
+//         return;
+//     }
+// }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
+
+// START rustc.node22.SimplifyCfg-qualify-consts.after.mir
+// fn main::{{closure}}(_1: [closure@NodeId(22) d:D]) -> i32 {
+//     let mut _0: i32;
+//     let _2: &'14_0rce D;
+//     let mut _3: ();
+//     let mut _4: i32;
+//
+//     bb0: {
+//         StorageLive(_2);
+//         _2 = &'14_0rce (_1.0: D);
+//         StorageLive(_4);
+//         _4 = ((*_2).0: i32);
+//         _0 = _4;
+//         StorageDead(_4);
+//         StorageDead(_2);
+//         EndRegion('14_0rce);
+//         drop(_1) -> bb1;
+//     }
+//     bb1: {
+//         return;
+//     }
+// }
+// END rustc.node22.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs
new file mode 100644
index 00000000000..7fb3f0b9118
--- /dev/null
+++ b/src/test/mir-opt/end_region_8.rs
@@ -0,0 +1,86 @@
+// 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.
+
+// compile-flags: -Z identify_regions -Z span_free_formats
+// ignore-tidy-linelength
+
+// Unwinding should EndRegion for in-scope borrows: Move of borrow into closure.
+
+fn main() {
+    let d = D(0);
+    let r = &d;
+    foo(move || -> i32 { r.0 });
+}
+
+struct D(i32);
+impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
+
+fn foo<F>(f: F) where F: FnOnce() -> i32 {
+    if f() > 0 { panic!("im positive"); }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+// fn main() -> () {
+//     let mut _0: ();
+//     let _1: D;
+//     let _3: &'6_1rce D;
+//     let mut _2: ();
+//     let mut _4: ();
+//     let mut _5: [closure@NodeId(22) r:&'6_1rce D];
+//     let mut _6: &'6_1rce D;
+//
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = D::{{constructor}}(const 0i32,);
+//         StorageLive(_3);
+//         _3 = &'6_1rce _1;
+//         StorageLive(_5);
+//         StorageLive(_6);
+//         _6 = _3;
+//         _5 = [closure@NodeId(22)] { r: _6 };
+//         StorageDead(_6);
+//         _4 = const foo(_5) -> [return: bb2, unwind: bb3];
+//     }
+//     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         StorageDead(_5);
+//         _0 = ();
+//         StorageDead(_3);
+//         EndRegion('6_1rce);
+//         drop(_1) -> bb4;
+//     }
+//     bb3: {
+//         EndRegion('6_1rce);
+//         drop(_1) -> bb1;
+//     }
+//     bb4: {
+//         StorageDead(_1);
+//         return;
+//     }
+// }
+// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
+
+// START rustc.node22.SimplifyCfg-qualify-consts.after.mir
+// fn main::{{closure}}(_1: [closure@NodeId(22) r:&'6_1rce D]) -> i32 {
+//     let mut _0: i32;
+//     let mut _2: i32;
+//
+//     bb0: {
+//         StorageLive(_2);
+//         _2 = ((*(_1.0: &'6_1rce D)).0: i32);
+//         _0 = _2;
+//         StorageDead(_2);
+//         return;
+//     }
+// }
+// END rustc.node22.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs
new file mode 100644
index 00000000000..deff984e4d0
--- /dev/null
+++ b/src/test/mir-opt/end_region_9.rs
@@ -0,0 +1,85 @@
+// 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.
+
+// compile-flags: -Z identify_regions -Z span_free_formats
+// ignore-tidy-linelength
+
+// This test models a scenario that arielb1 found during review.
+// Namely, any filtering of EndRegions must ensure to continue to emit
+// any necessary EndRegions that occur earlier in the source than the
+// first borrow involving that region.
+//
+// It is tricky to actually construct examples of this, which is the
+// main reason that I am keeping this test even though I have now
+// removed the pre-filter that motivated the test in the first place.
+
+fn main() {
+    let mut second_iter = false;
+    let x = 3;
+    'a: loop {
+        let mut y;
+        loop {
+            if second_iter {
+                break 'a; // want to generate `EndRegion('a)` here
+            } else {
+                y = &/*'a*/ x;
+            }
+            second_iter = true;
+        }
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
+// fn main() -> () {
+//     let mut _0: ();
+//     let mut _1: bool;
+//     let _2: i32;
+//     let mut _4: &'13_0rce i32;
+//     let mut _3: ();
+//     let mut _5: !;
+//     let mut _6: ();
+//     let mut _7: bool;
+//     let mut _8: !;
+//
+//     bb0: {
+//        StorageLive(_1);
+//        _1 = const false;
+//        StorageLive(_2);
+//        _2 = const 3i32;
+//        StorageLive(_4);
+//        goto -> bb1;
+//    }
+//
+//    bb1: {
+//        StorageLive(_7);
+//        _7 = _1;
+//        switchInt(_7) -> [0u8: bb3, otherwise: bb2];
+//    }
+//
+//    bb2: {
+//        _0 = ();
+//        StorageDead(_7);
+//        StorageDead(_4);
+//        EndRegion('13_0rce);
+//        StorageDead(_2);
+//        StorageDead(_1);
+//        return;
+//    }
+//
+//    bb3: {
+//        _4 = &'13_0rce _2;
+//        _6 = ();
+//        StorageDead(_7);
+//        _1 = const true;
+//        _3 = ();
+//        goto -> bb1;
+//    }
+// }