about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs79
-rw-r--r--src/librustc_borrowck/diagnostics.rs469
-rw-r--r--src/librustc_borrowck/lib.rs9
-rw-r--r--src/librustc_driver/lib.rs1
-rw-r--r--src/librustc_mir/diagnostics.rs461
-rw-r--r--src/librustc_mir/transform/nll/infer.rs222
-rw-r--r--src/librustc_mir/transform/nll/mod.rs32
-rw-r--r--src/librustc_mir/util/borrowck_errors.rs133
-rw-r--r--src/test/mir-opt/README.md33
-rw-r--r--src/test/mir-opt/box_expr.rs5
-rw-r--r--src/test/mir-opt/copy_propagation.rs6
-rw-r--r--src/test/mir-opt/deaggregator_test.rs6
-rw-r--r--src/test/mir-opt/deaggregator_test_enum.rs8
-rw-r--r--src/test/mir-opt/deaggregator_test_enum_2.rs10
-rw-r--r--src/test/mir-opt/deaggregator_test_multiple.rs10
-rw-r--r--src/test/mir-opt/end_region_1.rs4
-rw-r--r--src/test/mir-opt/end_region_2.rs6
-rw-r--r--src/test/mir-opt/end_region_3.rs6
-rw-r--r--src/test/mir-opt/end_region_4.rs5
-rw-r--r--src/test/mir-opt/end_region_5.rs3
-rw-r--r--src/test/mir-opt/end_region_6.rs5
-rw-r--r--src/test/mir-opt/end_region_7.rs6
-rw-r--r--src/test/mir-opt/end_region_8.rs3
-rw-r--r--src/test/mir-opt/end_region_9.rs7
-rw-r--r--src/test/mir-opt/end_region_cyclic.rs1
-rw-r--r--src/test/mir-opt/issue-41110.rs10
-rw-r--r--src/test/mir-opt/storage_live_dead_in_statics.rs200
-rw-r--r--src/test/mir-opt/storage_ranges.rs1
-rw-r--r--src/test/mir-opt/validate_1.rs5
-rw-r--r--src/test/mir-opt/validate_2.rs8
-rw-r--r--src/test/mir-opt/validate_3.rs15
-rw-r--r--src/test/mir-opt/validate_4.rs9
-rw-r--r--src/test/mir-opt/validate_5.rs5
-rw-r--r--src/tools/compiletest/src/runtest.rs126
34 files changed, 1265 insertions, 644 deletions
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index d6f7c2aa887..fc3c2a3f68f 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -614,11 +614,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 let partial = moved_lp.depth() > lp.depth();
                 let msg = if !has_fork && partial { "partially " }
                           else if has_fork && !has_common { "collaterally "}
-                          else { "" };
-                let mut err = struct_span_err!(
-                    self.tcx.sess, use_span, E0382,
-                    "{} of {}moved value: `{}`",
-                    verb, msg, nl);
+                else { "" };
+                let mut err = self.cannot_act_on_moved_value(use_span,
+                                                             verb,
+                                                             msg,
+                                                             &format!("{}", nl),
+                                                             Origin::Ast);
                 let need_note = match lp.ty.sty {
                     ty::TypeVariants::TyClosure(id, _) => {
                         let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
@@ -698,10 +699,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             &self,
             span: Span,
             lp: &LoanPath<'tcx>) {
-        span_err!(
-            self.tcx.sess, span, E0383,
-            "partial reinitialization of uninitialized structure `{}`",
-            self.loan_path_to_string(lp));
+        self.cannot_partially_reinit_an_uninit_struct(span,
+                                                      &self.loan_path_to_string(lp),
+                                                      Origin::Ast)
+            .emit();
     }
 
     pub fn report_reassigned_immutable_variable(&self,
@@ -776,8 +777,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                         db
                     }
                     BorrowViolation(euv::ClosureCapture(_)) => {
-                        struct_span_err!(self.tcx.sess, error_span, E0595,
-                                         "closure cannot assign to {}", descr)
+                        self.closure_cannot_assign_to_borrowed(error_span, &descr, Origin::Ast)
                     }
                     BorrowViolation(euv::OverloadedOperator) |
                     BorrowViolation(euv::AddrOf) |
@@ -786,8 +786,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     BorrowViolation(euv::AutoUnsafe) |
                     BorrowViolation(euv::ForLoop) |
                     BorrowViolation(euv::MatchDiscriminant) => {
-                        struct_span_err!(self.tcx.sess, error_span, E0596,
-                                         "cannot borrow {} as mutable", descr)
+                        self.cannot_borrow_path_as_mutable(error_span, &descr, Origin::Ast)
                     }
                     BorrowViolation(euv::ClosureInvocation) => {
                         span_bug!(err.span,
@@ -869,21 +868,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
 
                 if let Some((yield_span, _)) = maybe_borrow_across_yield {
                     debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span);
-                    struct_span_err!(self.tcx.sess,
-                                     error_span,
-                                     E0626,
-                                     "borrow may still be in use when generator yields")
-                        .span_label(yield_span, "possible yield occurs here")
+                    self.cannot_borrow_across_generator_yield(error_span, yield_span, Origin::Ast)
                         .emit();
                     return;
                 }
 
-                let mut db = struct_span_err!(self.tcx.sess,
-                                              error_span,
-                                              E0597,
-                                              "{} does not live long enough",
-                                              msg);
-
+                let mut db = self.path_does_not_live_long_enough(error_span, &msg, Origin::Ast);
                 let (value_kind, value_msg) = match err.cmt.cat {
                     mc::Categorization::Rvalue(..) =>
                         ("temporary value", "temporary value created here"),
@@ -992,11 +982,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             }
             err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
                 let descr = self.cmt_to_path_or_string(&err.cmt);
-                let mut db = struct_span_err!(self.tcx.sess, error_span, E0598,
-                                              "lifetime of {} is too short to guarantee \
-                                               its contents can be safely reborrowed",
-                                              descr);
-
+                let mut db = self.lifetime_too_short_for_reborrow(error_span, &descr, Origin::Ast);
                 let descr = match opt_loan_path(&err.cmt) {
                     Some(lp) => {
                         format!("`{}`", self.loan_path_to_string(&lp))
@@ -1068,12 +1054,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         let blame = cmt.immutability_blame();
         let mut err = match blame {
             Some(ImmutabilityBlame::ClosureEnv(id)) => {
-                let mut err = struct_span_err!(
-                    self.tcx.sess, span, E0387,
-                    "{} in a captured outer variable in an `Fn` closure", prefix);
-
                 // FIXME: the distinction between these 2 messages looks wrong.
-                let help = if let BorrowViolation(euv::ClosureCapture(_)) = kind {
+                let help_msg = if let BorrowViolation(euv::ClosureCapture(_)) = kind {
                     // The aliasability violation with closure captures can
                     // happen for nested closures, so we know the enclosing
                     // closure incorrectly accepts an `Fn` while it needs to
@@ -1084,15 +1066,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     "consider changing this closure to take self by mutable reference"
                 };
                 let node_id = self.tcx.hir.def_index_to_node_id(id);
-                err.span_help(self.tcx.hir.span(node_id), help);
-                err
+                let help_span = self.tcx.hir.span(node_id);
+                self.cannot_act_on_capture_in_sharable_fn(span,
+                                                          prefix,
+                                                          (help_span, help_msg),
+                                                          Origin::Ast)
             }
             _ =>  {
-                let mut err = struct_span_err!(
-                    self.tcx.sess, span, E0389,
-                    "{} in a `&` reference", prefix);
-                err.span_label(span, "assignment into an immutable reference");
-                err
+                self.cannot_assign_into_immutable_reference(span, prefix,
+                                                            Origin::Ast)
             }
         };
         self.note_immutability_blame(&mut err, blame);
@@ -1244,17 +1226,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 Err(_) => format!("move |<args>| <body>")
             };
 
-        struct_span_err!(self.tcx.sess, err.span, E0373,
-                         "closure may outlive the current function, \
-                          but it borrows {}, \
-                          which is owned by the current function",
-                         cmt_path_or_string)
-            .span_label(capture_span,
-                       format!("{} is borrowed here",
-                                cmt_path_or_string))
-            .span_label(err.span,
-                       format!("may outlive borrowed value {}",
-                                cmt_path_or_string))
+        self.cannot_capture_in_long_lived_closure(err.span,
+                                                  &cmt_path_or_string,
+                                                  capture_span,
+                                                  Origin::Ast)
             .span_suggestion(err.span,
                              &format!("to force the closure to take ownership of {} \
                                        (and any other referenced variables), \
diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs
index 031dbcb1ebb..3fea01443be 100644
--- a/src/librustc_borrowck/diagnostics.rs
+++ b/src/librustc_borrowck/diagnostics.rs
@@ -9,472 +9,3 @@
 // except according to those terms.
 
 #![allow(non_snake_case)]
-
-register_long_diagnostics! {
-
-E0373: r##"
-This error occurs when an attempt is made to use data captured by a closure,
-when that data may no longer exist. It's most commonly seen when attempting to
-return a closure:
-
-```compile_fail,E0373
-fn foo() -> Box<Fn(u32) -> u32> {
-    let x = 0u32;
-    Box::new(|y| x + y)
-}
-```
-
-Notice that `x` is stack-allocated by `foo()`. By default, Rust captures
-closed-over data by reference. This means that once `foo()` returns, `x` no
-longer exists. An attempt to access `x` within the closure would thus be
-unsafe.
-
-Another situation where this might be encountered is when spawning threads:
-
-```compile_fail,E0373
-fn foo() {
-    let x = 0u32;
-    let y = 1u32;
-
-    let thr = std::thread::spawn(|| {
-        x + y
-    });
-}
-```
-
-Since our new thread runs in parallel, the stack frame containing `x` and `y`
-may well have disappeared by the time we try to use them. Even if we call
-`thr.join()` within foo (which blocks until `thr` has completed, ensuring the
-stack frame won't disappear), we will not succeed: the compiler cannot prove
-that this behaviour is safe, and so won't let us do it.
-
-The solution to this problem is usually to switch to using a `move` closure.
-This approach moves (or copies, where possible) data into the closure, rather
-than taking references to it. For example:
-
-```
-fn foo() -> Box<Fn(u32) -> u32> {
-    let x = 0u32;
-    Box::new(move |y| x + y)
-}
-```
-
-Now that the closure has its own copy of the data, there's no need to worry
-about safety.
-"##,
-
-E0382: r##"
-This error occurs when an attempt is made to use a variable after its contents
-have been moved elsewhere. For example:
-
-```compile_fail,E0382
-struct MyStruct { s: u32 }
-
-fn main() {
-    let mut x = MyStruct{ s: 5u32 };
-    let y = x;
-    x.s = 6;
-    println!("{}", x.s);
-}
-```
-
-Since `MyStruct` is a type that is not marked `Copy`, the data gets moved out
-of `x` when we set `y`. This is fundamental to Rust's ownership system: outside
-of workarounds like `Rc`, a value cannot be owned by more than one variable.
-
-If we own the type, the easiest way to address this problem is to implement
-`Copy` and `Clone` on it, as shown below. This allows `y` to copy the
-information in `x`, while leaving the original version owned by `x`. Subsequent
-changes to `x` will not be reflected when accessing `y`.
-
-```
-#[derive(Copy, Clone)]
-struct MyStruct { s: u32 }
-
-fn main() {
-    let mut x = MyStruct{ s: 5u32 };
-    let y = x;
-    x.s = 6;
-    println!("{}", x.s);
-}
-```
-
-Alternatively, if we don't control the struct's definition, or mutable shared
-ownership is truly required, we can use `Rc` and `RefCell`:
-
-```
-use std::cell::RefCell;
-use std::rc::Rc;
-
-struct MyStruct { s: u32 }
-
-fn main() {
-    let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 }));
-    let y = x.clone();
-    x.borrow_mut().s = 6;
-    println!("{}", x.borrow().s);
-}
-```
-
-With this approach, x and y share ownership of the data via the `Rc` (reference
-count type). `RefCell` essentially performs runtime borrow checking: ensuring
-that at most one writer or multiple readers can access the data at any one time.
-
-If you wish to learn more about ownership in Rust, start with the chapter in the
-Book:
-
-https://doc.rust-lang.org/book/first-edition/ownership.html
-"##,
-
-E0383: r##"
-This error occurs when an attempt is made to partially reinitialize a
-structure that is currently uninitialized.
-
-For example, this can happen when a drop has taken place:
-
-```compile_fail,E0383
-struct Foo {
-    a: u32,
-}
-impl Drop for Foo {
-    fn drop(&mut self) { /* ... */ }
-}
-
-let mut x = Foo { a: 1 };
-drop(x); // `x` is now uninitialized
-x.a = 2; // error, partial reinitialization of uninitialized structure `t`
-```
-
-This error can be fixed by fully reinitializing the structure in question:
-
-```
-struct Foo {
-    a: u32,
-}
-impl Drop for Foo {
-    fn drop(&mut self) { /* ... */ }
-}
-
-let mut x = Foo { a: 1 };
-drop(x);
-x = Foo { a: 2 };
-```
-"##,
-
-/*E0386: r##"
-This error occurs when an attempt is made to mutate the target of a mutable
-reference stored inside an immutable container.
-
-For example, this can happen when storing a `&mut` inside an immutable `Box`:
-
-```compile_fail,E0386
-let mut x: i64 = 1;
-let y: Box<_> = Box::new(&mut x);
-**y = 2; // error, cannot assign to data in an immutable container
-```
-
-This error can be fixed by making the container mutable:
-
-```
-let mut x: i64 = 1;
-let mut y: Box<_> = Box::new(&mut x);
-**y = 2;
-```
-
-It can also be fixed by using a type with interior mutability, such as `Cell`
-or `RefCell`:
-
-```
-use std::cell::Cell;
-
-let x: i64 = 1;
-let y: Box<Cell<_>> = Box::new(Cell::new(x));
-y.set(2);
-```
-"##,*/
-
-E0387: r##"
-This error occurs when an attempt is made to mutate or mutably reference data
-that a closure has captured immutably. Examples of this error are shown below:
-
-```compile_fail,E0387
-// Accepts a function or a closure that captures its environment immutably.
-// Closures passed to foo will not be able to mutate their closed-over state.
-fn foo<F: Fn()>(f: F) { }
-
-// Attempts to mutate closed-over data. Error message reads:
-// `cannot assign to data in a captured outer variable...`
-fn mutable() {
-    let mut x = 0u32;
-    foo(|| x = 2);
-}
-
-// Attempts to take a mutable reference to closed-over data.  Error message
-// reads: `cannot borrow data mutably in a captured outer variable...`
-fn mut_addr() {
-    let mut x = 0u32;
-    foo(|| { let y = &mut x; });
-}
-```
-
-The problem here is that foo is defined as accepting a parameter of type `Fn`.
-Closures passed into foo will thus be inferred to be of type `Fn`, meaning that
-they capture their context immutably.
-
-If the definition of `foo` is under your control, the simplest solution is to
-capture the data mutably. This can be done by defining `foo` to take FnMut
-rather than Fn:
-
-```
-fn foo<F: FnMut()>(f: F) { }
-```
-
-Alternatively, we can consider using the `Cell` and `RefCell` types to achieve
-interior mutability through a shared reference. Our example's `mutable`
-function could be redefined as below:
-
-```
-use std::cell::Cell;
-
-fn foo<F: Fn()>(f: F) { }
-
-fn mutable() {
-    let x = Cell::new(0u32);
-    foo(|| x.set(2));
-}
-```
-
-You can read more about cell types in the API documentation:
-
-https://doc.rust-lang.org/std/cell/
-"##,
-
-E0388: r##"
-E0388 was removed and is no longer issued.
-"##,
-
-E0389: r##"
-An attempt was made to mutate data using a non-mutable reference. This
-commonly occurs when attempting to assign to a non-mutable reference of a
-mutable reference (`&(&mut T)`).
-
-Example of erroneous code:
-
-```compile_fail,E0389
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let mut fancy = FancyNum{ num: 5 };
-    let fancy_ref = &(&mut fancy);
-    fancy_ref.num = 6; // error: cannot assign to data in a `&` reference
-    println!("{}", fancy_ref.num);
-}
-```
-
-Here, `&mut fancy` is mutable, but `&(&mut fancy)` is not. Creating an
-immutable reference to a value borrows it immutably. There can be multiple
-references of type `&(&mut T)` that point to the same value, so they must be
-immutable to prevent multiple mutable references to the same value.
-
-To fix this, either remove the outer reference:
-
-```
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let mut fancy = FancyNum{ num: 5 };
-
-    let fancy_ref = &mut fancy;
-    // `fancy_ref` is now &mut FancyNum, rather than &(&mut FancyNum)
-
-    fancy_ref.num = 6; // No error!
-
-    println!("{}", fancy_ref.num);
-}
-```
-
-Or make the outer reference mutable:
-
-```
-struct FancyNum {
-    num: u8
-}
-
-fn main() {
-    let mut fancy = FancyNum{ num: 5 };
-
-    let fancy_ref = &mut (&mut fancy);
-    // `fancy_ref` is now &mut(&mut FancyNum), rather than &(&mut FancyNum)
-
-    fancy_ref.num = 6; // No error!
-
-    println!("{}", fancy_ref.num);
-}
-```
-"##,
-
-E0595: r##"
-Closures cannot mutate immutable captured variables.
-
-Erroneous code example:
-
-```compile_fail,E0595
-let x = 3; // error: closure cannot assign to immutable local variable `x`
-let mut c = || { x += 1 };
-```
-
-Make the variable binding mutable:
-
-```
-let mut x = 3; // ok!
-let mut c = || { x += 1 };
-```
-"##,
-
-E0596: r##"
-This error occurs because you tried to mutably borrow a non-mutable variable.
-
-Example of erroneous code:
-
-```compile_fail,E0596
-let x = 1;
-let y = &mut x; // error: cannot borrow mutably
-```
-
-In here, `x` isn't mutable, so when we try to mutably borrow it in `y`, it
-fails. To fix this error, you need to make `x` mutable:
-
-```
-let mut x = 1;
-let y = &mut x; // ok!
-```
-"##,
-
-E0597: r##"
-This error occurs because a borrow was made inside a variable which has a
-greater lifetime than the borrowed one.
-
-Example of erroneous code:
-
-```compile_fail,E0597
-struct Foo<'a> {
-    x: Option<&'a u32>,
-}
-
-let mut x = Foo { x: None };
-let y = 0;
-x.x = Some(&y); // error: `y` does not live long enough
-```
-
-In here, `x` is created before `y` and therefore has a greater lifetime. Always
-keep in mind that values in a scope are dropped in the opposite order they are
-created. So to fix the previous example, just make the `y` lifetime greater than
-the `x`'s one:
-
-```
-struct Foo<'a> {
-    x: Option<&'a u32>,
-}
-
-let y = 0;
-let mut x = Foo { x: None };
-x.x = Some(&y);
-```
-"##,
-
-E0626: r##"
-This error occurs because a borrow in a generator persists across a
-yield point.
-
-```compile_fail,E0626
-# #![feature(generators, generator_trait)]
-# use std::ops::Generator;
-let mut b = || {
-    let a = &String::new(); // <-- This borrow...
-    yield (); // ...is still in scope here, when the yield occurs.
-    println!("{}", a);
-};
-b.resume();
-```
-
-At present, it is not permitted to have a yield that occurs while a
-borrow is still in scope. To resolve this error, the borrow must
-either be "contained" to a smaller scope that does not overlap the
-yield or else eliminated in another way. So, for example, we might
-resolve the previous example by removing the borrow and just storing
-the integer by value:
-
-```
-# #![feature(generators, generator_trait)]
-# use std::ops::Generator;
-let mut b = || {
-    let a = 3;
-    yield ();
-    println!("{}", a);
-};
-b.resume();
-```
-
-This is a very simple case, of course. In more complex cases, we may
-wish to have more than one reference to the value that was borrowed --
-in those cases, something like the `Rc` or `Arc` types may be useful.
-
-This error also frequently arises with iteration:
-
-```compile_fail,E0626
-# #![feature(generators, generator_trait)]
-# use std::ops::Generator;
-let mut b = || {
-  let v = vec![1,2,3];
-  for &x in &v { // <-- borrow of `v` is still in scope...
-    yield x; // ...when this yield occurs.
-  }
-};
-b.resume();
-```
-
-Such cases can sometimes be resolved by iterating "by value" (or using
-`into_iter()`) to avoid borrowing:
-
-```
-# #![feature(generators, generator_trait)]
-# use std::ops::Generator;
-let mut b = || {
-  let v = vec![1,2,3];
-  for x in v { // <-- Take ownership of the values instead!
-    yield x; // <-- Now yield is OK.
-  }
-};
-b.resume();
-```
-
-If taking ownership is not an option, using indices can work too:
-
-```
-# #![feature(generators, generator_trait)]
-# use std::ops::Generator;
-let mut b = || {
-  let v = vec![1,2,3];
-  let len = v.len(); // (*)
-  for i in 0..len {
-    let x = v[i]; // (*)
-    yield x; // <-- Now yield is OK.
-  }
-};
-b.resume();
-
-// (*) -- Unfortunately, these temporaries are currently required.
-// See <https://github.com/rust-lang/rust/issues/43122>.
-```
-"##,
-
-}
-
-register_diagnostics! {
-//    E0385, // {} in an aliasable location
-    E0598, // lifetime of {} is too short to guarantee its contents can be...
-}
diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs
index 9bedbfed5db..11120d2e46f 100644
--- a/src/librustc_borrowck/lib.rs
+++ b/src/librustc_borrowck/lib.rs
@@ -16,10 +16,9 @@
 #![allow(non_camel_case_types)]
 
 #![feature(quote)]
-#![feature(rustc_diagnostic_macros)]
 
 #[macro_use] extern crate log;
-#[macro_use] extern crate syntax;
+extern crate syntax;
 extern crate syntax_pos;
 extern crate rustc_errors as errors;
 
@@ -33,14 +32,8 @@ extern crate rustc_mir;
 pub use borrowck::check_crate;
 pub use borrowck::build_borrowck_dataflow_data_for_fn;
 
-// NB: This module needs to be declared first so diagnostics are
-// registered before they are used.
-mod diagnostics;
-
 mod borrowck;
 
 pub mod graphviz;
 
 pub use borrowck::provide;
-
-__build_diagnostic_array! { librustc_borrowck, DIAGNOSTICS }
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index cd21060aff6..3514302c6c8 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -1259,7 +1259,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry {
     let mut all_errors = Vec::new();
     all_errors.extend_from_slice(&rustc::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS);
-    all_errors.extend_from_slice(&rustc_borrowck::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS);
     all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS);
     #[cfg(feature="llvm")]
diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs
index 645af0bff64..98c5345c69d 100644
--- a/src/librustc_mir/diagnostics.rs
+++ b/src/librustc_mir/diagnostics.rs
@@ -229,6 +229,57 @@ fn main() {
 See also https://doc.rust-lang.org/book/first-edition/unsafe.html
 "##,
 
+E0373: r##"
+This error occurs when an attempt is made to use data captured by a closure,
+when that data may no longer exist. It's most commonly seen when attempting to
+return a closure:
+
+```compile_fail,E0373
+fn foo() -> Box<Fn(u32) -> u32> {
+    let x = 0u32;
+    Box::new(|y| x + y)
+}
+```
+
+Notice that `x` is stack-allocated by `foo()`. By default, Rust captures
+closed-over data by reference. This means that once `foo()` returns, `x` no
+longer exists. An attempt to access `x` within the closure would thus be
+unsafe.
+
+Another situation where this might be encountered is when spawning threads:
+
+```compile_fail,E0373
+fn foo() {
+    let x = 0u32;
+    let y = 1u32;
+
+    let thr = std::thread::spawn(|| {
+        x + y
+    });
+}
+```
+
+Since our new thread runs in parallel, the stack frame containing `x` and `y`
+may well have disappeared by the time we try to use them. Even if we call
+`thr.join()` within foo (which blocks until `thr` has completed, ensuring the
+stack frame won't disappear), we will not succeed: the compiler cannot prove
+that this behaviour is safe, and so won't let us do it.
+
+The solution to this problem is usually to switch to using a `move` closure.
+This approach moves (or copies, where possible) data into the closure, rather
+than taking references to it. For example:
+
+```
+fn foo() -> Box<Fn(u32) -> u32> {
+    let x = 0u32;
+    Box::new(move |y| x + y)
+}
+```
+
+Now that the closure has its own copy of the data, there's no need to worry
+about safety.
+"##,
+
 E0381: r##"
 It is not allowed to use or capture an uninitialized variable. For example:
 
@@ -250,6 +301,104 @@ fn main() {
 ```
 "##,
 
+E0382: r##"
+This error occurs when an attempt is made to use a variable after its contents
+have been moved elsewhere. For example:
+
+```compile_fail,E0382
+struct MyStruct { s: u32 }
+
+fn main() {
+    let mut x = MyStruct{ s: 5u32 };
+    let y = x;
+    x.s = 6;
+    println!("{}", x.s);
+}
+```
+
+Since `MyStruct` is a type that is not marked `Copy`, the data gets moved out
+of `x` when we set `y`. This is fundamental to Rust's ownership system: outside
+of workarounds like `Rc`, a value cannot be owned by more than one variable.
+
+If we own the type, the easiest way to address this problem is to implement
+`Copy` and `Clone` on it, as shown below. This allows `y` to copy the
+information in `x`, while leaving the original version owned by `x`. Subsequent
+changes to `x` will not be reflected when accessing `y`.
+
+```
+#[derive(Copy, Clone)]
+struct MyStruct { s: u32 }
+
+fn main() {
+    let mut x = MyStruct{ s: 5u32 };
+    let y = x;
+    x.s = 6;
+    println!("{}", x.s);
+}
+```
+
+Alternatively, if we don't control the struct's definition, or mutable shared
+ownership is truly required, we can use `Rc` and `RefCell`:
+
+```
+use std::cell::RefCell;
+use std::rc::Rc;
+
+struct MyStruct { s: u32 }
+
+fn main() {
+    let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 }));
+    let y = x.clone();
+    x.borrow_mut().s = 6;
+    println!("{}", x.borrow().s);
+}
+```
+
+With this approach, x and y share ownership of the data via the `Rc` (reference
+count type). `RefCell` essentially performs runtime borrow checking: ensuring
+that at most one writer or multiple readers can access the data at any one time.
+
+If you wish to learn more about ownership in Rust, start with the chapter in the
+Book:
+
+https://doc.rust-lang.org/book/first-edition/ownership.html
+"##,
+
+E0383: r##"
+This error occurs when an attempt is made to partially reinitialize a
+structure that is currently uninitialized.
+
+For example, this can happen when a drop has taken place:
+
+```compile_fail,E0383
+struct Foo {
+    a: u32,
+}
+impl Drop for Foo {
+    fn drop(&mut self) { /* ... */ }
+}
+
+let mut x = Foo { a: 1 };
+drop(x); // `x` is now uninitialized
+x.a = 2; // error, partial reinitialization of uninitialized structure `t`
+```
+
+This error can be fixed by fully reinitializing the structure in question:
+
+```
+struct Foo {
+    a: u32,
+}
+impl Drop for Foo {
+    fn drop(&mut self) { /* ... */ }
+}
+
+let mut x = Foo { a: 1 };
+drop(x);
+x = Foo { a: 2 };
+```
+"##,
+
 E0384: r##"
 This error occurs when an attempt is made to reassign an immutable variable.
 For example:
@@ -272,6 +421,161 @@ fn main() {
 ```
 "##,
 
+/*E0386: r##"
+This error occurs when an attempt is made to mutate the target of a mutable
+reference stored inside an immutable container.
+
+For example, this can happen when storing a `&mut` inside an immutable `Box`:
+
+```compile_fail,E0386
+let mut x: i64 = 1;
+let y: Box<_> = Box::new(&mut x);
+**y = 2; // error, cannot assign to data in an immutable container
+```
+
+This error can be fixed by making the container mutable:
+
+```
+let mut x: i64 = 1;
+let mut y: Box<_> = Box::new(&mut x);
+**y = 2;
+```
+
+It can also be fixed by using a type with interior mutability, such as `Cell`
+or `RefCell`:
+
+```
+use std::cell::Cell;
+
+let x: i64 = 1;
+let y: Box<Cell<_>> = Box::new(Cell::new(x));
+y.set(2);
+```
+"##,*/
+
+E0387: r##"
+This error occurs when an attempt is made to mutate or mutably reference data
+that a closure has captured immutably. Examples of this error are shown below:
+
+```compile_fail,E0387
+// Accepts a function or a closure that captures its environment immutably.
+// Closures passed to foo will not be able to mutate their closed-over state.
+fn foo<F: Fn()>(f: F) { }
+
+// Attempts to mutate closed-over data. Error message reads:
+// `cannot assign to data in a captured outer variable...`
+fn mutable() {
+    let mut x = 0u32;
+    foo(|| x = 2);
+}
+
+// Attempts to take a mutable reference to closed-over data.  Error message
+// reads: `cannot borrow data mutably in a captured outer variable...`
+fn mut_addr() {
+    let mut x = 0u32;
+    foo(|| { let y = &mut x; });
+}
+```
+
+The problem here is that foo is defined as accepting a parameter of type `Fn`.
+Closures passed into foo will thus be inferred to be of type `Fn`, meaning that
+they capture their context immutably.
+
+If the definition of `foo` is under your control, the simplest solution is to
+capture the data mutably. This can be done by defining `foo` to take FnMut
+rather than Fn:
+
+```
+fn foo<F: FnMut()>(f: F) { }
+```
+
+Alternatively, we can consider using the `Cell` and `RefCell` types to achieve
+interior mutability through a shared reference. Our example's `mutable`
+function could be redefined as below:
+
+```
+use std::cell::Cell;
+
+fn foo<F: Fn()>(f: F) { }
+
+fn mutable() {
+    let x = Cell::new(0u32);
+    foo(|| x.set(2));
+}
+```
+
+You can read more about cell types in the API documentation:
+
+https://doc.rust-lang.org/std/cell/
+"##,
+
+E0388: r##"
+E0388 was removed and is no longer issued.
+"##,
+
+E0389: r##"
+An attempt was made to mutate data using a non-mutable reference. This
+commonly occurs when attempting to assign to a non-mutable reference of a
+mutable reference (`&(&mut T)`).
+
+Example of erroneous code:
+
+```compile_fail,E0389
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let mut fancy = FancyNum{ num: 5 };
+    let fancy_ref = &(&mut fancy);
+    fancy_ref.num = 6; // error: cannot assign to data in a `&` reference
+    println!("{}", fancy_ref.num);
+}
+```
+
+Here, `&mut fancy` is mutable, but `&(&mut fancy)` is not. Creating an
+immutable reference to a value borrows it immutably. There can be multiple
+references of type `&(&mut T)` that point to the same value, so they must be
+immutable to prevent multiple mutable references to the same value.
+
+To fix this, either remove the outer reference:
+
+```
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let mut fancy = FancyNum{ num: 5 };
+
+    let fancy_ref = &mut fancy;
+    // `fancy_ref` is now &mut FancyNum, rather than &(&mut FancyNum)
+
+    fancy_ref.num = 6; // No error!
+
+    println!("{}", fancy_ref.num);
+}
+```
+
+Or make the outer reference mutable:
+
+```
+struct FancyNum {
+    num: u8
+}
+
+fn main() {
+    let mut fancy = FancyNum{ num: 5 };
+
+    let fancy_ref = &mut (&mut fancy);
+    // `fancy_ref` is now &mut(&mut FancyNum), rather than &(&mut FancyNum)
+
+    fancy_ref.num = 6; // No error!
+
+    println!("{}", fancy_ref.num);
+}
+```
+"##,
 
 E0394: r##"
 A static was referred to by value by another static.
@@ -1265,12 +1569,169 @@ fn main() {
 ```
 "##,
 
+E0595: r##"
+Closures cannot mutate immutable captured variables.
+
+Erroneous code example:
+
+```compile_fail,E0595
+let x = 3; // error: closure cannot assign to immutable local variable `x`
+let mut c = || { x += 1 };
+```
+
+Make the variable binding mutable:
+
+```
+let mut x = 3; // ok!
+let mut c = || { x += 1 };
+```
+"##,
+
+E0596: r##"
+This error occurs because you tried to mutably borrow a non-mutable variable.
+
+Example of erroneous code:
+
+```compile_fail,E0596
+let x = 1;
+let y = &mut x; // error: cannot borrow mutably
+```
+
+In here, `x` isn't mutable, so when we try to mutably borrow it in `y`, it
+fails. To fix this error, you need to make `x` mutable:
+
+```
+let mut x = 1;
+let y = &mut x; // ok!
+```
+"##,
+
+E0597: r##"
+This error occurs because a borrow was made inside a variable which has a
+greater lifetime than the borrowed one.
+
+Example of erroneous code:
+
+```compile_fail,E0597
+struct Foo<'a> {
+    x: Option<&'a u32>,
+}
+
+let mut x = Foo { x: None };
+let y = 0;
+x.x = Some(&y); // error: `y` does not live long enough
+```
+
+In here, `x` is created before `y` and therefore has a greater lifetime. Always
+keep in mind that values in a scope are dropped in the opposite order they are
+created. So to fix the previous example, just make the `y` lifetime greater than
+the `x`'s one:
+
+```
+struct Foo<'a> {
+    x: Option<&'a u32>,
+}
+
+let y = 0;
+let mut x = Foo { x: None };
+x.x = Some(&y);
+```
+"##,
+
+E0626: r##"
+This error occurs because a borrow in a generator persists across a
+yield point.
+
+```compile_fail,E0626
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+    let a = &String::new(); // <-- This borrow...
+    yield (); // ...is still in scope here, when the yield occurs.
+    println!("{}", a);
+};
+b.resume();
+```
+
+At present, it is not permitted to have a yield that occurs while a
+borrow is still in scope. To resolve this error, the borrow must
+either be "contained" to a smaller scope that does not overlap the
+yield or else eliminated in another way. So, for example, we might
+resolve the previous example by removing the borrow and just storing
+the integer by value:
+
+```
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+    let a = 3;
+    yield ();
+    println!("{}", a);
+};
+b.resume();
+```
+
+This is a very simple case, of course. In more complex cases, we may
+wish to have more than one reference to the value that was borrowed --
+in those cases, something like the `Rc` or `Arc` types may be useful.
+
+This error also frequently arises with iteration:
+
+```compile_fail,E0626
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+  let v = vec![1,2,3];
+  for &x in &v { // <-- borrow of `v` is still in scope...
+    yield x; // ...when this yield occurs.
+  }
+};
+b.resume();
+```
+
+Such cases can sometimes be resolved by iterating "by value" (or using
+`into_iter()`) to avoid borrowing:
+
+```
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+  let v = vec![1,2,3];
+  for x in v { // <-- Take ownership of the values instead!
+    yield x; // <-- Now yield is OK.
+  }
+};
+b.resume();
+```
+
+If taking ownership is not an option, using indices can work too:
+
+```
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+  let v = vec![1,2,3];
+  let len = v.len(); // (*)
+  for i in 0..len {
+    let x = v[i]; // (*)
+    yield x; // <-- Now yield is OK.
+  }
+};
+b.resume();
+
+// (*) -- Unfortunately, these temporaries are currently required.
+// See <https://github.com/rust-lang/rust/issues/43122>.
+```
+"##,
+
 }
 
 register_diagnostics! {
+//    E0385, // {} in an aliasable location
     E0493, // destructors cannot be evaluated at compile-time
     E0524, // two closures require unique access to `..` at the same time
     E0526, // shuffle indices are not constant
     E0594, // cannot assign to {}
+    E0598, // lifetime of {} is too short to guarantee its contents can be...
     E0625, // thread-local statics cannot be accessed at compile-time
 }
diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs
new file mode 100644
index 00000000000..e6e00f295ca
--- /dev/null
+++ b/src/librustc_mir/transform/nll/infer.rs
@@ -0,0 +1,222 @@
+// 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.
+
+use super::{Region, RegionIndex};
+use std::mem;
+use rustc::infer::InferCtxt;
+use rustc::mir::{Location, Mir};
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::fx::FxHashSet;
+
+pub struct InferenceContext {
+    definitions: IndexVec<RegionIndex, VarDefinition>,
+    constraints: IndexVec<ConstraintIndex, Constraint>,
+    errors: IndexVec<InferenceErrorIndex, InferenceError>,
+}
+
+pub struct InferenceError {
+    pub constraint_point: Location,
+    pub name: (), // FIXME(nashenas88) RegionName
+}
+
+newtype_index!(InferenceErrorIndex);
+
+struct VarDefinition {
+    name: (), // FIXME(nashenas88) RegionName
+    value: Region,
+    capped: bool,
+}
+
+impl VarDefinition {
+    pub fn new(value: Region) -> Self {
+        Self {
+            name: (),
+            value,
+            capped: false,
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Constraint {
+    sub: RegionIndex,
+    sup: RegionIndex,
+    point: Location,
+}
+
+newtype_index!(ConstraintIndex);
+
+impl InferenceContext {
+    pub fn new(values: IndexVec<RegionIndex, Region>) -> Self {
+        Self {
+            definitions: values.into_iter().map(VarDefinition::new).collect(),
+            constraints: IndexVec::new(),
+            errors: IndexVec::new(),
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn cap_var(&mut self, v: RegionIndex) {
+        self.definitions[v].capped = true;
+    }
+
+    #[allow(dead_code)]
+    pub fn add_live_point(&mut self, v: RegionIndex, point: Location) {
+        debug!("add_live_point({:?}, {:?})", v, point);
+        let definition = &mut self.definitions[v];
+        if definition.value.add_point(point) {
+            if definition.capped {
+                self.errors.push(InferenceError {
+                    constraint_point: point,
+                    name: definition.name,
+                });
+            }
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
+        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
+        self.constraints.push(Constraint { sup, sub, point });
+    }
+
+    #[allow(dead_code)]
+    pub fn region(&self, v: RegionIndex) -> &Region {
+        &self.definitions[v].value
+    }
+
+    pub fn solve<'a, 'gcx, 'tcx>(
+        &mut self,
+        infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+        mir: &'a Mir<'tcx>,
+    ) -> IndexVec<InferenceErrorIndex, InferenceError>
+    where
+        'gcx: 'tcx + 'a,
+        'tcx: 'a,
+    {
+        let mut changed = true;
+        let mut dfs = Dfs::new(infcx, mir);
+        while changed {
+            changed = false;
+            for constraint in &self.constraints {
+                let sub = &self.definitions[constraint.sub].value.clone();
+                let sup_def = &mut self.definitions[constraint.sup];
+                debug!("constraint: {:?}", constraint);
+                debug!("    sub (before): {:?}", sub);
+                debug!("    sup (before): {:?}", sup_def.value);
+
+                if dfs.copy(sub, &mut sup_def.value, constraint.point) {
+                    changed = true;
+                    if sup_def.capped {
+                        // This is kind of a hack, but when we add a
+                        // constraint, the "point" is always the point
+                        // AFTER the action that induced the
+                        // constraint. So report the error on the
+                        // action BEFORE that.
+                        assert!(constraint.point.statement_index > 0);
+                        let p = Location {
+                            block: constraint.point.block,
+                            statement_index: constraint.point.statement_index - 1,
+                        };
+
+                        self.errors.push(InferenceError {
+                            constraint_point: p,
+                            name: sup_def.name,
+                        });
+                    }
+                }
+
+                debug!("    sup (after) : {:?}", sup_def.value);
+                debug!("    changed     : {:?}", changed);
+            }
+            debug!("\n");
+        }
+
+        mem::replace(&mut self.errors, IndexVec::new())
+    }
+}
+
+struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
+    #[allow(dead_code)]
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
+    fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        Self { infcx, mir }
+    }
+
+    fn copy(
+        &mut self,
+        from_region: &Region,
+        to_region: &mut Region,
+        start_point: Location,
+    ) -> bool {
+        let mut changed = false;
+
+        let mut stack = vec![];
+        let mut visited = FxHashSet();
+
+        stack.push(start_point);
+        while let Some(p) = stack.pop() {
+            debug!("        dfs: p={:?}", p);
+
+            if !from_region.may_contain(p) {
+                debug!("            not in from-region");
+                continue;
+            }
+
+            if !visited.insert(p) {
+                debug!("            already visited");
+                continue;
+            }
+
+            changed |= to_region.add_point(p);
+
+            let block_data = &self.mir[p.block];
+            let successor_points = if p.statement_index < block_data.statements.len() {
+                vec![Location {
+                    statement_index: p.statement_index + 1,
+                    ..p
+                }]
+            } else {
+                block_data.terminator()
+                    .successors()
+                    .iter()
+                    .map(|&basic_block| Location {
+                        statement_index: 0,
+                        block: basic_block,
+                    })
+                    .collect::<Vec<_>>()
+            };
+
+            if successor_points.is_empty() {
+                // FIXME handle free regions
+                // If we reach the END point in the graph, then copy
+                // over any skolemized end points in the `from_region`
+                // and make sure they are included in the `to_region`.
+                // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
+                //     // FIXME(nashenas88) figure out skolemized_end points
+                //     let block = self.env.graph.skolemized_end(region_decl.name);
+                //     let skolemized_end_point = Location {
+                //         block,
+                //         statement_index: 0,
+                //     };
+                //     changed |= to_region.add_point(skolemized_end_point);
+                // }
+            } else {
+                stack.extend(successor_points);
+            }
+        }
+
+        changed
+    }
+}
diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs
index 4925b1fcfed..805e9c976e4 100644
--- a/src/librustc_mir/transform/nll/mod.rs
+++ b/src/librustc_mir/transform/nll/mod.rs
@@ -8,13 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use self::infer::InferenceContext;
 use rustc::ty::TypeFoldable;
 use rustc::ty::subst::{Kind, Substs};
 use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind};
 use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
 use rustc::mir::visit::{MutVisitor, Lookup};
 use rustc::mir::transform::{MirPass, MirSource};
-use rustc::infer::{self, InferCtxt};
+use rustc::infer::{self as rustc_infer, InferCtxt};
 use rustc::util::nodemap::FxHashSet;
 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use syntax_pos::DUMMY_SP;
@@ -24,15 +25,18 @@ use std::fmt;
 use util as mir_util;
 use self::mir_util::PassWhere;
 
+mod infer;
+
 #[allow(dead_code)]
 struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     lookup_map: HashMap<RegionVid, Lookup>,
     regions: IndexVec<RegionIndex, Region>,
-    infcx: InferCtxt<'a, 'gcx, 'tcx>,
+    #[allow(dead_code)]
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
-    pub fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>) -> Self {
+    pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
         NLLVisitor {
             infcx,
             lookup_map: HashMap::new(),
@@ -40,14 +44,14 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn into_results(self) -> HashMap<RegionVid, Lookup> {
-        self.lookup_map
+    pub fn into_results(self) -> (HashMap<RegionVid, Lookup>, IndexVec<RegionIndex, Region>) {
+        (self.lookup_map, self.regions)
     }
 
     fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
         self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
             self.regions.push(Region::default());
-            self.infcx.next_region_var(infer::MiscVariable(DUMMY_SP))
+            self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
         })
     }
 
@@ -147,7 +151,7 @@ impl MirPass for NLL {
         tcx.infer_ctxt().enter(|infcx| {
             // Clone mir so we can mutate it without disturbing the rest of the compiler
             let mut renumbered_mir = mir.clone();
-            let mut visitor = NLLVisitor::new(infcx);
+            let mut visitor = NLLVisitor::new(&infcx);
             visitor.visit_mir(&mut renumbered_mir);
             mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| {
                 if let PassWhere::BeforeCFG = pass_where {
@@ -157,13 +161,15 @@ impl MirPass for NLL {
                 }
                 Ok(())
             });
-            let _results = visitor.into_results();
+            let (_lookup_map, regions) = visitor.into_results();
+            let mut inference_context = InferenceContext::new(regions);
+            inference_context.solve(&infcx, &renumbered_mir);
         })
     }
 }
 
 #[derive(Clone, Default, PartialEq, Eq)]
-struct Region {
+pub struct Region {
     points: FxHashSet<Location>,
 }
 
@@ -173,6 +179,14 @@ impl fmt::Debug for Region {
     }
 }
 
+impl Region {
+    pub fn add_point(&mut self, point: Location) -> bool {
+        self.points.insert(point)
+    }
 
+    pub fn may_contain(&self, point: Location) -> bool {
+        self.points.contains(&point)
+    }
+}
 
 newtype_index!(RegionIndex);
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
index 37d53ca829e..216f6e44570 100644
--- a/src/librustc_mir/util/borrowck_errors.rs
+++ b/src/librustc_mir/util/borrowck_errors.rs
@@ -294,6 +294,139 @@ pub trait BorrowckErrors {
         err.span_label(move_from_span, "cannot move out of here");
         err
     }
+
+    fn cannot_act_on_moved_value(&self,
+                                 use_span: Span,
+                                 verb: &str,
+                                 optional_adverb_for_moved: &str,
+                                 moved_path: &str,
+                                 o: Origin)
+                                 -> DiagnosticBuilder
+    {
+        let err = struct_span_err!(self, use_span, E0382,
+                                   "{} of {}moved value: `{}`{OGN}",
+                                   verb, optional_adverb_for_moved, moved_path, OGN=o);
+        err
+    }
+
+    fn cannot_partially_reinit_an_uninit_struct(&self,
+                                                span: Span,
+                                                uninit_path: &str,
+                                                o: Origin)
+                                                -> DiagnosticBuilder
+    {
+        let err = struct_span_err!(self,
+                                   span,
+                                   E0383,
+                                   "partial reinitialization of uninitialized structure `{}`{OGN}",
+                                   uninit_path, OGN=o);
+        err
+    }
+
+    fn closure_cannot_assign_to_borrowed(&self,
+                                         span: Span,
+                                         descr: &str,
+                                         o: Origin)
+                                         -> DiagnosticBuilder
+    {
+        let err = struct_span_err!(self, span, E0595, "closure cannot assign to {}{OGN}",
+                                   descr, OGN=o);
+        err
+    }
+
+    fn cannot_borrow_path_as_mutable(&self,
+                                     span: Span,
+                                     path: &str,
+                                     o: Origin)
+                                     -> DiagnosticBuilder
+    {
+        let err = struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{OGN}",
+                                   path, OGN=o);
+        err
+    }
+
+    fn cannot_borrow_across_generator_yield(&self,
+                                            span: Span,
+                                            yield_span: Span,
+                                            o: Origin)
+                                            -> DiagnosticBuilder
+    {
+        let mut err = struct_span_err!(self,
+                                       span,
+                                       E0626,
+                                       "borrow may still be in use when generator yields{OGN}",
+                                       OGN=o);
+        err.span_label(yield_span, "possible yield occurs here");
+        err
+    }
+
+    fn path_does_not_live_long_enough(&self,
+                                      span: Span,
+                                      path: &str,
+                                      o: Origin)
+                                      -> DiagnosticBuilder
+    {
+        let err = struct_span_err!(self, span, E0597, "{} does not live long enough{OGN}",
+                                   path, OGN=o);
+        err
+    }
+
+    fn lifetime_too_short_for_reborrow(&self,
+                                       span: Span,
+                                       path: &str,
+                                       o: Origin)
+                                       -> DiagnosticBuilder
+    {
+        let err = struct_span_err!(self, span, E0598,
+                                   "lifetime of {} is too short to guarantee \
+                                    its contents can be safely reborrowed{OGN}",
+                                   path, OGN=o);
+        err
+    }
+
+    fn cannot_act_on_capture_in_sharable_fn(&self,
+                                            span: Span,
+                                            bad_thing: &str,
+                                            help: (Span, &str),
+                                            o: Origin)
+                                            -> DiagnosticBuilder
+    {
+        let (help_span, help_msg) = help;
+        let mut err = struct_span_err!(self, span, E0387,
+                                       "{} in a captured outer variable in an `Fn` closure{OGN}",
+                                       bad_thing, OGN=o);
+        err.span_help(help_span, help_msg);
+        err
+    }
+
+    fn cannot_assign_into_immutable_reference(&self,
+                                              span: Span,
+                                              bad_thing: &str,
+                                              o: Origin)
+                                              -> DiagnosticBuilder
+    {
+        let mut err = struct_span_err!(self, span, E0389, "{} in a `&` reference{OGN}",
+                                       bad_thing, OGN=o);
+        err.span_label(span, "assignment into an immutable reference");
+        err
+    }
+
+    fn cannot_capture_in_long_lived_closure(&self,
+                                            closure_span: Span,
+                                            borrowed_path: &str,
+                                            capture_span: Span,
+                                            o: Origin)
+                                            -> DiagnosticBuilder
+    {
+        let mut err = struct_span_err!(self, closure_span, E0373,
+                                       "closure may outlive the current function, \
+                                        but it borrows {}, \
+                                        which is owned by the current function{OGN}",
+                                       borrowed_path, OGN=o);
+        err.span_label(capture_span, format!("{} is borrowed here", borrowed_path))
+            .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path));
+        err
+    }
 }
 
 impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {
diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md
index d999ff97551..b00b35aa29f 100644
--- a/src/test/mir-opt/README.md
+++ b/src/test/mir-opt/README.md
@@ -7,13 +7,13 @@ The test format is:
 // END RUST SOURCE
 // START $file_name_of_some_mir_dump_0
 //  $expected_line_0
-// ...
+// (lines or elision)
 // $expected_line_N
 // END $file_name_of_some_mir_dump_0
-// ...
+// (lines or elision)
 // START $file_name_of_some_mir_dump_N
 //  $expected_line_0
-// ...
+// (lines or elision)
 // $expected_line_N
 // END $file_name_of_some_mir_dump_N
 ```
@@ -22,10 +22,15 @@ 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. 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:
+other non-matched lines before and after, but not between $expected_lines,
+should you want to skip lines, you must include an elision comment, of the form
+(as a regex) `//\s*...\s*`. The lines will be skipped lazily, that is, if there
+are two identical lines in the output that match the line after the elision
+comment, the first one wil be matched.
+
+Examples:
+
+The following blocks will not match the one after it.
 
 ```
 bb0: {
@@ -35,8 +40,6 @@ bb0: {
 }
 ```
 
-that will inadvertantly pattern-matching against:
-
 ```
 bb0: {
     StorageLive(_1);
@@ -49,6 +52,18 @@ bb1: {
 }
 ```
 
+But this will match the one above,
+
+```
+bb0: {
+    StorageLive(_1);
+    _1 = const true;
+    ...
+    StorageDead(_1);
+    ...
+}
+```
+
 Lines match ignoring whitespace, and the prefix "//" is removed.
 
 It also currently strips trailing comments -- partly because the full file path
diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs
index 4015930ef76..5c3b418e950 100644
--- a/src/test/mir-opt/box_expr.rs
+++ b/src/test/mir-opt/box_expr.rs
@@ -30,7 +30,10 @@ impl Drop for S {
 // END RUST SOURCE
 // START rustc.node4.ElaborateDrops.before.mir
 //     let mut _0: ();
-//     let _1: std::boxed::Box<S>;
+//     scope 1 {
+//         let _1: std::boxed::Box<S>;
+//     }
+//     ...
 //     let mut _2: std::boxed::Box<S>;
 //     let mut _3: ();
 //     let mut _4: std::boxed::Box<S>;
diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs
index 26b042d0343..0b0d2f45f1c 100644
--- a/src/test/mir-opt/copy_propagation.rs
+++ b/src/test/mir-opt/copy_propagation.rs
@@ -18,17 +18,23 @@ fn main() { }
 // END RUST SOURCE
 // START rustc.node4.CopyPropagation.before.mir
 //  bb0: {
+//      ...
 //      _2 = _1;
+//      ...
 //      _4 = _2;
 //      _3 = _4;
+//      ...
 //      _5 = _3;
 //      _0 = _5;
+//      ...
 //      return;
 //  }
 // END rustc.node4.CopyPropagation.before.mir
 // START rustc.node4.CopyPropagation.after.mir
 //  bb0: {
+//      ...
 //      _0 = _1;
+//      ...
 //      return;
 //  }
 // END rustc.node4.CopyPropagation.after.mir
diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs
index 81dd1932894..ce2b13ecda7 100644
--- a/src/test/mir-opt/deaggregator_test.rs
+++ b/src/test/mir-opt/deaggregator_test.rs
@@ -23,19 +23,25 @@ fn main() {}
 // END RUST SOURCE
 // START rustc.node13.Deaggregator.before.mir
 // bb0: {
+//     ...
 //     _2 = _1;
+//     ...
 //     _3 = _2;
 //     _0 = Baz { x: _3, y: const 0f32, z: const false };
+//     ...
 //     return;
 // }
 // END rustc.node13.Deaggregator.before.mir
 // START rustc.node13.Deaggregator.after.mir
 // bb0: {
+//     ...
 //     _2 = _1;
+//     ...
 //     _3 = _2;
 //     (_0.0: usize) = _3;
 //     (_0.1: f32) = const 0f32;
 //     (_0.2: bool) = const false;
+//     ...
 //     return;
 // }
 // END rustc.node13.Deaggregator.after.mir
diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs
index 25fa0e90835..d77dcb62781 100644
--- a/src/test/mir-opt/deaggregator_test_enum.rs
+++ b/src/test/mir-opt/deaggregator_test_enum.rs
@@ -28,18 +28,26 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node10.Deaggregator.before.mir
 // bb0: {
+//     StorageLive(_2);
 //     _2 = _1;
+//     StorageLive(_3);
 //     _3 = _2;
 //     _0 = Baz::Foo { x: _3 };
+//     StorageDead(_3);
+//     StorageDead(_2);
 //     return;
 // }
 // END rustc.node10.Deaggregator.before.mir
 // START rustc.node10.Deaggregator.after.mir
 // bb0: {
+//     StorageLive(_2);
 //     _2 = _1;
+//     StorageLive(_3);
 //     _3 = _2;
 //     ((_0 as Foo).0: usize) = _3;
 //     discriminant(_0) = 1;
+//     StorageDead(_3);
+//     StorageDead(_2);
 //     return;
 // }
 // END rustc.node10.Deaggregator.after.mir
diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs
index 02d496b2901..e65830bddc4 100644
--- a/src/test/mir-opt/deaggregator_test_enum_2.rs
+++ b/src/test/mir-opt/deaggregator_test_enum_2.rs
@@ -28,29 +28,35 @@ fn main() {}
 // END RUST SOURCE
 // START rustc.node12.Deaggregator.before.mir
 //  bb1: {
+//      StorageLive(_6);
 //      _6 = _4;
 //      _0 = Foo::A(_6,);
+//      StorageDead(_6);
 //      goto -> bb3;
 //  }
-//
 //  bb2: {
+//      StorageLive(_7);
 //      _7 = _4;
 //      _0 = Foo::B(_7,);
+//      StorageDead(_7);
 //      goto -> bb3;
 //  }
 // END rustc.node12.Deaggregator.before.mir
 // START rustc.node12.Deaggregator.after.mir
 //  bb1: {
+//      StorageLive(_6);
 //      _6 = _4;
 //      ((_0 as A).0: i32) = _6;
 //      discriminant(_0) = 0;
+//      StorageDead(_6);
 //      goto -> bb3;
 //  }
-//
 //  bb2: {
+//      StorageLive(_7);
 //      _7 = _4;
 //      ((_0 as B).0: i32) = _7;
 //      discriminant(_0) = 1;
+//      StorageDead(_7);
 //      goto -> bb3;
 //  }
 // END rustc.node12.Deaggregator.after.mir
diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs
index a180a69be55..ed68d3bf5f7 100644
--- a/src/test/mir-opt/deaggregator_test_multiple.rs
+++ b/src/test/mir-opt/deaggregator_test_multiple.rs
@@ -24,25 +24,35 @@ fn main() { }
 // END RUST SOURCE
 // START rustc.node10.Deaggregator.before.mir
 // bb0: {
+//     ...
 //     _2 = _1;
+//     ...
 //     _4 = _2;
 //     _3 = Foo::A(_4,);
+//     ...
 //     _6 = _2;
 //     _5 = Foo::A(_6,);
+//     ...
 //     _0 = [_3, _5];
+//     ...
 //     return;
 // }
 // END rustc.node10.Deaggregator.before.mir
 // START rustc.node10.Deaggregator.after.mir
 // bb0: {
+//     ...
 //     _2 = _1;
+//     ...
 //     _4 = _2;
 //     ((_3 as A).0: i32) = _4;
 //     discriminant(_3) = 0;
+//     ...
 //     _6 = _2;
 //     ((_5 as A).0: i32) = _6;
 //     discriminant(_5) = 0;
+//     ...
 //     _0 = [_3, _5];
+//     ...
 //     return;
 // }
 // END rustc.node10.Deaggregator.after.mir
diff --git a/src/test/mir-opt/end_region_1.rs b/src/test/mir-opt/end_region_1.rs
index 1941d1bc7be..a0edcc82fe1 100644
--- a/src/test/mir-opt/end_region_1.rs
+++ b/src/test/mir-opt/end_region_1.rs
@@ -21,9 +21,11 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 //     let mut _0: ();
+//     ...
 //     let _1: i32;
+//     ...
 //     let _2: &'10_1rs i32;
-//
+//     ...
 //     bb0: {
 //         StorageLive(_1);
 //         _1 = const 3i32;
diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs
index d8dd4aeadf4..69042fecc7d 100644
--- a/src/test/mir-opt/end_region_2.rs
+++ b/src/test/mir-opt/end_region_2.rs
@@ -26,11 +26,16 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 //     let mut _0: ();
+//     ...
 //     let _2: bool;
+//     ...
 //     let _3: &'23_1rs bool;
+//     ...
 //     let _7: &'23_3rs bool;
+//     ...
 //     let mut _4: ();
 //     let mut _5: bool;
+//     ...
 //     bb0: {
 //         goto -> bb1;
 //     }
@@ -52,6 +57,7 @@ fn main() {
 //         return;
 //     }
 //     bb3: {
+//         _4 = ();
 //         StorageDead(_5);
 //         StorageLive(_7);
 //         _7 = &'23_3rs _2;
diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs
index e404af838ce..da423163e84 100644
--- a/src/test/mir-opt/end_region_3.rs
+++ b/src/test/mir-opt/end_region_3.rs
@@ -27,13 +27,17 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 //     let mut _0: ();
+//     ...
 //     let mut _1: bool;
+//     ...
 //     let _3: &'26_1rs bool;
+//     ...
 //     let _7: &'26_3rs bool;
+//     ...
 //     let mut _2: ();
 //     let mut _4: ();
 //     let mut _5: bool;
-//
+//     let mut _6: !;
 //     bb0: {
 //         StorageLive(_1);
 //         goto -> bb1;
diff --git a/src/test/mir-opt/end_region_4.rs b/src/test/mir-opt/end_region_4.rs
index d51c627d14b..2087485b913 100644
--- a/src/test/mir-opt/end_region_4.rs
+++ b/src/test/mir-opt/end_region_4.rs
@@ -31,10 +31,15 @@ fn foo(i: i32) {
 // END RUST SOURCE
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 //     let mut _0: ();
+//     ...
 //     let _1: D;
+//     ...
 //     let _2: i32;
+//     ...
 //     let _3: &'26_2rs i32;
+//     ...
 //     let _6: &'26_4rs i32;
+//     ...
 //     let mut _4: ();
 //     let mut _5: i32;
 //     bb0: {
diff --git a/src/test/mir-opt/end_region_5.rs b/src/test/mir-opt/end_region_5.rs
index 6299ec3815c..4663b71bd7c 100644
--- a/src/test/mir-opt/end_region_5.rs
+++ b/src/test/mir-opt/end_region_5.rs
@@ -28,8 +28,11 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // 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: [closure@NodeId(18) d:&'14s D];
 //     let mut _4: &'14s D;
diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs
index 13ab3e4f2dd..7d2868ee4ba 100644
--- a/src/test/mir-opt/end_region_6.rs
+++ b/src/test/mir-opt/end_region_6.rs
@@ -29,7 +29,9 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 // fn main() -> () {
 //     let mut _0: ();
+//     ...
 //     let _1: D;
+//     ...
 //     let mut _2: ();
 //     let mut _3: [closure@NodeId(22) d:&'19s D];
 //     let mut _4: &'19s D;
@@ -65,9 +67,10 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.node22.SimplifyCfg-qualify-consts.after.mir
 // fn main::{{closure}}(_1: [closure@NodeId(22) d:&'19s D]) -> i32 {
 //     let mut _0: i32;
+//     ...
 //     let _2: &'15_0rs D;
+//     ...
 //     let mut _3: i32;
-//
 //     bb0: {
 //         StorageLive(_2);
 //         _2 = &'15_0rs (*(_1.0: &'19s D));
diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs
index 826d3749167..0156c1be7ed 100644
--- a/src/test/mir-opt/end_region_7.rs
+++ b/src/test/mir-opt/end_region_7.rs
@@ -29,11 +29,12 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 // fn main() -> () {
 //     let mut _0: ();
+//     ...
 //     let _1: D;
+//     ...
 //     let mut _2: ();
 //     let mut _3: [closure@NodeId(22) d:D];
 //     let mut _4: D;
-//
 //     bb0: {
 //         StorageLive(_1);
 //         _1 = D::{{constructor}}(const 0i32,);
@@ -74,9 +75,10 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.node22.SimplifyCfg-qualify-consts.after.mir
 // fn main::{{closure}}(_1: [closure@NodeId(22) d:D]) -> i32 {
 //     let mut _0: i32;
+//     ...
 //     let _2: &'15_0rs D;
+//     ...
 //     let mut _3: i32;
-//
 //     bb0: {
 //         StorageLive(_2);
 //         _2 = &'15_0rs (_1.0: D);
diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs
index 6438484fcfa..6e8cf4204ee 100644
--- a/src/test/mir-opt/end_region_8.rs
+++ b/src/test/mir-opt/end_region_8.rs
@@ -30,8 +30,11 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 // fn main() -> () {
 //    let mut _0: ();
+//    ...
 //    let _1: D;
+//    ...
 //    let _2: &'21_1rs D;
+//    ...
 //    let mut _3: ();
 //    let mut _4: [closure@NodeId(22) r:&'21_1rs D];
 //    let mut _5: &'21_1rs D;
diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs
index 59d5d934391..fd23d813452 100644
--- a/src/test/mir-opt/end_region_9.rs
+++ b/src/test/mir-opt/end_region_9.rs
@@ -40,15 +40,18 @@ fn main() {
 // START rustc.node4.SimplifyCfg-qualify-consts.after.mir
 // fn main() -> () {
 //     let mut _0: ();
+//     ...
 //     let mut _1: bool;
+//     ...
 //     let _2: i32;
+//     ...
 //     let mut _4: &'33_0rs i32;
+//     ...
 //     let mut _3: ();
 //     let mut _5: !;
 //     let mut _6: ();
 //     let mut _7: bool;
 //     let mut _8: !;
-//
 //     bb0: {
 //        StorageLive(_1);
 //        _1 = const false;
@@ -63,7 +66,6 @@ fn main() {
 //        _7 = _1;
 //        switchInt(_7) -> [0u8: bb3, otherwise: bb2];
 //    }
-//
 //    bb2: {
 //        _0 = ();
 //        StorageDead(_7);
@@ -73,7 +75,6 @@ fn main() {
 //        StorageDead(_1);
 //        return;
 //    }
-//
 //    bb3: {
 //        _4 = &'33_0rs _2;
 //        _6 = ();
diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs
index 8f9dd79cd75..f70f6519275 100644
--- a/src/test/mir-opt/end_region_cyclic.rs
+++ b/src/test/mir-opt/end_region_cyclic.rs
@@ -45,6 +45,7 @@ fn query() -> bool { true }
 //     scope 1 {
 //         let _2: S<'35_0rs>;
 //     }
+//     ...
 //     let mut _1: ();
 //     let mut _3: std::cell::Cell<std::option::Option<&'35_0rs S<'35_0rs>>>;
 //     let mut _4: std::option::Option<&'35_0rs S<'35_0rs>>;
diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs
index 3a8b5c449c2..384201b7c12 100644
--- a/src/test/mir-opt/issue-41110.rs
+++ b/src/test/mir-opt/issue-41110.rs
@@ -35,22 +35,26 @@ impl S {
 // END RUST SOURCE
 // START rustc.node4.ElaborateDrops.after.mir
 //    let mut _0: ();
-//    let _1: ();
+//    scope 1 {
+//        let _1: ();
+//    }
+//    ...
 //    let mut _2: S;
 //    let mut _3: S;
 //    let mut _4: S;
 //    let mut _5: bool;
-//
 //    bb0: {
 // END rustc.node4.ElaborateDrops.after.mir
 // START rustc.node13.ElaborateDrops.after.mir
 //    let mut _0: ();
+//    ...
 //    let _1: S;
+//    ...
 //    let mut _2: S;
+//    ...
 //    let mut _3: ();
 //    let mut _4: S;
 //    let mut _5: S;
 //    let mut _6: bool;
-//
 //    bb0: {
 // END rustc.node13.ElaborateDrops.after.mir
diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs
index 9fb725a980e..370ab599eca 100644
--- a/src/test/mir-opt/storage_live_dead_in_statics.rs
+++ b/src/test/mir-opt/storage_live_dead_in_statics.rs
@@ -45,56 +45,156 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.node4.mir_map.0.mir
+//    let mut _0: &'static Foo;
+//    let mut _1: &'static Foo;
+//    let mut _2: Foo;
+//    let mut _3: &'static [(u32, u32)];
+//    let mut _4: &'static [(u32, u32); 42];
+//    let mut _5: &'static [(u32, u32); 42];
+//    let mut _6: [(u32, u32); 42];
+//    let mut _7: (u32, u32);
+//    let mut _8: (u32, u32);
+//    let mut _9: (u32, u32);
+//    let mut _10: (u32, u32);
+//    let mut _11: (u32, u32);
+//    let mut _12: (u32, u32);
+//    let mut _13: (u32, u32);
+//    let mut _14: (u32, u32);
+//    let mut _15: (u32, u32);
+//    let mut _16: (u32, u32);
+//    let mut _17: (u32, u32);
+//    let mut _18: (u32, u32);
+//    let mut _19: (u32, u32);
+//    let mut _20: (u32, u32);
+//    let mut _21: (u32, u32);
+//    let mut _22: (u32, u32);
+//    let mut _23: (u32, u32);
+//    let mut _24: (u32, u32);
+//    let mut _25: (u32, u32);
+//    let mut _26: (u32, u32);
+//    let mut _27: (u32, u32);
+//    let mut _28: (u32, u32);
+//    let mut _29: (u32, u32);
+//    let mut _30: (u32, u32);
+//    let mut _31: (u32, u32);
+//    let mut _32: (u32, u32);
+//    let mut _33: (u32, u32);
+//    let mut _34: (u32, u32);
+//    let mut _35: (u32, u32);
+//    let mut _36: (u32, u32);
+//    let mut _37: (u32, u32);
+//    let mut _38: (u32, u32);
+//    let mut _39: (u32, u32);
+//    let mut _40: (u32, u32);
+//    let mut _41: (u32, u32);
+//    let mut _42: (u32, u32);
+//    let mut _43: (u32, u32);
+//    let mut _44: (u32, u32);
+//    let mut _45: (u32, u32);
+//    let mut _46: (u32, u32);
+//    let mut _47: (u32, u32);
+//    let mut _48: (u32, u32);
 //    bb0: {
-//        _7 = (const 0u32, const 1u32);   // scope 0 at src/test/mir-opt/basic_assignment.rs:29:9: 29:15
-//        _8 = (const 0u32, const 2u32);   // scope 0 at src/test/mir-opt/basic_assignment.rs:29:17: 29:23
-//        _9 = (const 0u32, const 3u32);   // scope 0 at src/test/mir-opt/basic_assignment.rs:29:25: 29:31
-//        _10 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:30:9: 30:15
-//        _11 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:30:17: 30:23
-//        _12 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:30:25: 30:31
-//        _13 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:31:9: 31:15
-//        _14 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:31:17: 31:23
-//        _15 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:31:25: 31:31
-//        _16 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:32:9: 32:15
-//        _17 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:32:17: 32:23
-//        _18 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:32:25: 32:31
-//        _19 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:33:9: 33:15
-//        _20 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:33:17: 33:23
-//        _21 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:33:25: 33:31
-//        _22 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:34:9: 34:15
-//        _23 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:34:17: 34:23
-//        _24 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:34:25: 34:31
-//        _25 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:35:9: 35:15
-//        _26 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:35:17: 35:23
-//        _27 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:35:25: 35:31
-//        _28 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:36:9: 36:15
-//        _29 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:36:17: 36:23
-//        _30 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:36:25: 36:31
-//        _31 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:37:9: 37:15
-//        _32 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:37:17: 37:23
-//        _33 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:37:25: 37:31
-//        _34 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:38:9: 38:15
-//        _35 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:38:17: 38:23
-//        _36 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:38:25: 38:31
-//        _37 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:39:9: 39:15
-//        _38 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:39:17: 39:23
-//        _39 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:39:25: 39:31
-//        _40 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:40:9: 40:15
-//        _41 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:40:17: 40:23
-//        _42 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:40:25: 40:31
-//        _43 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:41:9: 41:15
-//        _44 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:41:17: 41:23
-//        _45 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:41:25: 41:31
-//        _46 = (const 0u32, const 1u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:42:9: 42:15
-//        _47 = (const 0u32, const 2u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:42:17: 42:23
-//        _48 = (const 0u32, const 3u32);  // scope 0 at src/test/mir-opt/basic_assignment.rs:42:25: 42:31
-//        _6 = [_7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48]; // scope 0 at src/test/mir-opt/basic_assignment.rs:28:12: 43:6
-//        _5 = &_6;                        // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6
-//        _4 = &(*_5);                     // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6
-//        _3 = _4 as &'static [(u32, u32)] (Unsize); // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6
-//        _2 = Foo { tup: const "hi", data: _3 }; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:29: 44:2
-//        _1 = &_2;                        // scope 0 at src/test/mir-opt/basic_assignment.rs:26:28: 44:2
-//        _0 = &(*_1);                     // scope 0 at src/test/mir-opt/basic_assignment.rs:26:28: 44:2
-//        return;                          // scope 0 at src/test/mir-opt/basic_assignment.rs:26:1: 44:3
+//        StorageLive(_1);
+//        StorageLive(_2);
+//        StorageLive(_3);
+//        StorageLive(_4);
+//        StorageLive(_5);
+//        StorageLive(_6);
+//        StorageLive(_7);
+//        _7 = (const 0u32, const 1u32);
+//        StorageLive(_8);
+//        _8 = (const 0u32, const 2u32);
+//        StorageLive(_9);
+//        _9 = (const 0u32, const 3u32);
+//        StorageLive(_10);
+//        _10 = (const 0u32, const 1u32);
+//        StorageLive(_11);
+//        _11 = (const 0u32, const 2u32);
+//        StorageLive(_12);
+//        _12 = (const 0u32, const 3u32);
+//        StorageLive(_13);
+//        _13 = (const 0u32, const 1u32);
+//        StorageLive(_14);
+//        _14 = (const 0u32, const 2u32);
+//        StorageLive(_15);
+//        _15 = (const 0u32, const 3u32);
+//        StorageLive(_16);
+//        _16 = (const 0u32, const 1u32);
+//        StorageLive(_17);
+//        _17 = (const 0u32, const 2u32);
+//        StorageLive(_18);
+//        _18 = (const 0u32, const 3u32);
+//        StorageLive(_19);
+//        _19 = (const 0u32, const 1u32);
+//        StorageLive(_20);
+//        _20 = (const 0u32, const 2u32);
+//        StorageLive(_21);
+//        _21 = (const 0u32, const 3u32);
+//        StorageLive(_22);
+//        _22 = (const 0u32, const 1u32);
+//        StorageLive(_23);
+//        _23 = (const 0u32, const 2u32);
+//        StorageLive(_24);
+//        _24 = (const 0u32, const 3u32);
+//        StorageLive(_25);
+//        _25 = (const 0u32, const 1u32);
+//        StorageLive(_26);
+//        _26 = (const 0u32, const 2u32);
+//        StorageLive(_27);
+//        _27 = (const 0u32, const 3u32);
+//        StorageLive(_28);
+//        _28 = (const 0u32, const 1u32);
+//        StorageLive(_29);
+//        _29 = (const 0u32, const 2u32);
+//        StorageLive(_30);
+//        _30 = (const 0u32, const 3u32);
+//        StorageLive(_31);
+//        _31 = (const 0u32, const 1u32);
+//        StorageLive(_32);
+//        _32 = (const 0u32, const 2u32);
+//        StorageLive(_33);
+//        _33 = (const 0u32, const 3u32);
+//        StorageLive(_34);
+//        _34 = (const 0u32, const 1u32);
+//        StorageLive(_35);
+//        _35 = (const 0u32, const 2u32);
+//        StorageLive(_36);
+//        _36 = (const 0u32, const 3u32);
+//        StorageLive(_37);
+//        _37 = (const 0u32, const 1u32);
+//        StorageLive(_38);
+//        _38 = (const 0u32, const 2u32);
+//        StorageLive(_39);
+//        _39 = (const 0u32, const 3u32);
+//        StorageLive(_40);
+//        _40 = (const 0u32, const 1u32);
+//        StorageLive(_41);
+//        _41 = (const 0u32, const 2u32);
+//        StorageLive(_42);
+//        _42 = (const 0u32, const 3u32);
+//        StorageLive(_43);
+//        _43 = (const 0u32, const 1u32);
+//        StorageLive(_44);
+//        _44 = (const 0u32, const 2u32);
+//        StorageLive(_45);
+//        _45 = (const 0u32, const 3u32);
+//        StorageLive(_46);
+//        _46 = (const 0u32, const 1u32);
+//        StorageLive(_47);
+//        _47 = (const 0u32, const 2u32);
+//        StorageLive(_48);
+//        _48 = (const 0u32, const 3u32);
+//        _6 = [_7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48];
+//        _5 = &_6;
+//        _4 = &(*_5);
+//        _3 = _4 as &'static [(u32, u32)] (Unsize);
+//        _2 = Foo { tup: const "hi", data: _3 };
+//        _1 = &_2;
+//        _0 = &(*_1);
+//        StorageDead(_1);
+//        StorageDead(_5);
+//        return;
 //    }
+//}
 // END rustc.node4.mir_map.0.mir
diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs
index 3fbd1a36f2f..7dbcf82af34 100644
--- a/src/test/mir-opt/storage_ranges.rs
+++ b/src/test/mir-opt/storage_ranges.rs
@@ -38,5 +38,6 @@ fn main() {
 //         _0 = ();
 //         StorageDead(_6);
 //         StorageDead(_1);
+//         return;
 //      }
 // END rustc.node4.TypeckMir.before.mir
diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs
index 39f43e4e990..5a31be8bd50 100644
--- a/src/test/mir-opt/validate_1.rs
+++ b/src/test/mir-opt/validate_1.rs
@@ -31,12 +31,15 @@ fn main() {
 // START rustc.node12.EraseRegions.after.mir
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:5) => validate_1[8cd8]::{{impl}}[0]::foo[0] }, BrAnon(0)) Test, _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:5) => validate_1[8cd8]::{{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(Node(ItemLocalId(10)))), [_1: i32]);
 //         _6 = &ReErased mut _1;
 //         Validate(Acquire, [(*_6): i32/ReScope(Node(ItemLocalId(10)))]);
@@ -50,12 +53,14 @@ fn main() {
 //     bb1: {
 //         Validate(Acquire, [_2: ()]);
 //         EndRegion(ReScope(Node(ItemLocalId(10))));
+//         ...
 //         return;
 //     }
 // }
 // END rustc.node23.EraseRegions.after.mir
 // START rustc.node50.EraseRegions.after.mir
 // fn main::{{closure}}(_1: &ReErased [closure@NodeId(50)], _2: &ReErased mut i32) -> i32 {
+//     ...
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
 //         StorageLive(_3);
diff --git a/src/test/mir-opt/validate_2.rs b/src/test/mir-opt/validate_2.rs
index 37ebd720d52..aacf5a5ed0f 100644
--- a/src/test/mir-opt/validate_2.rs
+++ b/src/test/mir-opt/validate_2.rs
@@ -18,10 +18,18 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node4.EraseRegions.after.mir
 // fn main() -> () {
+//     ...
 //     bb1: {
+//         Validate(Acquire, [_2: std::boxed::Box<[i32; 3]>]);
 //         Validate(Release, [_2: std::boxed::Box<[i32; 3]>]);
 //         _1 = _2 as std::boxed::Box<[i32]> (Unsize);
 //         Validate(Acquire, [_1: std::boxed::Box<[i32]>]);
+//         StorageDead(_2);
+//         StorageDead(_3);
+//         _0 = ();
+//         Validate(Release, [_1: std::boxed::Box<[i32]>]);
+//         drop(_1) -> bb2;
 //     }
+//     ...
 // }
 // END rustc.node4.EraseRegions.after.mir
diff --git a/src/test/mir-opt/validate_3.rs b/src/test/mir-opt/validate_3.rs
index 116e35b2d6f..d7d3e023c9e 100644
--- a/src/test/mir-opt/validate_3.rs
+++ b/src/test/mir-opt/validate_3.rs
@@ -30,8 +30,17 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node16.EraseRegions.after.mir
 // fn main() -> () {
+//     ...
 //     let mut _5: &ReErased i32;
 //     bb0: {
+//         StorageLive(_1);
+//         _1 = Test { x: const 0i32 };
+//         StorageLive(_2);
+//         Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 }))), [_1: Test]);
+//         _2 = &ReErased _1;
+//         Validate(Acquire, [(*_2): Test/ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })) (imm)]);
+//         StorageLive(_4);
+//         StorageLive(_5);
 //         Validate(Suspend(ReScope(Node(ItemLocalId(17)))), [((*_2).0: i32): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })) (imm)]);
 //         _5 = &ReErased ((*_2).0: i32);
 //         Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(17))) (imm)]);
@@ -42,8 +51,14 @@ fn main() {
 //         _3 = const foo(_4) -> bb1;
 //     }
 //     bb1: {
+//         Validate(Acquire, [_3: ()]);
 //         EndRegion(ReScope(Node(ItemLocalId(17))));
+//         StorageDead(_4);
+//         StorageDead(_5);
+//         _0 = ();
 //         EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })));
+//         StorageDead(_2);
+//         StorageDead(_1);
 //         return;
 //     }
 // }
diff --git a/src/test/mir-opt/validate_4.rs b/src/test/mir-opt/validate_4.rs
index b670d8094dd..bcb21c60b26 100644
--- a/src/test/mir-opt/validate_4.rs
+++ b/src/test/mir-opt/validate_4.rs
@@ -38,15 +38,18 @@ fn main() {
 // 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.node22.EraseRegions.after.mir
 // fn write_42::{{closure}}(_1: &ReErased [closure@NodeId(22)], _2: *mut i32) -> () {
+//     ...
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_4[8cd8]::write_42[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(22)], _2: *mut i32]);
 //         Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_4[8cd8]::write_42[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(22)], _2: *mut i32]);
@@ -60,24 +63,30 @@ fn main() {
 // END rustc.node22.EraseRegions.after.mir
 // START rustc.node31.EraseRegions.after.mir
 // fn test(_1: &ReErased mut i32) -> () {
+//     ...
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_4[8cd8]::test[0] }, BrAnon(0)) mut i32]);
 //         Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_4[8cd8]::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
 // START rustc.node60.EraseRegions.after.mir
 // fn main::{{closure}}(_1: &ReErased [closure@NodeId(60)], _2: &ReErased mut i32) -> bool {
+//     ...
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
 //         Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
 //         StorageLive(_3);
+//         ...
 //         _0 = const write_42(_4) -> bb1;
 //     }
+//     ...
 // }
 // END rustc.node60.EraseRegions.after.mir
diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs
index 059d3adb407..44280539c41 100644
--- a/src/test/mir-opt/validate_5.rs
+++ b/src/test/mir-opt/validate_5.rs
@@ -35,15 +35,19 @@ fn main() {
 // END RUST SOURCE
 // START rustc.node17.EraseRegions.after.mir
 // fn test(_1: &ReErased mut i32) -> () {
+//     ...
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_5[8cd8]::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
 // START rustc.node46.EraseRegions.after.mir
 // fn main::{{closure}}(_1: &ReErased [closure@NodeId(46)], _2: &ReErased mut i32) -> bool {
+//     ...
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
 //         StorageLive(_3);
@@ -59,5 +63,6 @@ fn main() {
 //         Validate(Release, [_0: bool, _4: *mut i32]);
 //         _0 = const write_42(_4) -> bb1;
 //     }
+//     ...
 // }
 // END rustc.node46.EraseRegions.after.mir
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index dfdbabdcd4a..af9061ab367 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -25,6 +25,7 @@ use std::collections::HashSet;
 use std::env;
 use std::ffi::OsString;
 use std::fs::{self, File, create_dir_all};
+use std::fmt;
 use std::io::prelude::*;
 use std::io::{self, BufReader};
 use std::path::{Path, PathBuf};
@@ -2228,7 +2229,7 @@ actual:\n\
             let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
             let tests_text_str = String::from(tests_text);
             let mut curr_test : Option<&str> = None;
-            let mut curr_test_contents = Vec::new();
+            let mut curr_test_contents = vec![ExpectedLine::Elision];
             for l in tests_text_str.lines() {
                 debug!("line: {:?}", l);
                 if l.starts_with("// START ") {
@@ -2242,11 +2243,14 @@ actual:\n\
                     self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents);
                     curr_test = None;
                     curr_test_contents.clear();
+                    curr_test_contents.push(ExpectedLine::Elision);
                 } else if l.is_empty() {
                     // ignore
+                } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." {
+                    curr_test_contents.push(ExpectedLine::Elision)
                 } else if l.starts_with("// ") {
                     let (_, test_content) = l.split_at("// ".len());
-                    curr_test_contents.push(test_content);
+                    curr_test_contents.push(ExpectedLine::Text(test_content));
                 }
             }
         }
@@ -2264,7 +2268,7 @@ actual:\n\
         }
     }
 
-    fn compare_mir_test_output(&self, test_name: &str, expected_content: &[&str]) {
+    fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) {
         let mut output_file = PathBuf::new();
         output_file.push(self.get_mir_dump_dir());
         output_file.push(test_name);
@@ -2276,38 +2280,77 @@ actual:\n\
         let mut dumped_string = String::new();
         dumped_file.read_to_string(&mut dumped_string).unwrap();
         let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
-        let mut expected_lines = expected_content.iter().filter(|l| !l.is_empty());
+        let mut expected_lines = expected_content.iter().filter(|&l| {
+            if let &ExpectedLine::Text(l) = l {
+                !l.is_empty()
+            } else {
+                true
+            }
+        }).peekable();
 
-        // We expect each non-empty line from expected_content to appear
-        // in the dump in order, but there may be extra lines interleaved
-        while let Some(expected_line) = expected_lines.next() {
+        let compare = |expected_line, dumped_line| {
             let e_norm = normalize_mir_line(expected_line);
-            if e_norm.is_empty() {
-                continue;
+            let d_norm = normalize_mir_line(dumped_line);
+            debug!("found: {:?}", d_norm);
+            debug!("expected: {:?}", e_norm);
+            e_norm == d_norm
+        };
+
+        let error = |expected_line, extra_msg| {
+            let normalize_all = dumped_string.lines()
+                                             .map(nocomment_mir_line)
+                                             .filter(|l| !l.is_empty())
+                                             .collect::<Vec<_>>()
+                                             .join("\n");
+            let f = |l: &ExpectedLine<_>| match l {
+                &ExpectedLine::Elision => "... (elided)".into(),
+                &ExpectedLine::Text(t) => t
             };
-            let mut found = false;
-            while let Some(dumped_line) = dumped_lines.next() {
-                let d_norm = normalize_mir_line(dumped_line);
-                debug!("found: {:?}", d_norm);
-                debug!("expected: {:?}", e_norm);
-                if e_norm == d_norm {
-                    found = true;
-                    break;
-                };
-            }
-            if !found {
-                let normalize_all = dumped_string.lines()
-                                                 .map(nocomment_mir_line)
-                                                 .filter(|l| !l.is_empty())
-                                                 .collect::<Vec<_>>()
-                                                 .join("\n");
-                panic!("ran out of mir dump output to match against.\n\
-                        Did not find expected line: {:?}\n\
-                        Expected:\n{}\n\
-                        Actual:\n{}",
-                        expected_line,
-                        expected_content.join("\n"),
-                        normalize_all);
+            let expected_content = expected_content.iter()
+                                                   .map(|l| f(l))
+                                                   .collect::<Vec<_>>()
+                                                   .join("\n");
+            panic!("Did not find expected line, error: {}\n\
+                   Actual Line: {:?}\n\
+                   Expected:\n{}\n\
+                   Actual:\n{}",
+                   extra_msg,
+                   expected_line,
+                   expected_content,
+                   normalize_all);
+        };
+
+        // We expect each non-empty line to appear consecutively, non-consecutive lines
+        // must be separated by at least one Elision
+        while let Some(dumped_line) = dumped_lines.next() {
+            match expected_lines.next() {
+                Some(&ExpectedLine::Text(expected_line)) =>
+                    if !compare(expected_line, dumped_line) {
+                        error(expected_line,
+                              format!("Mismatch in lines\nExpected Line: {:?}", dumped_line));
+                    },
+                Some(&ExpectedLine::Elision) => {
+                    // skip any number of elisions in a row.
+                    while let Some(&&ExpectedLine::Elision) = expected_lines.peek() {
+                        expected_lines.next();
+                    }
+                    if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() {
+                        let mut found = compare(expected_line, dumped_line);
+                        if found {
+                            continue;
+                        }
+                        while let Some(dumped_line) = dumped_lines.next() {
+                            found = compare(expected_line, dumped_line);
+                            if found {
+                                break;
+                            }
+                        }
+                        if !found {
+                            error(expected_line, "ran out of mir dump to match against".into());
+                        }
+                    }
+                },
+                None => {},
             }
         }
     }
@@ -2430,6 +2473,25 @@ enum TargetLocation {
     ThisDirectory(PathBuf),
 }
 
+#[derive(Clone, PartialEq, Eq)]
+enum ExpectedLine<T: AsRef<str>> {
+    Elision,
+    Text(T)
+}
+
+impl<T> fmt::Debug for ExpectedLine<T>
+where
+    T: AsRef<str> + fmt::Debug
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        if let &ExpectedLine::Text(ref t) = self {
+            write!(formatter, "{:?}", t)
+        } else {
+            write!(formatter, "\"...\" (Elision)")
+        }
+    }
+}
+
 fn normalize_mir_line(line: &str) -> String {
     nocomment_mir_line(line).replace(char::is_whitespace, "")
 }