about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-03-24 23:12:57 -0700
committerbors <bors@rust-lang.org>2016-03-24 23:12:57 -0700
commitf1578d37dc844ffeefcabc30960784082c3c54fa (patch)
tree1bf89b1b95acf53779def3397327c7423cd93110 /src
parent40deb279a87e640f799140e9f19b3e64623c30da (diff)
parent091a00797ecf91989d901fcbb795869f4c6c7fc1 (diff)
downloadrust-f1578d37dc844ffeefcabc30960784082c3c54fa.tar.gz
rust-f1578d37dc844ffeefcabc30960784082c3c54fa.zip
Auto merge of #32428 - nikomatsakis:scopes-in-mir, r=nagisa
Scopes in mir

This PR adds scopes to MIR. There is a tree of scopes (each represented by a `ScopeId`). Every statement, variable, and terminator now has an associated scope and span.  It also adds a `-Z dump-mir` switch one can use to conveniently examine the MIR as optimizations proceed.

The intention is two-fold. First, to support MIR debug-info. This PR does not attempt to modify trans to make use of the scope information, however.

Second, in a more temporary capacity, to support the goal of moving regionck and borowck into the MIR. To that end, the PR also constructs a "scope auxiliary" table storing the extent of each span (this is kept separate from the main MIR, since it contains node-ids) and the dom/post-dom of the region in the graph where the scope occurs. When we move to non-lexical lifetimes, I expect this auxiliary information to be discarded, but that is still some ways in the future (requires, at minimum, an RFC, and there are some thorny details to work out -- though I've got an in-progress draft).

Right now, I'm just dropping this auxiliary information after it is constructed. I was debating for some time whether to add some sort of sanity tests, but decided to just open this PR instead, because I couldn't figure out what such a test would look like (and we don't have independent tests for this today beyond the regionck and borrowck tests).

I'd prefer not to store the auxiliary data into any kind of "per-fn" map. Rather, I'd prefer that we do regionck/borrowck/whatever-else immediately after construction -- that is, we build the MIR for fn X and immediately thereafter do extended correctness checking on it. This will reduce peak memory usage and also ensure that the auxiliary data doesn't exist once optimizations begin. It also clarifies the transition point where static checks are complete and MIR can be more freely optimized.

cc @rust-lang/compiler @nagisa
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/repr.rs107
-rw-r--r--src/librustc/mir/visit.rs426
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow.rs21
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs101
-rw-r--r--src/librustc_mir/build/block.rs18
-rw-r--r--src/librustc_mir/build/cfg.rs26
-rw-r--r--src/librustc_mir/build/expr/as_lvalue.rs11
-rw-r--r--src/librustc_mir/build/expr/as_operand.rs2
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs7
-rw-r--r--src/librustc_mir/build/expr/as_temp.rs5
-rw-r--r--src/librustc_mir/build/expr/into.rs72
-rw-r--r--src/librustc_mir/build/matches/mod.rs78
-rw-r--r--src/librustc_mir/build/matches/test.rs42
-rw-r--r--src/librustc_mir/build/matches/util.rs3
-rw-r--r--src/librustc_mir/build/misc.rs9
-rw-r--r--src/librustc_mir/build/mod.rs218
-rw-r--r--src/librustc_mir/build/scope.rs339
-rw-r--r--src/librustc_mir/graphviz.rs4
-rw-r--r--src/librustc_mir/mir_map.rs23
-rw-r--r--src/librustc_mir/pretty.rs194
-rw-r--r--src/librustc_mir/transform/erase_regions.rs18
-rw-r--r--src/librustc_mir/transform/no_landing_pads.rs18
-rw-r--r--src/librustc_mir/transform/simplify_cfg.rs43
-rw-r--r--src/librustc_mir/transform/type_check.rs18
-rw-r--r--src/librustc_trans/trans/mir/block.rs18
26 files changed, 1370 insertions, 453 deletions
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index ff3e292f458..47d923cbce3 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -32,6 +32,10 @@ pub struct Mir<'tcx> {
     /// that indexes into this vector.
     pub basic_blocks: Vec<BasicBlockData<'tcx>>,
 
+    /// List of lexical scopes; these are referenced by statements and
+    /// used (eventually) for debuginfo. Indexed by a `ScopeId`.
+    pub scopes: Vec<ScopeData>,
+
     /// Return type of the function.
     pub return_ty: FnOutput<'tcx>,
 
@@ -152,9 +156,21 @@ pub enum BorrowKind {
 /// decl, a let, etc.
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct VarDecl<'tcx> {
+    /// `let mut x` vs `let x`
     pub mutability: Mutability,
+
+    /// name that user gave the variable; not that, internally,
+    /// mir references variables by index
     pub name: Name,
+
+    /// type inferred for this variable (`let x: ty = ...`)
     pub ty: Ty<'tcx>,
+
+    /// scope in which variable was declared
+    pub scope: ScopeId,
+
+    /// span where variable was declared
+    pub span: Span,
 }
 
 /// A "temp" is a temporary that we place on the stack. They are
@@ -191,7 +207,7 @@ pub struct ArgDecl<'tcx> {
 /// list of the `Mir`.
 ///
 /// (We use a `u32` internally just to save memory.)
-#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub struct BasicBlock(u32);
 
 impl BasicBlock {
@@ -217,13 +233,35 @@ impl Debug for BasicBlock {
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct BasicBlockData<'tcx> {
+    /// List of statements in this block.
     pub statements: Vec<Statement<'tcx>>,
+
+    /// Terminator for this block.
+    ///
+    /// NB. This should generally ONLY be `None` during construction.
+    /// Therefore, you should generally access it via the
+    /// `terminator()` or `terminator_mut()` methods. The only
+    /// exception is that certain passes, such as `simplify_cfg`, swap
+    /// out the terminator temporarily with `None` while they continue
+    /// to recurse over the set of basic blocks.
     pub terminator: Option<Terminator<'tcx>>,
+
+    /// If true, this block lies on an unwind path. This is used
+    /// during trans where distinct kinds of basic blocks may be
+    /// generated (particularly for MSVC cleanup). Unwind blocks must
+    /// only branch to other unwind blocks.
     pub is_cleanup: bool,
 }
 
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct Terminator<'tcx> {
+    pub span: Span,
+    pub scope: ScopeId,
+    pub kind: TerminatorKind<'tcx>
+}
+
 #[derive(Clone, RustcEncodable, RustcDecodable)]
-pub enum Terminator<'tcx> {
+pub enum TerminatorKind<'tcx> {
     /// block should have one successor in the graph; we jump there
     Goto {
         target: BasicBlock,
@@ -293,7 +331,17 @@ pub enum Terminator<'tcx> {
 
 impl<'tcx> Terminator<'tcx> {
     pub fn successors(&self) -> Cow<[BasicBlock]> {
-        use self::Terminator::*;
+        self.kind.successors()
+    }
+
+    pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
+        self.kind.successors_mut()
+    }
+}
+
+impl<'tcx> TerminatorKind<'tcx> {
+    pub fn successors(&self) -> Cow<[BasicBlock]> {
+        use self::TerminatorKind::*;
         match *self {
             Goto { target: ref b } => slice::ref_slice(b).into_cow(),
             If { targets: (b1, b2), .. } => vec![b1, b2].into_cow(),
@@ -314,7 +362,7 @@ impl<'tcx> Terminator<'tcx> {
     // FIXME: no mootable cow. I’m honestly not sure what a “cow” between `&mut [BasicBlock]` and
     // `Vec<&mut BasicBlock>` would look like in the first place.
     pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
-        use self::Terminator::*;
+        use self::TerminatorKind::*;
         match *self {
             Goto { target: ref mut b } => vec![b],
             If { targets: (ref mut b1, ref mut b2), .. } => vec![b1, b2],
@@ -354,7 +402,7 @@ impl<'tcx> BasicBlockData<'tcx> {
     }
 }
 
-impl<'tcx> Debug for Terminator<'tcx> {
+impl<'tcx> Debug for TerminatorKind<'tcx> {
     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
         self.fmt_head(fmt)?;
         let successors = self.successors();
@@ -381,12 +429,12 @@ impl<'tcx> Debug for Terminator<'tcx> {
     }
 }
 
-impl<'tcx> Terminator<'tcx> {
+impl<'tcx> TerminatorKind<'tcx> {
     /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the
     /// successor basic block, if any. The only information not inlcuded is the list of possible
     /// successors, which may be rendered differently between the text and the graphviz format.
     pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
-        use self::Terminator::*;
+        use self::TerminatorKind::*;
         match *self {
             Goto { .. } => write!(fmt, "goto"),
             If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv),
@@ -413,7 +461,7 @@ impl<'tcx> Terminator<'tcx> {
 
     /// Return the list of labels for the edges to the successor basic blocks.
     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
-        use self::Terminator::*;
+        use self::TerminatorKind::*;
         match *self {
             Return | Resume => vec![],
             Goto { .. } => vec!["".into()],
@@ -452,6 +500,7 @@ impl<'tcx> Terminator<'tcx> {
 #[derive(Clone, RustcEncodable, RustcDecodable)]
 pub struct Statement<'tcx> {
     pub span: Span,
+    pub scope: ScopeId,
     pub kind: StatementKind<'tcx>,
 }
 
@@ -468,6 +517,7 @@ impl<'tcx> Debug for Statement<'tcx> {
         }
     }
 }
+
 ///////////////////////////////////////////////////////////////////////////
 // Lvalues
 
@@ -614,12 +664,49 @@ impl<'tcx> Debug for Lvalue<'tcx> {
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// Scopes
+
+impl Index<ScopeId> for Vec<ScopeData> {
+    type Output = ScopeData;
+
+    #[inline]
+    fn index(&self, index: ScopeId) -> &ScopeData {
+        &self[index.index()]
+    }
+}
+
+impl IndexMut<ScopeId> for Vec<ScopeData> {
+    #[inline]
+    fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData {
+        &mut self[index.index()]
+    }
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+pub struct ScopeId(u32);
+
+impl ScopeId {
+    pub fn new(index: usize) -> ScopeId {
+        assert!(index < (u32::MAX as usize));
+        ScopeId(index as u32)
+    }
+
+    pub fn index(self) -> usize {
+        self.0 as usize
+    }
+}
+
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct ScopeData {
+    pub parent_scope: Option<ScopeId>,
+}
+
+///////////////////////////////////////////////////////////////////////////
 // Operands
-//
+
 /// These are values that can appear inside an rvalue (or an index
 /// lvalue). They are intentionally limited to prevent rvalues from
 /// being nested in one another.
-
 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Operand<'tcx> {
     Consume(Lvalue<'tcx>),
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 36d45f0a51e..bc0056b0af0 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -8,12 +8,79 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::const_eval::ConstVal;
 use middle::def_id::DefId;
-use middle::ty::Region;
+use middle::subst::Substs;
+use middle::ty::{ClosureSubsts, FnOutput, Region, Ty};
 use mir::repr::*;
+use rustc_const_eval::ConstUsize;
 use rustc_data_structures::tuple_slice::TupleSlice;
 use syntax::codemap::Span;
 
+// # The MIR Visitor
+//
+// ## Overview
+//
+// There are two visitors, one for immutable and one for mutable references,
+// but both are generated by the following macro. The code is written according
+// to the following conventions:
+//
+// - introduce a `visit_foo` and a `super_foo` method for every MIR type
+// - `visit_foo`, by default, calls `super_foo`
+// - `super_foo`, by default, destructures the `foo` and calls `visit_foo`
+//
+// This allows you as a user to override `visit_foo` for types are
+// interested in, and invoke (within that method) call
+// `self.super_foo` to get the default behavior. Just as in an OO
+// language, you should never call `super` methods ordinarily except
+// in that circumstance.
+//
+// For the most part, we do not destructure things external to the
+// MIR, e.g. types, spans, etc, but simply visit them and stop. This
+// avoids duplication with other visitors like `TypeFoldable`. But
+// there is one exception: we do destructure the `FnOutput` to reach
+// the type within. Just because.
+//
+// ## Updating
+//
+// The code is written in a very deliberate style intended to minimize
+// the chance of things being overlooked. You'll notice that we always
+// use pattern matching to reference fields and we ensure that all
+// matches are exhaustive.
+//
+// For example, the `super_basic_block_data` method begins like this:
+//
+// ```rust
+// fn super_basic_block_data(&mut self,
+//                           block: BasicBlock,
+//                           data: & $($mutability)* BasicBlockData<'tcx>) {
+//     let BasicBlockData {
+//         ref $($mutability)* statements,
+//         ref $($mutability)* terminator,
+//         is_cleanup: _
+//     } = *data;
+//
+//     for statement in statements {
+//         self.visit_statement(block, statement);
+//     }
+//
+//     ...
+// }
+// ```
+//
+// Here we used `let BasicBlockData { <fields> } = *data` deliberately,
+// rather than writing `data.statements` in the body. This is because if one
+// adds a new field to `BasicBlockData`, one will be forced to revise this code,
+// and hence one will (hopefully) invoke the correct visit methods (if any).
+//
+// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
+// That means you never write `..` to skip over fields, nor do you write `_`
+// to skip over variants in a `match`.
+//
+// The only place that `_` is acceptable is to match a field (or
+// variant argument) that does not require visiting, as in
+// `is_cleanup` above.
+
 macro_rules! make_mir_visitor {
     ($visitor_trait_name:ident, $($mutability:ident)*) => {
         pub trait $visitor_trait_name<'tcx> {
@@ -30,6 +97,11 @@ macro_rules! make_mir_visitor {
                 self.super_basic_block_data(block, data);
             }
 
+            fn visit_scope_data(&mut self,
+                                scope_data: & $($mutability)* ScopeData) {
+                self.super_scope_data(scope_data);
+            }
+
             fn visit_statement(&mut self,
                                block: BasicBlock,
                                statement: & $($mutability)* Statement<'tcx>) {
@@ -49,6 +121,12 @@ macro_rules! make_mir_visitor {
                 self.super_terminator(block, terminator);
             }
 
+            fn visit_terminator_kind(&mut self,
+                                     block: BasicBlock,
+                                     kind: & $($mutability)* TerminatorKind<'tcx>) {
+                self.super_terminator_kind(block, kind);
+            }
+
             fn visit_rvalue(&mut self,
                             rvalue: & $($mutability)* Rvalue<'tcx>) {
                 self.super_rvalue(rvalue);
@@ -65,6 +143,18 @@ macro_rules! make_mir_visitor {
                 self.super_lvalue(lvalue, context);
             }
 
+            fn visit_projection(&mut self,
+                                lvalue: & $($mutability)* LvalueProjection<'tcx>,
+                                context: LvalueContext) {
+                self.super_projection(lvalue, context);
+            }
+
+            fn visit_projection_elem(&mut self,
+                                     lvalue: & $($mutability)* LvalueElem<'tcx>,
+                                     context: LvalueContext) {
+                self.super_projection_elem(lvalue, context);
+            }
+
             fn visit_branch(&mut self,
                             source: BasicBlock,
                             target: BasicBlock) {
@@ -91,35 +181,143 @@ macro_rules! make_mir_visitor {
                 self.super_span(span);
             }
 
+            fn visit_fn_output(&mut self,
+                               fn_output: & $($mutability)* FnOutput<'tcx>) {
+                self.super_fn_output(fn_output);
+            }
+
+            fn visit_ty(&mut self,
+                        ty: & $($mutability)* Ty<'tcx>) {
+                self.super_ty(ty);
+            }
+
+            fn visit_substs(&mut self,
+                            substs: & $($mutability)* &'tcx Substs<'tcx>) {
+                self.super_substs(substs);
+            }
+
+            fn visit_closure_substs(&mut self,
+                                    substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) {
+                self.super_closure_substs(substs);
+            }
+
+            fn visit_const_val(&mut self,
+                               const_val: & $($mutability)* ConstVal) {
+                self.super_const_val(const_val);
+            }
+
+            fn visit_const_usize(&mut self,
+                                 const_usize: & $($mutability)* ConstUsize) {
+                self.super_const_usize(const_usize);
+            }
+
+            fn visit_typed_const_val(&mut self,
+                                     val: & $($mutability)* TypedConstVal<'tcx>) {
+                self.super_typed_const_val(val);
+            }
+
+            fn visit_var_decl(&mut self,
+                              var_decl: & $($mutability)* VarDecl<'tcx>) {
+                self.super_var_decl(var_decl);
+            }
+
+            fn visit_temp_decl(&mut self,
+                               temp_decl: & $($mutability)* TempDecl<'tcx>) {
+                self.super_temp_decl(temp_decl);
+            }
+
+            fn visit_arg_decl(&mut self,
+                              arg_decl: & $($mutability)* ArgDecl<'tcx>) {
+                self.super_arg_decl(arg_decl);
+            }
+
+            fn visit_scope_id(&mut self,
+                              scope_id: & $($mutability)* ScopeId) {
+                self.super_scope_id(scope_id);
+            }
+
             // The `super_xxx` methods comprise the default behavior and are
             // not meant to be overridden.
 
             fn super_mir(&mut self,
                          mir: & $($mutability)* Mir<'tcx>) {
-                for block in mir.all_basic_blocks() {
-                    let data = & $($mutability)* mir[block];
+                let Mir {
+                    ref $($mutability)* basic_blocks,
+                    ref $($mutability)* scopes,
+                    ref $($mutability)* return_ty,
+                    ref $($mutability)* var_decls,
+                    ref $($mutability)* arg_decls,
+                    ref $($mutability)* temp_decls,
+                    ref $($mutability)* span,
+                } = *mir;
+
+                for (index, data) in basic_blocks.into_iter().enumerate() {
+                    let block = BasicBlock::new(index);
                     self.visit_basic_block_data(block, data);
                 }
+
+                for scope in scopes {
+                    self.visit_scope_data(scope);
+                }
+
+                self.visit_fn_output(return_ty);
+
+                for var_decl in var_decls {
+                    self.visit_var_decl(var_decl);
+                }
+
+                for arg_decl in arg_decls {
+                    self.visit_arg_decl(arg_decl);
+                }
+
+                for temp_decl in temp_decls {
+                    self.visit_temp_decl(temp_decl);
+                }
+
+                self.visit_span(span);
             }
 
             fn super_basic_block_data(&mut self,
                                       block: BasicBlock,
                                       data: & $($mutability)* BasicBlockData<'tcx>) {
-                for statement in & $($mutability)* data.statements {
+                let BasicBlockData {
+                    ref $($mutability)* statements,
+                    ref $($mutability)* terminator,
+                    is_cleanup: _
+                } = *data;
+
+                for statement in statements {
                     self.visit_statement(block, statement);
                 }
 
-                if let Some(ref $($mutability)* terminator) = data.terminator {
+                if let Some(ref $($mutability)* terminator) = *terminator {
                     self.visit_terminator(block, terminator);
                 }
             }
 
+            fn super_scope_data(&mut self,
+                                scope_data: & $($mutability)* ScopeData) {
+                let ScopeData {
+                    ref $($mutability)* parent_scope,
+                } = *scope_data;
+
+                if let Some(ref $($mutability)* parent_scope) = *parent_scope {
+                    self.visit_scope_id(parent_scope);
+                }
+            }
+
             fn super_statement(&mut self,
                                block: BasicBlock,
                                statement: & $($mutability)* Statement<'tcx>) {
-                self.visit_span(& $($mutability)* statement.span);
-
-                match statement.kind {
+                let Statement {
+                    ref $($mutability)* span,
+                    ref $($mutability)* scope,
+                    ref $($mutability)* kind,
+                } = *statement;
+
+                self.visit_span(span);
+                self.visit_scope_id(scope);
+                match *kind {
                     StatementKind::Assign(ref $($mutability)* lvalue,
                                           ref $($mutability)* rvalue) => {
                         self.visit_assign(block, lvalue, rvalue);
@@ -138,52 +336,72 @@ macro_rules! make_mir_visitor {
             fn super_terminator(&mut self,
                                 block: BasicBlock,
                                 terminator: &$($mutability)* Terminator<'tcx>) {
-                match *terminator {
-                    Terminator::Goto { target } => {
+                let Terminator {
+                    ref $($mutability)* span,
+                    ref $($mutability)* scope,
+                    ref $($mutability)* kind,
+                } = *terminator;
+
+                self.visit_span(span);
+                self.visit_scope_id(scope);
+                self.visit_terminator_kind(block, kind);
+            }
+
+            fn super_terminator_kind(&mut self,
+                                     block: BasicBlock,
+                                     kind: & $($mutability)* TerminatorKind<'tcx>) {
+                match *kind {
+                    TerminatorKind::Goto { target } => {
                         self.visit_branch(block, target);
                     }
 
-                    Terminator::If { ref $($mutability)* cond,
-                                     ref $($mutability)* targets } => {
+                    TerminatorKind::If { ref $($mutability)* cond,
+                                         ref $($mutability)* targets } => {
                         self.visit_operand(cond);
                         for &target in targets.as_slice() {
                             self.visit_branch(block, target);
                         }
                     }
 
-                    Terminator::Switch { ref $($mutability)* discr,
-                                         adt_def: _,
-                                         ref targets } => {
+                    TerminatorKind::Switch { ref $($mutability)* discr,
+                                             adt_def: _,
+                                             ref targets } => {
                         self.visit_lvalue(discr, LvalueContext::Inspect);
                         for &target in targets {
                             self.visit_branch(block, target);
                         }
                     }
 
-                    Terminator::SwitchInt { ref $($mutability)* discr,
-                                            switch_ty: _,
-                                            values: _,
-                                            ref targets } => {
+                    TerminatorKind::SwitchInt { ref $($mutability)* discr,
+                                                ref $($mutability)* switch_ty,
+                                                ref $($mutability)* values,
+                                                ref targets } => {
                         self.visit_lvalue(discr, LvalueContext::Inspect);
+                        self.visit_ty(switch_ty);
+                        for value in values {
+                            self.visit_const_val(value);
+                        }
                         for &target in targets {
                             self.visit_branch(block, target);
                         }
                     }
 
-                    Terminator::Resume |
-                    Terminator::Return => {
+                    TerminatorKind::Resume |
+                    TerminatorKind::Return => {
                     }
 
-                    Terminator::Drop { ref $($mutability)* value, target, unwind } => {
+                    TerminatorKind::Drop { ref $($mutability)* value,
+                                           target,
+                                           unwind } => {
                         self.visit_lvalue(value, LvalueContext::Drop);
                         self.visit_branch(block, target);
                         unwind.map(|t| self.visit_branch(block, t));
                     }
 
-                    Terminator::Call { ref $($mutability)* func,
-                                       ref $($mutability)* args,
-                                       ref $($mutability)* destination,
-                                       cleanup } => {
+                    TerminatorKind::Call { ref $($mutability)* func,
+                                           ref $($mutability)* args,
+                                           ref $($mutability)* destination,
+                                           cleanup } => {
                         self.visit_operand(func);
                         for arg in args {
                             self.visit_operand(arg);
@@ -205,8 +423,9 @@ macro_rules! make_mir_visitor {
                     }
 
                     Rvalue::Repeat(ref $($mutability)* value,
-                                   _) => {
+                                   ref $($mutability)* typed_const_val) => {
                         self.visit_operand(value);
+                        self.visit_typed_const_val(typed_const_val);
                     }
 
                     Rvalue::Ref(r, bk, ref $($mutability)* path) => {
@@ -220,34 +439,48 @@ macro_rules! make_mir_visitor {
                         self.visit_lvalue(path, LvalueContext::Inspect);
                     }
 
-                    Rvalue::Cast(_, ref $($mutability)* operand, _) => {
+                    Rvalue::Cast(_cast_kind,
+                                 ref $($mutability)* operand,
+                                 ref $($mutability)* ty) => {
                         self.visit_operand(operand);
+                        self.visit_ty(ty);
                     }
 
-                    Rvalue::BinaryOp(_,
+                    Rvalue::BinaryOp(_bin_op,
                                      ref $($mutability)* lhs,
                                      ref $($mutability)* rhs) => {
                         self.visit_operand(lhs);
                         self.visit_operand(rhs);
                     }
 
-                    Rvalue::UnaryOp(_, ref $($mutability)* op) => {
+                    Rvalue::UnaryOp(_un_op, ref $($mutability)* op) => {
                         self.visit_operand(op);
                     }
 
-                    Rvalue::Box(_) => {
+                    Rvalue::Box(ref $($mutability)* ty) => {
+                        self.visit_ty(ty);
                     }
 
                     Rvalue::Aggregate(ref $($mutability)* kind,
                                       ref $($mutability)* operands) => {
                         match *kind {
-                            AggregateKind::Closure(ref $($mutability)* def_id, _) => {
+                            AggregateKind::Vec => {
+                            }
+                            AggregateKind::Tuple => {
+                            }
+                            AggregateKind::Adt(_adt_def,
+                                               _variant_index,
+                                               ref $($mutability)* substs) => {
+                                self.visit_substs(substs);
+                            }
+                            AggregateKind::Closure(ref $($mutability)* def_id,
+                                                   ref $($mutability)* closure_substs) => {
                                 self.visit_def_id(def_id);
+                                self.visit_closure_substs(closure_substs);
                             }
-                            _ => { /* nothing to do */ }
                         }
 
-                        for operand in & $($mutability)* operands[..] {
+                        for operand in operands {
                             self.visit_operand(operand);
                         }
                     }
@@ -262,7 +495,8 @@ macro_rules! make_mir_visitor {
                     }
 
                     Rvalue::InlineAsm { ref $($mutability)* outputs,
-                                        ref $($mutability)* inputs, .. } => {
+                                        ref $($mutability)* inputs,
+                                        asm: _ } => {
                         for output in & $($mutability)* outputs[..] {
                             self.visit_lvalue(output, LvalueContext::Store);
                         }
@@ -287,7 +521,7 @@ macro_rules! make_mir_visitor {
 
             fn super_lvalue(&mut self,
                             lvalue: & $($mutability)* Lvalue<'tcx>,
-                            _context: LvalueContext) {
+                            context: LvalueContext) {
                 match *lvalue {
                     Lvalue::Var(_) |
                     Lvalue::Temp(_) |
@@ -298,12 +532,81 @@ macro_rules! make_mir_visitor {
                         self.visit_def_id(def_id);
                     }
                     Lvalue::Projection(ref $($mutability)* proj) => {
-                        self.visit_lvalue(& $($mutability)* proj.base,
-                                          LvalueContext::Projection);
+                        self.visit_projection(proj, context);
                     }
                 }
             }
 
+            fn super_projection(&mut self,
+                                proj: & $($mutability)* LvalueProjection<'tcx>,
+                                context: LvalueContext) {
+                let Projection {
+                    ref $($mutability)* base,
+                    ref $($mutability)* elem,
+                } = *proj;
+                self.visit_lvalue(base, LvalueContext::Projection);
+                self.visit_projection_elem(elem, context);
+            }
+
+            fn super_projection_elem(&mut self,
+                                     proj: & $($mutability)* LvalueElem<'tcx>,
+                                     _context: LvalueContext) {
+                match *proj {
+                    ProjectionElem::Deref => {
+                    }
+                    ProjectionElem::Field(_field, ref $($mutability)* ty) => {
+                        self.visit_ty(ty);
+                    }
+                    ProjectionElem::Index(ref $($mutability)* operand) => {
+                        self.visit_operand(operand);
+                    }
+                    ProjectionElem::ConstantIndex { offset: _,
+                                                    min_length: _,
+                                                    from_end: _ } => {
+                    }
+                    ProjectionElem::Downcast(_adt_def, _variant_index) => {
+                    }
+                }
+            }
+
+            fn super_var_decl(&mut self,
+                              var_decl: & $($mutability)* VarDecl<'tcx>) {
+                let VarDecl {
+                    mutability: _,
+                    name: _,
+                    ref $($mutability)* ty,
+                    ref $($mutability)* scope,
+                    ref $($mutability)* span,
+                } = *var_decl;
+
+                self.visit_ty(ty);
+                self.visit_scope_id(scope);
+                self.visit_span(span);
+            }
+
+            fn super_temp_decl(&mut self,
+                               temp_decl: & $($mutability)* TempDecl<'tcx>) {
+                let TempDecl {
+                    ref $($mutability)* ty,
+                } = *temp_decl;
+
+                self.visit_ty(ty);
+            }
+
+            fn super_arg_decl(&mut self,
+                              arg_decl: & $($mutability)* ArgDecl<'tcx>) {
+                let ArgDecl {
+                    ref $($mutability)* ty,
+                    spread: _
+                } = *arg_decl;
+
+                self.visit_ty(ty);
+            }
+
+            fn super_scope_id(&mut self,
+                              _scope_id: & $($mutability)* ScopeId) {
+            }
+
             fn super_branch(&mut self,
                             _source: BasicBlock,
                             _target: BasicBlock) {
@@ -312,17 +615,32 @@ macro_rules! make_mir_visitor {
             fn super_constant(&mut self,
                               constant: & $($mutability)* Constant<'tcx>) {
                 self.visit_span(& $($mutability)* constant.span);
+                self.visit_ty(& $($mutability)* constant.ty);
                 self.visit_literal(& $($mutability)* constant.literal);
             }
 
+            fn super_typed_const_val(&mut self,
+                                     constant: & $($mutability)* TypedConstVal<'tcx>) {
+                let TypedConstVal {
+                    ref $($mutability)* span,
+                    ref $($mutability)* ty,
+                    ref $($mutability)* value,
+                } = *constant;
+                self.visit_span(span);
+                self.visit_ty(ty);
+                self.visit_const_usize(value);
+            }
+
             fn super_literal(&mut self,
                              literal: & $($mutability)* Literal<'tcx>) {
                 match *literal {
-                    Literal::Item { ref $($mutability)* def_id, .. } => {
+                    Literal::Item { ref $($mutability)* def_id,
+                                    ref $($mutability)* substs } => {
                         self.visit_def_id(def_id);
+                        self.visit_substs(substs);
                     },
-                    Literal::Value { .. } => {
-                        // Nothing to do
+                    Literal::Value { ref $($mutability)* value } => {
+                        self.visit_const_val(value);
                     }
                 }
             }
@@ -332,6 +650,32 @@ macro_rules! make_mir_visitor {
 
             fn super_span(&mut self, _span: & $($mutability)* Span) {
             }
+
+            fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) {
+                match *fn_output {
+                    FnOutput::FnConverging(ref $($mutability)* ty) => {
+                        self.visit_ty(ty);
+                    }
+                    FnOutput::FnDiverging => {
+                    }
+                }
+            }
+
+            fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) {
+            }
+
+            fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) {
+            }
+
+            fn super_closure_substs(&mut self,
+                                    _substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) {
+            }
+
+            fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) {
+            }
+
+            fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) {
+            }
         }
     }
 }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 17f70b2d8dc..bf532d9ccf9 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -663,6 +663,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "print the result of the translation item collection pass"),
     mir_opt_level: Option<usize> = (None, parse_opt_uint,
           "set the MIR optimization level (0-3)"),
+    dump_mir: Option<String> = (None, parse_opt_string,
+          "dump MIR state at various points in translation"),
     orbit: bool = (false, parse_bool,
           "get MIR where it belongs - everywhere; most importantly, in orbit"),
 }
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs
index 69aaae91c49..3c3a60b995f 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs
@@ -410,29 +410,28 @@ impl<D: BitDenotation> DataflowState<D> {
         bb: &repr::BasicBlockData,
         on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue)
     {
-        let term = if let Some(ref term) = bb.terminator { term } else { return };
-        match *term {
-            repr::Terminator::Return |
-            repr::Terminator::Resume => {}
-            repr::Terminator::Goto { ref target } |
-            repr::Terminator::Drop { ref target, value: _, unwind: None } => {
+        match bb.terminator().kind {
+            repr::TerminatorKind::Return |
+            repr::TerminatorKind::Resume => {}
+            repr::TerminatorKind::Goto { ref target } |
+            repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => {
                 self.propagate_bits_into_entry_set_for(in_out, changed, target);
             }
-            repr::Terminator::Drop { ref target, value: _, unwind: Some(ref unwind) } => {
+            repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => {
                 self.propagate_bits_into_entry_set_for(in_out, changed, target);
                 self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
             }
-            repr::Terminator::If { ref targets, .. } => {
+            repr::TerminatorKind::If { ref targets, .. } => {
                 self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0);
                 self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1);
             }
-            repr::Terminator::Switch { ref targets, .. } |
-            repr::Terminator::SwitchInt { ref targets, .. } => {
+            repr::TerminatorKind::Switch { ref targets, .. } |
+            repr::TerminatorKind::SwitchInt { ref targets, .. } => {
                 for target in targets {
                     self.propagate_bits_into_entry_set_for(in_out, changed, target);
                 }
             }
-            repr::Terminator::Call { ref cleanup, ref destination, func: _, args: _ } => {
+            repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => {
                 if let Some(ref unwind) = *cleanup {
                     self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
                 }
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index 826284f1d78..46eb3d3ca03 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -9,9 +9,8 @@
 // except according to those terms.
 
 
-use rustc::middle::ty;
-use rustc::mir::repr::{self, Mir, BasicBlock, Lvalue, Rvalue};
-use rustc::mir::repr::{StatementKind, Terminator};
+use rustc::middle::ty::TyCtxt;
+use rustc::mir::repr::*;
 use rustc::util::nodemap::FnvHashMap;
 
 use std::cell::{Cell};
@@ -361,7 +360,7 @@ impl<'tcx> MovePathLookup<'tcx> {
     }
 
     fn lookup_proj(&mut self,
-                   proj: &repr::LvalueProjection<'tcx>,
+                   proj: &LvalueProjection<'tcx>,
                    base: MovePathIndex) -> Lookup<MovePathIndex> {
         let MovePathLookup { ref mut projections,
                              ref mut next_index, .. } = *self;
@@ -484,7 +483,7 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
 }
 
 impl<'tcx> MoveData<'tcx> {
-    pub fn gather_moves(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> Self {
+    pub fn gather_moves(mir: &Mir<'tcx>, tcx: &TyCtxt<'tcx>) -> Self {
         gather_moves(mir, tcx)
     }
 }
@@ -495,7 +494,7 @@ enum StmtKind {
     Aggregate, Drop, CallFn, CallArg, Return,
 }
 
-fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> {
+fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &TyCtxt<'tcx>) -> MoveData<'tcx> {
     use self::StmtKind as SK;
 
     let bbs = mir.all_basic_blocks();
@@ -554,9 +553,9 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx>
                         Rvalue::Box(ref _ty) => {
                             // this is creating uninitialized
                             // memory that needs to be initialized.
-                            let deref_lval = Lvalue::Projection(Box::new( repr::Projection {
+                            let deref_lval = Lvalue::Projection(Box::new(Projection {
                                 base: lval.clone(),
-                                elem: repr::ProjectionElem::Deref,
+                                elem: ProjectionElem::Deref,
                             }));
                             bb_ctxt.on_move_out_lval(SK::Box, &deref_lval, source);
                         }
@@ -577,50 +576,48 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx>
             }
         }
 
-        if let Some(ref term) = bb_data.terminator {
-            match *term {
-                Terminator::Goto { target: _ } | Terminator::Resume => { }
+        match bb_data.terminator().kind {
+            TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { }
 
-                Terminator::Return => {
-                    let source = Location { block: bb,
-                                            index: bb_data.statements.len() };
-                    let lval = &Lvalue::ReturnPointer.deref();
-                    bb_ctxt.on_move_out_lval(SK::Return, lval, source);
-                }
+            TerminatorKind::Return => {
+                let source = Location { block: bb,
+                                        index: bb_data.statements.len() };
+                let lval = &Lvalue::ReturnPointer.deref();
+                bb_ctxt.on_move_out_lval(SK::Return, lval, source);
+            }
 
-                Terminator::If { ref cond, targets: _ } => {
-                    // The `cond` is always of (copyable) type `bool`,
-                    // so there will never be anything to move.
-                    let _ = cond;
-                }
+            TerminatorKind::If { ref cond, targets: _ } => {
+                // The `cond` is always of (copyable) type `bool`,
+                // so there will never be anything to move.
+                let _ = cond;
+            }
 
-                Terminator::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
-                Terminator::Switch { adt_def: _, targets: _, ref discr } => {
-                    // The `discr` is not consumed; that is instead
-                    // encoded on specific match arms (and for
-                    // SwitchInt`, it is always a copyable integer
-                    // type anyway).
-                    let _ = discr;
-                }
+            TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
+            TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => {
+                // The `discr` is not consumed; that is instead
+                // encoded on specific match arms (and for
+                // SwitchInt`, it is always a copyable integer
+                // type anyway).
+                let _ = discr;
+            }
 
-                Terminator::Drop { value: ref lval, target: _, unwind: _ } => {
-                    let source = Location { block: bb,
-                                            index: bb_data.statements.len() };
-                    bb_ctxt.on_move_out_lval(SK::Drop, lval, source);
-                }
+            TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => {
+                let source = Location { block: bb,
+                                        index: bb_data.statements.len() };
+                bb_ctxt.on_move_out_lval(SK::Drop, lval, source);
+            }
 
-                Terminator::Call { ref func, ref args, ref destination, cleanup: _ } => {
-                    let source = Location { block: bb,
-                                            index: bb_data.statements.len() };
-                    bb_ctxt.on_operand(SK::CallFn, func, source);
-                    for arg in args {
-                        bb_ctxt.on_operand(SK::CallArg, arg, source);
-                    }
-                    if let Some((ref destination, _bb)) = *destination {
-                        // Create MovePath for `destination`, then
-                        // discard returned index.
-                        bb_ctxt.builder.move_path_for(destination);
-                    }
+            TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
+                let source = Location { block: bb,
+                                        index: bb_data.statements.len() };
+                bb_ctxt.on_operand(SK::CallFn, func, source);
+                for arg in args {
+                    bb_ctxt.on_operand(SK::CallArg, arg, source);
+                }
+                if let Some((ref destination, _bb)) = *destination {
+                    // Create MovePath for `destination`, then
+                    // discard returned index.
+                    bb_ctxt.builder.move_path_for(destination);
                 }
             }
         }
@@ -670,7 +667,7 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx>
 }
 
 struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
-    tcx: &'b ty::TyCtxt<'tcx>,
+    tcx: &'b TyCtxt<'tcx>,
     moves: &'b mut Vec<MoveOut>,
     builder: MovePathDataBuilder<'a, 'tcx>,
     path_map: &'b mut Vec<Vec<MoveOutIndex>>,
@@ -680,7 +677,7 @@ struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
 impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
     fn on_move_out_lval(&mut self,
                         stmt_kind: StmtKind,
-                        lval: &repr::Lvalue<'tcx>,
+                        lval: &Lvalue<'tcx>,
                         source: Location) {
         let tcx = self.tcx;
         let lval_ty = self.builder.mir.lvalue_ty(tcx, lval);
@@ -726,10 +723,10 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
         self.loc_map_bb[i].push(index);
     }
 
-    fn on_operand(&mut self, stmt_kind: StmtKind, operand: &repr::Operand<'tcx>, source: Location) {
+    fn on_operand(&mut self, stmt_kind: StmtKind, operand: &Operand<'tcx>, source: Location) {
         match *operand {
-            repr::Operand::Constant(..) => {} // not-a-move
-            repr::Operand::Consume(ref lval) => { // a move
+            Operand::Constant(..) => {} // not-a-move
+            Operand::Consume(ref lval) => { // a move
                 self.on_move_out_lval(stmt_kind, lval, source);
             }
         }
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 4c80eab102f..ef48a408e79 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -20,7 +20,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                      ast_block: &'tcx hir::Block)
                      -> BlockAnd<()> {
         let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
-        self.in_scope(extent, block, move |this| {
+        self.in_scope(extent, block, move |this, _| {
             // This convoluted structure is to avoid using recursion as we walk down a list
             // of statements. Basically, the structure we get back is something like:
             //
@@ -42,23 +42,24 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 let Stmt { span: _, kind } = this.hir.mirror(stmt);
                 match kind {
                     StmtKind::Expr { scope, expr } => {
-                        unpack!(block = this.in_scope(scope, block, |this| {
+                        unpack!(block = this.in_scope(scope, block, |this, _| {
                             let expr = this.hir.mirror(expr);
+                            let expr_span = expr.span;
                             let temp = this.temp(expr.ty.clone());
                             unpack!(block = this.into(&temp, block, expr));
-                            unpack!(block = this.build_drop(block, temp));
+                            unpack!(block = this.build_drop(block, expr_span, temp));
                             block.unit()
                         }));
                     }
                     StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
-                        this.push_scope(remainder_scope);
+                        let remainder_scope_id = this.push_scope(remainder_scope, block);
                         let_extent_stack.push(remainder_scope);
-                        unpack!(block = this.in_scope(init_scope, block, move |this| {
+                        unpack!(block = this.in_scope(init_scope, block, move |this, _| {
                             // FIXME #30046                              ^~~~
                             if let Some(init) = initializer {
-                                this.expr_into_pattern(block, remainder_scope, pattern, init)
+                                this.expr_into_pattern(block, remainder_scope_id, pattern, init)
                             } else {
-                                this.declare_bindings(remainder_scope, &pattern);
+                                this.declare_bindings(remainder_scope_id, &pattern);
                                 block.unit()
                             }
                         }));
@@ -71,7 +72,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 unpack!(block = this.into(destination, block, expr));
             } else {
                 // FIXME(#31472)
-                this.cfg.push_assign_unit(block, span, destination);
+                let scope_id = this.innermost_scope_id();
+                this.cfg.push_assign_unit(block, scope_id, span, destination);
             }
             // Finally, we pop all the let scopes before exiting out from the scope of block
             // itself.
diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs
index c7147d111aa..4859257f291 100644
--- a/src/librustc_mir/build/cfg.rs
+++ b/src/librustc_mir/build/cfg.rs
@@ -13,7 +13,7 @@
 
 //! Routines for manipulating the control-flow graph.
 
-use build::CFG;
+use build::{CFG, Location};
 use rustc::mir::repr::*;
 use syntax::codemap::Span;
 
@@ -43,12 +43,19 @@ impl<'tcx> CFG<'tcx> {
         self.block_data_mut(block).statements.push(statement);
     }
 
+    pub fn current_location(&mut self, block: BasicBlock) -> Location {
+        let index = self.block_data(block).statements.len();
+        Location { block: block, statement_index: index }
+    }
+
     pub fn push_assign(&mut self,
                        block: BasicBlock,
+                       scope: ScopeId,
                        span: Span,
                        lvalue: &Lvalue<'tcx>,
                        rvalue: Rvalue<'tcx>) {
         self.push(block, Statement {
+            scope: scope,
             span: span,
             kind: StatementKind::Assign(lvalue.clone(), rvalue)
         });
@@ -56,26 +63,35 @@ impl<'tcx> CFG<'tcx> {
 
     pub fn push_assign_constant(&mut self,
                                 block: BasicBlock,
+                                scope: ScopeId,
                                 span: Span,
                                 temp: &Lvalue<'tcx>,
                                 constant: Constant<'tcx>) {
-        self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
+        self.push_assign(block, scope, span, temp,
+                         Rvalue::Use(Operand::Constant(constant)));
     }
 
     pub fn push_assign_unit(&mut self,
                             block: BasicBlock,
+                            scope: ScopeId,
                             span: Span,
                             lvalue: &Lvalue<'tcx>) {
-        self.push_assign(block, span, lvalue, Rvalue::Aggregate(
+        self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate(
             AggregateKind::Tuple, vec![]
         ));
     }
 
     pub fn terminate(&mut self,
                      block: BasicBlock,
-                     terminator: Terminator<'tcx>) {
+                     scope: ScopeId,
+                     span: Span,
+                     kind: TerminatorKind<'tcx>) {
         debug_assert!(self.block_data(block).terminator.is_none(),
                       "terminate: block {:?} already has a terminator set", block);
-        self.block_data_mut(block).terminator = Some(terminator);
+        self.block_data_mut(block).terminator = Some(Terminator {
+            span: span,
+            scope: scope,
+            kind: kind,
+        });
     }
 }
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index b2c7507ed7b..0c9323f4af3 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -34,10 +34,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr);
 
         let this = self;
+        let scope_id = this.innermost_scope_id();
         let expr_span = expr.span;
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this| this.as_lvalue(block, value))
+                this.in_scope(extent, block, |this, _| this.as_lvalue(block, value))
             }
             ExprKind::Field { lhs, name } => {
                 let lvalue = unpack!(block = this.as_lvalue(block, lhs));
@@ -58,16 +59,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
                 // bounds check:
                 let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
-                this.cfg.push_assign(block, expr_span, // len = len(slice)
+                this.cfg.push_assign(block, scope_id, expr_span, // len = len(slice)
                                      &len, Rvalue::Len(slice.clone()));
-                this.cfg.push_assign(block, expr_span, // lt = idx < len
+                this.cfg.push_assign(block, scope_id, expr_span, // lt = idx < len
                                      &lt, Rvalue::BinaryOp(BinOp::Lt,
                                                            idx.clone(),
                                                            Operand::Consume(len.clone())));
 
                 let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
                 this.cfg.terminate(block,
-                                   Terminator::If {
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::If {
                                        cond: Operand::Consume(lt),
                                        targets: (success, failure),
                                    });
diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs
index 7738ebca26b..661d01ce989 100644
--- a/src/librustc_mir/build/expr/as_operand.rs
+++ b/src/librustc_mir/build/expr/as_operand.rs
@@ -35,7 +35,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         let this = self;
 
         if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this| this.as_operand(block, value));
+            return this.in_scope(extent, block, |this, _| this.as_operand(block, value));
         }
 
         let category = Category::of(&expr.kind).unwrap();
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 4c0e9b98d9a..b340d933e64 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -33,11 +33,12 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr);
 
         let this = self;
+        let scope_id = this.innermost_scope_id();
         let expr_span = expr.span;
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this| this.as_rvalue(block, value))
+                this.in_scope(extent, block, |this, _| this.as_rvalue(block, value))
             }
             ExprKind::InlineAsm { asm, outputs, inputs } => {
                 let outputs = outputs.into_iter().map(|output| {
@@ -75,8 +76,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 let value = this.hir.mirror(value);
                 let result = this.temp(expr.ty);
                 // to start, malloc some memory of suitable type (thus far, uninitialized):
-                this.cfg.push_assign(block, expr_span, &result, Rvalue::Box(value.ty));
-                this.in_scope(value_extents, block, |this| {
+                this.cfg.push_assign(block, scope_id, expr_span, &result, Rvalue::Box(value.ty));
+                this.in_scope(value_extents, block, |this, _| {
                     // schedule a shallow free of that memory, lest we unwind:
                     this.schedule_box_free(expr_span, value_extents, &result, value.ty);
                     // initialize the box contents:
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index 27c374e1ac2..30a42bcd709 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -30,7 +30,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         let this = self;
 
         if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this| this.as_temp(block, value));
+            return this.in_scope(extent, block, |this, _| this.as_temp(block, value));
         }
 
         let expr_ty = expr.ty.clone();
@@ -55,7 +55,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 let expr_span = expr.span;
                 let lvalue = unpack!(block = this.as_lvalue(block, expr));
                 let rvalue = Rvalue::Use(Operand::Consume(lvalue));
-                this.cfg.push_assign(block, expr_span, &temp, rvalue);
+                let scope_id = this.innermost_scope_id();
+                this.cfg.push_assign(block, scope_id, expr_span, &temp, rvalue);
             }
             _ => {
                 unpack!(block = this.into(&temp, block, expr));
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index a7f4a53b022..30c039cdde0 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -36,10 +36,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // just use the name `this` uniformly
         let this = self;
         let expr_span = expr.span;
+        let scope_id = this.innermost_scope_id();
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this| this.into(destination, block, value))
+                this.in_scope(extent, block, |this, _| this.into(destination, block, value))
             }
             ExprKind::Block { body: ast_block } => {
                 this.ast_block(destination, block, ast_block)
@@ -52,7 +53,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
                 let mut then_block = this.cfg.start_new_block();
                 let mut else_block = this.cfg.start_new_block();
-                this.cfg.terminate(block, Terminator::If {
+                this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If {
                     cond: operand,
                     targets: (then_block, else_block)
                 });
@@ -63,13 +64,20 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 } else {
                     // Body of the `if` expression without an `else` clause must return `()`, thus
                     // we implicitly generate a `else {}` if it is not specified.
-                    this.cfg.push_assign_unit(else_block, expr_span, destination);
+                    let scope_id = this.innermost_scope_id();
+                    this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination);
                     else_block
                 };
 
                 let join_block = this.cfg.start_new_block();
-                this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
-                this.cfg.terminate(else_block, Terminator::Goto { target: join_block });
+                this.cfg.terminate(then_block,
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::Goto { target: join_block });
+                this.cfg.terminate(else_block,
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::Goto { target: join_block });
 
                 join_block.unit()
             }
@@ -95,16 +103,19 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                     LogicalOp::And => (else_block, false_block),
                     LogicalOp::Or => (true_block, else_block),
                 };
-                this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks });
+                this.cfg.terminate(block,
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::If { cond: lhs, targets: blocks });
 
                 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
-                this.cfg.terminate(else_block, Terminator::If {
+                this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If {
                     cond: rhs,
                     targets: (true_block, false_block)
                 });
 
                 this.cfg.push_assign_constant(
-                    true_block, expr_span, destination,
+                    true_block, scope_id, expr_span, destination,
                     Constant {
                         span: expr_span,
                         ty: this.hir.bool_ty(),
@@ -112,15 +123,21 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                     });
 
                 this.cfg.push_assign_constant(
-                    false_block, expr_span, destination,
+                    false_block, scope_id, expr_span, destination,
                     Constant {
                         span: expr_span,
                         ty: this.hir.bool_ty(),
                         literal: this.hir.false_literal(),
                     });
 
-                this.cfg.terminate(true_block, Terminator::Goto { target: join_block });
-                this.cfg.terminate(false_block, Terminator::Goto { target: join_block });
+                this.cfg.terminate(true_block,
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::Goto { target: join_block });
+                this.cfg.terminate(false_block,
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::Goto { target: join_block });
 
                 join_block.unit()
             }
@@ -144,7 +161,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 let exit_block = this.cfg.start_new_block();
 
                 // start the loop
-                this.cfg.terminate(block, Terminator::Goto { target: loop_block });
+                this.cfg.terminate(block,
+                                   scope_id,
+                                   expr_span,
+                                   TerminatorKind::Goto { target: loop_block });
 
                 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
                     // conduct the test, if necessary
@@ -157,7 +177,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                         let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
                         body_block = this.cfg.start_new_block();
                         this.cfg.terminate(loop_block_end,
-                                           Terminator::If {
+                                           scope_id,
+                                           expr_span,
+                                           TerminatorKind::If {
                                                cond: cond,
                                                targets: (body_block, exit_block)
                                            });
@@ -173,12 +195,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                     let tmp = this.get_unit_temp();
                     // Execute the body, branching back to the test.
                     let body_block_end = unpack!(this.into(&tmp, body_block, body));
-                    this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
+                    this.cfg.terminate(body_block_end,
+                                       scope_id,
+                                       expr_span,
+                                       TerminatorKind::Goto { target: loop_block });
                 });
                 // If the loop may reach its exit_block, we assign an empty tuple to the
                 // destination to keep the MIR well-formed.
                 if might_break {
-                    this.cfg.push_assign_unit(exit_block, expr_span, destination);
+                    this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination);
                 }
                 exit_block.unit()
             }
@@ -186,10 +211,12 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 // Note: we evaluate assignments right-to-left. This
                 // is better for borrowck interaction with overloaded
                 // operators like x[j] = x[i].
+                let lhs = this.hir.mirror(lhs);
+                let lhs_span = lhs.span;
                 let rhs = unpack!(block = this.as_operand(block, rhs));
                 let lhs = unpack!(block = this.as_lvalue(block, lhs));
-                unpack!(block = this.build_drop(block, lhs.clone()));
-                this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs));
+                unpack!(block = this.build_drop(block, lhs_span, lhs.clone()));
+                this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs));
                 block.unit()
             }
             ExprKind::AssignOp { op, lhs, rhs } => {
@@ -208,7 +235,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 // we don't have to drop prior contents or anything
                 // because AssignOp is only legal for Copy types
                 // (overloaded ops should be desugared into a call).
-                this.cfg.push_assign(block, expr_span, &lhs,
+                this.cfg.push_assign(block, scope_id, expr_span, &lhs,
                                      Rvalue::BinaryOp(op,
                                                       Operand::Consume(lhs.clone()),
                                                       rhs));
@@ -229,11 +256,12 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 block = match value {
                     Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
                     None => {
-                        this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
+                        this.cfg.push_assign_unit(block, scope_id,
+                                                  expr_span, &Lvalue::ReturnPointer);
                         block
                     }
                 };
-                let extent = this.extent_of_outermost_scope();
+                let extent = this.extent_of_return_scope();
                 this.exit_scope(expr_span, extent, block, END_BLOCK);
                 this.cfg.start_new_block().unit()
             }
@@ -252,7 +280,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
                 let success = this.cfg.start_new_block();
                 let cleanup = this.diverge_cleanup();
-                this.cfg.terminate(block, Terminator::Call {
+                this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call {
                     func: fun,
                     args: args,
                     cleanup: cleanup,
@@ -293,7 +321,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 });
 
                 let rvalue = unpack!(block = this.as_rvalue(block, expr));
-                this.cfg.push_assign(block, expr_span, destination, rvalue);
+                this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue);
                 block.unit()
             }
         }
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 673ff9e86c4..b1286e935b6 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -16,7 +16,6 @@
 use build::{BlockAnd, BlockAndExtension, Builder};
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc::middle::const_eval::ConstVal;
-use rustc::middle::region::CodeExtent;
 use rustc::middle::ty::{AdtDef, Ty};
 use rustc::mir::repr::*;
 use hair::*;
@@ -42,9 +41,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // suitable extent for all of the bindings in this match. It's
         // easiest to do this up front because some of these arms may
         // be unreachable or reachable multiple times.
-        let var_extent = self.extent_of_innermost_scope();
+        let var_scope_id = self.innermost_scope_id();
         for arm in &arms {
-            self.declare_bindings(var_extent, &arm.patterns[0]);
+            self.declare_bindings(var_scope_id, &arm.patterns[0]);
         }
 
         let mut arm_blocks = ArmBlocks {
@@ -72,6 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 })
                 .map(|(arm_index, pattern, guard)| {
                     Candidate {
+                        span: pattern.span,
                         match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)],
                         bindings: vec![],
                         guard: guard,
@@ -88,7 +88,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // an empty vector to be returned here, but the algorithm is
         // not entirely precise
         if !otherwise.is_empty() {
-            let join_block = self.join_otherwise_blocks(otherwise);
+            let join_block = self.join_otherwise_blocks(span, otherwise);
             self.panic(join_block, "something about matches algorithm not being precise", span);
         }
 
@@ -98,7 +98,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() {
             let mut arm_block = arm_blocks.blocks[arm_index];
             unpack!(arm_block = self.into(destination, arm_block, arm_body));
-            self.cfg.terminate(arm_block, Terminator::Goto { target: end_block });
+            self.cfg.terminate(arm_block,
+                               var_scope_id,
+                               span,
+                               TerminatorKind::Goto { target: end_block });
         }
 
         end_block.unit()
@@ -106,7 +109,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
     pub fn expr_into_pattern(&mut self,
                              mut block: BasicBlock,
-                             var_extent: CodeExtent, // lifetime of vars
+                             var_scope_id: ScopeId, // lifetime of vars
                              irrefutable_pat: Pattern<'tcx>,
                              initializer: ExprRef<'tcx>)
                              -> BlockAnd<()> {
@@ -118,7 +121,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                                    var,
                                    ty,
                                    subpattern: None } => {
-                let index = self.declare_binding(var_extent,
+                let index = self.declare_binding(var_scope_id,
                                                  mutability,
                                                  name,
                                                  var,
@@ -131,22 +134,23 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         }
         let lvalue = unpack!(block = self.as_lvalue(block, initializer));
         self.lvalue_into_pattern(block,
-                                 var_extent,
+                                 var_scope_id,
                                  irrefutable_pat,
                                  &lvalue)
     }
 
     pub fn lvalue_into_pattern(&mut self,
                                mut block: BasicBlock,
-                               var_extent: CodeExtent,
+                               var_scope_id: ScopeId,
                                irrefutable_pat: Pattern<'tcx>,
                                initializer: &Lvalue<'tcx>)
                                -> BlockAnd<()> {
         // first, creating the bindings
-        self.declare_bindings(var_extent, &irrefutable_pat);
+        self.declare_bindings(var_scope_id, &irrefutable_pat);
 
         // create a dummy candidate
         let mut candidate = Candidate {
+            span: irrefutable_pat.span,
             match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
             bindings: vec![],
             guard: None,
@@ -170,29 +174,29 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         block.unit()
     }
 
-    pub fn declare_bindings(&mut self, var_extent: CodeExtent, pattern: &Pattern<'tcx>) {
+    pub fn declare_bindings(&mut self, var_scope_id: ScopeId, pattern: &Pattern<'tcx>) {
         match *pattern.kind {
             PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => {
-                self.declare_binding(var_extent, mutability, name, var, ty, pattern.span);
+                self.declare_binding(var_scope_id, mutability, name, var, ty, pattern.span);
                 if let Some(subpattern) = subpattern.as_ref() {
-                    self.declare_bindings(var_extent, subpattern);
+                    self.declare_bindings(var_scope_id, subpattern);
                 }
             }
             PatternKind::Array { ref prefix, ref slice, ref suffix } |
             PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
                 for subpattern in prefix.iter().chain(slice).chain(suffix) {
-                    self.declare_bindings(var_extent, subpattern);
+                    self.declare_bindings(var_scope_id, subpattern);
                 }
             }
             PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
             }
             PatternKind::Deref { ref subpattern } => {
-                self.declare_bindings(var_extent, subpattern);
+                self.declare_bindings(var_scope_id, subpattern);
             }
             PatternKind::Leaf { ref subpatterns } |
             PatternKind::Variant { ref subpatterns, .. } => {
                 for subpattern in subpatterns {
-                    self.declare_bindings(var_extent, &subpattern.pattern);
+                    self.declare_bindings(var_scope_id, &subpattern.pattern);
                 }
             }
         }
@@ -207,6 +211,9 @@ struct ArmBlocks {
 
 #[derive(Clone, Debug)]
 pub struct Candidate<'pat, 'tcx:'pat> {
+    // span of the original pattern that gave rise to this candidate
+    span: Span,
+
     // all of these must be satisfied...
     match_pairs: Vec<MatchPair<'pat, 'tcx>>,
 
@@ -371,20 +378,25 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         }
 
         // Otherwise, let's process those remaining candidates.
-        let join_block = self.join_otherwise_blocks(otherwise);
+        let join_block = self.join_otherwise_blocks(span, otherwise);
         self.match_candidates(span, arm_blocks, untested_candidates, join_block)
     }
 
     fn join_otherwise_blocks(&mut self,
+                             span: Span,
                              otherwise: Vec<BasicBlock>)
                              -> BasicBlock
     {
+        let scope_id = self.innermost_scope_id();
         if otherwise.len() == 1 {
             otherwise[0]
         } else {
             let join_block = self.cfg.start_new_block();
             for block in otherwise {
-                self.cfg.terminate(block, Terminator::Goto { target: join_block });
+                self.cfg.terminate(block,
+                                   scope_id,
+                                   span,
+                                   TerminatorKind::Goto { target: join_block });
             }
             join_block
         }
@@ -551,16 +563,25 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
         let arm_block = arm_blocks.blocks[candidate.arm_index];
 
+        let scope_id = self.innermost_scope_id();
         if let Some(guard) = candidate.guard {
             // the block to branch to if the guard fails; if there is no
             // guard, this block is simply unreachable
+            let guard = self.hir.mirror(guard);
+            let guard_span = guard.span;
             let cond = unpack!(block = self.as_operand(block, guard));
             let otherwise = self.cfg.start_new_block();
-            self.cfg.terminate(block, Terminator::If { cond: cond,
-                                                       targets: (arm_block, otherwise)});
+            self.cfg.terminate(block,
+                               scope_id,
+                               guard_span,
+                               TerminatorKind::If { cond: cond,
+                                                    targets: (arm_block, otherwise)});
             Some(otherwise)
         } else {
-            self.cfg.terminate(block, Terminator::Goto { target: arm_block });
+            self.cfg.terminate(block,
+                               scope_id,
+                               candidate.span,
+                               TerminatorKind::Goto { target: arm_block });
             None
         }
     }
@@ -585,12 +606,14 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                     Rvalue::Ref(region, borrow_kind, binding.source),
             };
 
-            self.cfg.push_assign(block, binding.span, &Lvalue::Var(var_index), rvalue);
+            let scope_id = self.innermost_scope_id();
+            self.cfg.push_assign(block, scope_id, binding.span,
+                                 &Lvalue::Var(var_index), rvalue);
         }
     }
 
     fn declare_binding(&mut self,
-                       var_extent: CodeExtent,
+                       var_scope_id: ScopeId,
                        mutability: Mutability,
                        name: Name,
                        var_id: NodeId,
@@ -598,17 +621,20 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                        span: Span)
                        -> u32
     {
-        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_extent={:?}, span={:?})",
-               var_id, name, var_ty, var_extent, span);
+        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_scope_id={:?}, span={:?})",
+               var_id, name, var_ty, var_scope_id, span);
 
         let index = self.var_decls.len();
         self.var_decls.push(VarDecl::<'tcx> {
+            scope: var_scope_id,
             mutability: mutability,
             name: name,
             ty: var_ty.clone(),
+            span: span,
         });
         let index = index as u32;
-        self.schedule_drop(span, var_extent, &Lvalue::Var(index), var_ty);
+        let extent = self.scope_auxiliary[var_scope_id].extent;
+        self.schedule_drop(span, extent, &Lvalue::Var(index), var_ty);
         self.var_indices.insert(var_id, index);
 
         debug!("declare_binding: index={:?}", index);
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index 0efa24f3119..3211e5849a0 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -146,13 +146,14 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                         lvalue: &Lvalue<'tcx>,
                         test: &Test<'tcx>)
                         -> Vec<BasicBlock> {
+        let scope_id = self.innermost_scope_id();
         match test.kind {
             TestKind::Switch { adt_def } => {
                 let num_enum_variants = self.hir.num_variants(adt_def);
                 let target_blocks: Vec<_> =
                     (0..num_enum_variants).map(|_| self.cfg.start_new_block())
                                           .collect();
-                self.cfg.terminate(block, Terminator::Switch {
+                self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch {
                     discr: lvalue.clone(),
                     adt_def: adt_def,
                     targets: target_blocks.clone()
@@ -167,12 +168,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                            .map(|_| self.cfg.start_new_block())
                            .chain(Some(otherwise))
                            .collect();
-                self.cfg.terminate(block, Terminator::SwitchInt {
-                    discr: lvalue.clone(),
-                    switch_ty: switch_ty,
-                    values: options.clone(),
-                    targets: targets.clone(),
-                });
+                self.cfg.terminate(block,
+                                   scope_id,
+                                   test.span,
+                                   TerminatorKind::SwitchInt {
+                                       discr: lvalue.clone(),
+                                       switch_ty: switch_ty,
+                                       values: options.clone(),
+                                       targets: targets.clone(),
+                                   });
                 targets
             }
 
@@ -189,7 +193,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                         if let ty::TyArray(_, _) = mt.ty.sty {
                             ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
                             let val_slice = self.temp(ty);
-                            self.cfg.push_assign(block, test.span, &val_slice,
+                            self.cfg.push_assign(block, scope_id, test.span, &val_slice,
                                                  Rvalue::Cast(CastKind::Unsize, val, ty));
                             val = Operand::Consume(val_slice);
                         }
@@ -204,7 +208,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                     });
 
                     let slice = self.temp(ty);
-                    self.cfg.push_assign(block, test.span, &slice,
+                    self.cfg.push_assign(block, scope_id, test.span, &slice,
                                          Rvalue::Cast(CastKind::Unsize, array, ty));
                     Operand::Consume(slice)
                 } else {
@@ -225,7 +229,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                     let eq_result = self.temp(bool_ty);
                     let eq_block = self.cfg.start_new_block();
                     let cleanup = self.diverge_cleanup();
-                    self.cfg.terminate(block, Terminator::Call {
+                    self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Call {
                         func: Operand::Constant(Constant {
                             span: test.span,
                             ty: mty,
@@ -238,7 +242,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
                     // check the result
                     let block = self.cfg.start_new_block();
-                    self.cfg.terminate(eq_block, Terminator::If {
+                    self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If {
                         cond: Operand::Consume(eq_result),
                         targets: (block, fail),
                     });
@@ -268,13 +272,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
 
                 // actual = len(lvalue)
-                self.cfg.push_assign(block, test.span, &actual, Rvalue::Len(lvalue.clone()));
+                self.cfg.push_assign(block, scope_id, test.span,
+                                     &actual, Rvalue::Len(lvalue.clone()));
 
                 // expected = <N>
-                let expected = self.push_usize(block, test.span, len);
+                let expected = self.push_usize(block, scope_id, test.span, len);
 
                 // result = actual == expected OR result = actual < expected
                 self.cfg.push_assign(block,
+                                     scope_id,
                                      test.span,
                                      &result,
                                      Rvalue::BinaryOp(op,
@@ -284,7 +290,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 // branch based on result
                 let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
                                                  self.cfg.start_new_block()];
-                self.cfg.terminate(block, Terminator::If {
+                self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If {
                     cond: Operand::Consume(result),
                     targets: (target_blocks[0], target_blocks[1])
                 });
@@ -305,11 +311,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         let result = self.temp(bool_ty);
 
         // result = op(left, right)
-        self.cfg.push_assign(block, span, &result, Rvalue::BinaryOp(op, left, right));
+        let scope_id = self.innermost_scope_id();
+        self.cfg.push_assign(block, scope_id, span, &result,
+                             Rvalue::BinaryOp(op, left, right));
 
         // branch based on result
         let target_block = self.cfg.start_new_block();
-        self.cfg.terminate(block, Terminator::If {
+        self.cfg.terminate(block, scope_id, span, TerminatorKind::If {
             cond: Operand::Consume(result),
             targets: (target_block, fail_block)
         });
@@ -462,6 +470,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                                  .map(|(_, mp)| mp.clone())
                                  .collect();
         Candidate {
+            span: candidate.span,
             match_pairs: other_match_pairs,
             bindings: candidate.bindings.clone(),
             guard: candidate.guard.clone(),
@@ -503,6 +512,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
 
         Candidate {
+            span: candidate.span,
             match_pairs: all_match_pairs,
             bindings: candidate.bindings.clone(),
             guard: candidate.guard.clone(),
diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs
index b46c3ffb76a..101d7594309 100644
--- a/src/librustc_mir/build/matches/util.rs
+++ b/src/librustc_mir/build/matches/util.rs
@@ -61,7 +61,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 from_end: suffix_len,
             };
             let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy
-            self.cfg.push_assign(block, slice.span, &temp, rvalue);
+            let scope_id = self.innermost_scope_id();
+            self.cfg.push_assign(block, scope_id, slice.span, &temp, rvalue);
             match_pairs.push(MatchPair::new(temp, slice));
         }
 
diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs
index 13ab26c358d..0d169078330 100644
--- a/src/librustc_mir/build/misc.rs
+++ b/src/librustc_mir/build/misc.rs
@@ -46,11 +46,16 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         Operand::Constant(constant)
     }
 
-    pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: u64) -> Lvalue<'tcx> {
+    pub fn push_usize(&mut self,
+                      block: BasicBlock,
+                      scope_id: ScopeId,
+                      span: Span,
+                      value: u64)
+                      -> Lvalue<'tcx> {
         let usize_ty = self.hir.usize_ty();
         let temp = self.temp(usize_ty);
         self.cfg.push_assign_constant(
-            block, span, &temp,
+            block, scope_id, span, &temp,
             Constant {
                 span: span,
                 ty: self.hir.usize_ty(),
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index b40775f939f..b79f492179f 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -9,30 +9,106 @@
 // except according to those terms.
 
 use hair::cx::Cx;
-use rustc::middle::region::CodeExtent;
+use rustc::middle::region::{CodeExtent, CodeExtentData};
 use rustc::middle::ty::{FnOutput, Ty};
 use rustc::mir::repr::*;
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc_front::hir;
-
+use std::ops::{Index, IndexMut};
 use syntax::ast;
 use syntax::codemap::Span;
 
 pub struct Builder<'a, 'tcx: 'a> {
     hir: Cx<'a, 'tcx>,
     cfg: CFG<'tcx>,
+
+    fn_span: Span,
+
+    // the current set of scopes, updated as we traverse;
+    // see the `scope` module for more details
     scopes: Vec<scope::Scope<'tcx>>,
+
+    // for each scope, a span of blocks that defines it;
+    // we track these for use in region and borrow checking,
+    // but these are liable to get out of date once optimization
+    // begins. They are also hopefully temporary, and will be
+    // no longer needed when we adopt graph-based regions.
+    scope_auxiliary: ScopeAuxiliaryVec,
+
+    // the current set of loops; see the `scope` module for more
+    // details
     loop_scopes: Vec<scope::LoopScope>,
+
+    // the vector of all scopes that we have created thus far;
+    // we track this for debuginfo later
+    scope_datas: Vec<ScopeData>,
+
     var_decls: Vec<VarDecl<'tcx>>,
     var_indices: FnvHashMap<ast::NodeId, u32>,
     temp_decls: Vec<TempDecl<'tcx>>,
     unit_temp: Option<Lvalue<'tcx>>,
+
+    // cached block with a RESUME terminator; we create this at the
+    // first panic
+    cached_resume_block: Option<BasicBlock>,
 }
 
 struct CFG<'tcx> {
     basic_blocks: Vec<BasicBlockData<'tcx>>,
 }
 
+/// For each scope, we track the extent (from the HIR) and a
+/// single-entry-multiple-exit subgraph that contains all the
+/// statements/terminators within it.
+///
+/// This information is separated out from the main `ScopeData`
+/// because it is short-lived. First, the extent contains node-ids,
+/// so it cannot be saved and re-loaded. Second, any optimization will mess up
+/// the dominator/postdominator information.
+///
+/// The intention is basically to use this information to do
+/// regionck/borrowck and then throw it away once we are done.
+pub struct ScopeAuxiliary {
+    /// extent of this scope from the MIR.
+    pub extent: CodeExtent,
+
+    /// "entry point": dominator of all nodes in the scope
+    pub dom: Location,
+
+    /// "exit points": mutual postdominators of all nodes in the scope
+    pub postdoms: Vec<Location>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Location {
+    /// the location is within this block
+    pub block: BasicBlock,
+
+    /// the location is the start of the this statement; or, if `statement_index`
+    /// == num-statements, then the start of the terminator.
+    pub statement_index: usize,
+}
+
+pub struct ScopeAuxiliaryVec {
+    pub vec: Vec<ScopeAuxiliary>
+}
+
+impl Index<ScopeId> for ScopeAuxiliaryVec {
+    type Output = ScopeAuxiliary;
+
+    #[inline]
+    fn index(&self, index: ScopeId) -> &ScopeAuxiliary {
+        &self.vec[index.index()]
+    }
+}
+
+impl IndexMut<ScopeId> for ScopeAuxiliaryVec {
+    #[inline]
+    fn index_mut(&mut self, index: ScopeId) -> &mut ScopeAuxiliary {
+        &mut self.vec[index.index()]
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 /// The `BlockAnd` "monad" packages up the new basic block along with a
 /// produced value (sometimes just unit, of course). The `unpack!`
@@ -81,46 +157,85 @@ macro_rules! unpack {
 
 pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
                           span: Span,
+                          fn_id: ast::NodeId,
+                          body_id: ast::NodeId,
                           implicit_arguments: Vec<Ty<'tcx>>,
                           explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
-                          argument_extent: CodeExtent,
                           return_ty: FnOutput<'tcx>,
                           ast_block: &'tcx hir::Block)
-                          -> Mir<'tcx> {
+                          -> (Mir<'tcx>, ScopeAuxiliaryVec) {
+    let tcx = hir.tcx();
     let cfg = CFG { basic_blocks: vec![] };
 
     let mut builder = Builder {
         hir: hir,
         cfg: cfg,
+        fn_span: span,
         scopes: vec![],
+        scope_datas: vec![],
+        scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
         loop_scopes: vec![],
         temp_decls: vec![],
         var_decls: vec![],
         var_indices: FnvHashMap(),
         unit_temp: None,
+        cached_resume_block: None,
     };
 
     assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
     assert_eq!(builder.cfg.start_new_block(), END_BLOCK);
 
-    let mut block = START_BLOCK;
-    let arg_decls = unpack!(block = builder.args_and_body(block,
-                                                          implicit_arguments,
-                                                          explicit_arguments,
-                                                          argument_extent,
-                                                          ast_block));
-
-    builder.cfg.terminate(block, Terminator::Goto { target: END_BLOCK });
-    builder.cfg.terminate(END_BLOCK, Terminator::Return);
-
-    Mir {
-        basic_blocks: builder.cfg.basic_blocks,
-        var_decls: builder.var_decls,
-        arg_decls: arg_decls,
-        temp_decls: builder.temp_decls,
-        return_ty: return_ty,
-        span: span
-    }
+
+    let mut arg_decls = None; // assigned to `Some` in closures below
+    let call_site_extent =
+        tcx.region_maps.lookup_code_extent(
+            CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id });
+    let _ = builder.in_scope(call_site_extent, START_BLOCK, |builder, call_site_scope_id| {
+        let mut block = START_BLOCK;
+        let arg_extent =
+            tcx.region_maps.lookup_code_extent(
+                CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
+        unpack!(block = builder.in_scope(arg_extent, block, |builder, arg_scope_id| {
+            arg_decls = Some(unpack!(block = builder.args_and_body(block,
+                                                                   implicit_arguments,
+                                                                   explicit_arguments,
+                                                                   arg_scope_id,
+                                                                   ast_block)));
+            block.unit()
+        }));
+
+        builder.cfg.terminate(block, call_site_scope_id, span,
+                              TerminatorKind::Goto { target: END_BLOCK });
+        builder.cfg.terminate(END_BLOCK, call_site_scope_id, span,
+                              TerminatorKind::Return);
+
+        END_BLOCK.unit()
+    });
+
+    assert!(
+        builder.cfg.basic_blocks
+                   .iter()
+                   .enumerate()
+                   .all(|(index, block)| {
+                       if block.terminator.is_none() {
+                           panic!("no terminator on block {:?} in fn {:?}",
+                               index, fn_id)
+                       }
+                       true
+                   }));
+
+    (
+        Mir {
+            basic_blocks: builder.cfg.basic_blocks,
+            scopes: builder.scope_datas,
+            var_decls: builder.var_decls,
+            arg_decls: arg_decls.take().expect("args never built?"),
+            temp_decls: builder.temp_decls,
+            return_ty: return_ty,
+            span: span
+        },
+        builder.scope_auxiliary,
+    )
 }
 
 impl<'a,'tcx> Builder<'a,'tcx> {
@@ -128,39 +243,40 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                      mut block: BasicBlock,
                      implicit_arguments: Vec<Ty<'tcx>>,
                      explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
-                     argument_extent: CodeExtent,
+                     argument_scope_id: ScopeId,
                      ast_block: &'tcx hir::Block)
                      -> BlockAnd<Vec<ArgDecl<'tcx>>>
     {
-        self.in_scope(argument_extent, block, |this| {
-            // to start, translate the argument patterns and collect the argument types.
-            let implicits = implicit_arguments.into_iter().map(|ty| (ty, None));
-            let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat)));
+        // to start, translate the argument patterns and collect the argument types.
+        let implicits = implicit_arguments.into_iter().map(|ty| (ty, None));
+        let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat)));
             let arg_decls =
-                implicits
-                .chain(explicits)
-                .enumerate()
-                .map(|(index, (ty, pattern))| {
-                    let lvalue = Lvalue::Arg(index as u32);
-                    if let Some(pattern) = pattern {
-                        let pattern = this.hir.irrefutable_pat(pattern);
-                        unpack!(block = this.lvalue_into_pattern(block,
-                                                                 argument_extent,
-                                                                 pattern,
-                                                                 &lvalue));
-                    }
-                    // Make sure we drop (parts of) the argument even when not matched on.
-                    this.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
-                                       argument_extent, &lvalue, ty);
-                    ArgDecl { ty: ty, spread: false }
-                })
-                .collect();
-
-            // start the first basic block and translate the body
-            unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block));
-
-            block.and(arg_decls)
-        })
+            implicits
+            .chain(explicits)
+            .enumerate()
+            .map(|(index, (ty, pattern))| {
+                let lvalue = Lvalue::Arg(index as u32);
+                if let Some(pattern) = pattern {
+                    let pattern = self.hir.irrefutable_pat(pattern);
+                    unpack!(block = self.lvalue_into_pattern(block,
+                                                             argument_scope_id,
+                                                             pattern,
+                                                             &lvalue));
+                }
+
+                // Make sure we drop (parts of) the argument even when not matched on.
+                let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
+                self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
+                                   argument_extent, &lvalue, ty);
+
+                ArgDecl { ty: ty, spread: false }
+            })
+            .collect();
+
+        // start the first basic block and translate the body
+        unpack!(block = self.ast_block(&Lvalue::ReturnPointer, block, ast_block));
+
+        block.and(arg_decls)
     }
 
     fn get_unit_temp(&mut self) -> Lvalue<'tcx> {
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 6d411b9c07b..f5f6f409eab 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -86,8 +86,8 @@ should go to.
 
 */
 
-use build::{BlockAnd, BlockAndExtension, Builder, CFG};
-use rustc::middle::region::CodeExtent;
+use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary};
+use rustc::middle::region::{CodeExtent, CodeExtentData};
 use rustc::middle::lang_items;
 use rustc::middle::subst::{Substs, Subst, VecPerParamSpace};
 use rustc::middle::ty::{self, Ty, TyCtxt};
@@ -98,35 +98,66 @@ use rustc::middle::const_eval::ConstVal;
 use rustc_const_eval::ConstInt;
 
 pub struct Scope<'tcx> {
+    /// the scope-id within the scope_datas
+    id: ScopeId,
+
+    /// the extent of this scope within source code; also stored in
+    /// `ScopeAuxiliary`, but kept here for convenience
     extent: CodeExtent,
+
+    /// set of lvalues to drop when exiting this scope. This starts
+    /// out empty but grows as variables are declared during the
+    /// building process. This is a stack, so we always drop from the
+    /// end of the vector (top of the stack) first.
     drops: Vec<DropData<'tcx>>,
-    // A scope may only have one associated free, because:
-    // 1. We require a `free` to only be scheduled in the scope of `EXPR` in `box EXPR`;
-    // 2. It only makes sense to have it translated into the diverge-path.
-    //
-    // This kind of drop will be run *after* all the regular drops scheduled onto this scope,
-    // because drops may have dependencies on the allocated memory.
-    //
-    // This is expected to go away once `box EXPR` becomes a sugar for placement protocol and gets
-    // desugared in some earlier stage.
+
+    /// A scope may only have one associated free, because:
+    ///
+    /// 1. We require a `free` to only be scheduled in the scope of
+    ///    `EXPR` in `box EXPR`;
+    /// 2. It only makes sense to have it translated into the diverge-path.
+    ///
+    /// This kind of drop will be run *after* all the regular drops
+    /// scheduled onto this scope, because drops may have dependencies
+    /// on the allocated memory.
+    ///
+    /// This is expected to go away once `box EXPR` becomes a sugar
+    /// for placement protocol and gets desugared in some earlier
+    /// stage.
     free: Option<FreeData<'tcx>>,
+
+    /// The cached block for the cleanups-on-diverge path. This block
+    /// contains a block that will just do a RESUME to an appropriate
+    /// place. This block does not execute any of the drops or free:
+    /// each of those has their own cached-blocks, which will branch
+    /// to this point.
+    cached_block: Option<BasicBlock>
 }
 
 struct DropData<'tcx> {
+    /// span where drop obligation was incurred (typically where lvalue was declared)
+    span: Span,
+
+    /// lvalue to drop
     value: Lvalue<'tcx>,
-    // NB: per-drop “cache” is necessary for the build_scope_drops function below.
-    /// The cached block for the cleanups-on-diverge path. This block contains code to run the
-    /// current drop and all the preceding drops (i.e. those having lower index in Drop’s
-    /// Scope drop array)
+
+    /// The cached block for the cleanups-on-diverge path. This block
+    /// contains code to run the current drop and all the preceding
+    /// drops (i.e. those having lower index in Drop’s Scope drop
+    /// array)
     cached_block: Option<BasicBlock>
 }
 
 struct FreeData<'tcx> {
+    /// span where free obligation was incurred
     span: Span,
+
     /// Lvalue containing the allocated box.
     value: Lvalue<'tcx>,
+
     /// type of item for which the box was allocated for (i.e. the T in Box<T>).
     item_ty: Ty<'tcx>,
+
     /// The cached block containing code to run the free. The block will also execute all the drops
     /// in the scope.
     cached_block: Option<BasicBlock>
@@ -151,6 +182,7 @@ impl<'tcx> Scope<'tcx> {
     /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
     /// larger extent of code.
     fn invalidate_cache(&mut self) {
+        self.cached_block = None;
         for dropdata in &mut self.drops {
             dropdata.cached_block = None;
         }
@@ -205,11 +237,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     /// Convenience wrapper that pushes a scope and then executes `f`
     /// to build its contents, popping the scope afterwards.
     pub fn in_scope<F, R>(&mut self, extent: CodeExtent, mut block: BasicBlock, f: F) -> BlockAnd<R>
-        where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>
+        where F: FnOnce(&mut Builder<'a, 'tcx>, ScopeId) -> BlockAnd<R>
     {
         debug!("in_scope(extent={:?}, block={:?})", extent, block);
-        self.push_scope(extent);
-        let rv = unpack!(block = f(self));
+        let id = self.push_scope(extent, block);
+        let rv = unpack!(block = f(self, id));
         unpack!(block = self.pop_scope(extent, block));
         debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
         block.and(rv)
@@ -219,26 +251,46 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     /// scope and call `pop_scope` afterwards. Note that these two
     /// calls must be paired; using `in_scope` as a convenience
     /// wrapper maybe preferable.
-    pub fn push_scope(&mut self, extent: CodeExtent) {
+    pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) -> ScopeId {
         debug!("push_scope({:?})", extent);
+        let parent_id = self.scopes.last().map(|s| s.id);
+        let id = ScopeId::new(self.scope_datas.len());
+        self.scope_datas.push(ScopeData {
+            parent_scope: parent_id,
+        });
         self.scopes.push(Scope {
-            extent: extent.clone(),
+            id: id,
+            extent: extent,
             drops: vec![],
-            free: None
+            free: None,
+            cached_block: None,
         });
+        self.scope_auxiliary.vec.push(ScopeAuxiliary {
+            extent: extent,
+            dom: self.cfg.current_location(entry),
+            postdoms: vec![]
+        });
+        id
     }
 
     /// Pops a scope, which should have extent `extent`, adding any
     /// drops onto the end of `block` that are needed.  This must
     /// match 1-to-1 with `push_scope`.
-    pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) -> BlockAnd<()> {
+    pub fn pop_scope(&mut self,
+                     extent: CodeExtent,
+                     mut block: BasicBlock)
+                     -> BlockAnd<()> {
         debug!("pop_scope({:?}, {:?})", extent, block);
         // We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
         // to make sure all the `cached_block`s are filled in.
         self.diverge_cleanup();
         let scope = self.scopes.pop().unwrap();
         assert_eq!(scope.extent, extent);
-        build_scope_drops(&mut self.cfg, &scope, &self.scopes[..], block)
+        unpack!(block = build_scope_drops(&mut self.cfg, &scope, &self.scopes, block));
+        self.scope_auxiliary[scope.id]
+            .postdoms
+            .push(self.cfg.current_location(block));
+        block.unit()
     }
 
 
@@ -265,12 +317,24 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                                               block));
             if let Some(ref free_data) = scope.free {
                 let next = self.cfg.start_new_block();
-                let free = build_free(self.hir.tcx(), tmp.clone(), free_data, next);
-                self.cfg.terminate(block, free);
+                let free = build_free(self.hir.tcx(), &tmp, free_data, next);
+                self.cfg.terminate(block, scope.id, span, free);
                 block = next;
             }
+            self.scope_auxiliary[scope.id]
+                .postdoms
+                .push(self.cfg.current_location(block));
         }
-        self.cfg.terminate(block, Terminator::Goto { target: target });
+
+        assert!(scope_count < self.scopes.len(),
+                "should never use `exit_scope` to pop *ALL* scopes");
+        let scope = self.scopes.iter().rev().skip(scope_count)
+                                            .next()
+                                            .unwrap();
+        self.cfg.terminate(block,
+                           scope.id,
+                           span,
+                           TerminatorKind::Goto { target: target });
     }
 
     // Finding scopes
@@ -297,12 +361,25 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         }.unwrap_or_else(|| hir.span_bug(span, "no enclosing loop scope found?"))
     }
 
+    pub fn innermost_scope_id(&self) -> ScopeId {
+        self.scopes.last().map(|scope| scope.id).unwrap()
+    }
+
     pub fn extent_of_innermost_scope(&self) -> CodeExtent {
         self.scopes.last().map(|scope| scope.extent).unwrap()
     }
 
-    pub fn extent_of_outermost_scope(&self) -> CodeExtent {
-        self.scopes.first().map(|scope| scope.extent).unwrap()
+    /// Returns the extent of the scope which should be exited by a
+    /// return.
+    pub fn extent_of_return_scope(&self) -> CodeExtent {
+        // The outermost scope (`scopes[0]`) will be the `CallSiteScope`.
+        // We want `scopes[1]`, which is the `ParameterScope`.
+        assert!(self.scopes.len() >= 2);
+        assert!(match self.hir.tcx().region_maps.code_extent_data(self.scopes[1].extent) {
+            CodeExtentData::ParameterScope { .. } => true,
+            _ => false,
+        });
+        self.scopes[1].extent
     }
 
     // Scheduling drops
@@ -322,6 +399,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 // No need to invalidate any caches here. The just-scheduled drop will branch into
                 // the drop that comes before it in the vector.
                 scope.drops.push(DropData {
+                    span: span,
                     value: lvalue.clone(),
                     cached_block: None
                 });
@@ -378,36 +456,60 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     /// See module comment for more details. None indicates there’s no
     /// cleanup to do at this point.
     pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
-        if self.scopes.is_empty() {
+        if self.scopes.iter().all(|scope| scope.drops.is_empty() && scope.free.is_none()) {
             return None;
         }
+        assert!(!self.scopes.is_empty()); // or `all` above would be true
+
         let unit_temp = self.get_unit_temp();
-        let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = *self;
-        let mut next_block = None;
-
-        // Given an array of scopes, we generate these from the outermost scope to the innermost
-        // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
-        // generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks
-        // always ends up at a block with the Resume terminator.
-        for scope in scopes.iter_mut().filter(|s| !s.drops.is_empty() || s.free.is_some()) {
-            next_block = Some(build_diverge_scope(hir.tcx(),
-                                                  cfg,
-                                                  unit_temp.clone(),
-                                                  scope,
-                                                  next_block));
+        let Builder { ref mut hir, ref mut cfg, ref mut scopes,
+                      ref mut cached_resume_block, .. } = *self;
+
+        // Build up the drops in **reverse** order. The end result will
+        // look like:
+        //
+        //    scopes[n] -> scopes[n-1] -> ... -> scopes[0]
+        //
+        // However, we build this in **reverse order**. That is, we
+        // process scopes[0], then scopes[1], etc, pointing each one at
+        // the result generates from the one before. Along the way, we
+        // store caches. If everything is cached, we'll just walk right
+        // to left reading the cached results but never created anything.
+
+        // To start, create the resume terminator.
+        let mut target = if let Some(target) = *cached_resume_block {
+            target
+        } else {
+            let resumeblk = cfg.start_new_cleanup_block();
+            cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume);
+            *cached_resume_block = Some(resumeblk);
+            resumeblk
+        };
+
+        for scope in scopes {
+            target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
         }
-        scopes.iter().rev().flat_map(|x| x.cached_block()).next()
+
+        Some(target)
     }
 
     /// Utility function for *non*-scope code to build their own drops
-    pub fn build_drop(&mut self, block: BasicBlock, value: Lvalue<'tcx>) -> BlockAnd<()> {
+    pub fn build_drop(&mut self,
+                      block: BasicBlock,
+                      span: Span,
+                      value: Lvalue<'tcx>)
+                      -> BlockAnd<()> {
+        let scope_id = self.innermost_scope_id();
         let next_target = self.cfg.start_new_block();
         let diverge_target = self.diverge_cleanup();
-        self.cfg.terminate(block, Terminator::Drop {
-            value: value,
-            target: next_target,
-            unwind: diverge_target,
-        });
+        self.cfg.terminate(block,
+                           scope_id,
+                           span,
+                           TerminatorKind::Drop {
+                               value: value,
+                               target: next_target,
+                               unwind: diverge_target,
+                           });
         next_target.unit()
     }
 
@@ -416,10 +518,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     // =========
     // FIXME: should be moved into their own module
     pub fn panic_bounds_check(&mut self,
-                             block: BasicBlock,
-                             index: Operand<'tcx>,
-                             len: Operand<'tcx>,
-                             span: Span) {
+                              block: BasicBlock,
+                              index: Operand<'tcx>,
+                              len: Operand<'tcx>,
+                              span: Span) {
         // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
         let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
         let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
@@ -435,16 +537,17 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
         let (file, line) = self.span_to_fileline_args(span);
         let elems = vec![Operand::Constant(file), Operand::Constant(line)];
+        let scope_id = self.innermost_scope_id();
         // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
         // icache with cold branch code), however to achieve that we either have to rely on rvalue
         // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, span, &tuple, // tuple = (file_arg, line_arg);
+        self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (file_arg, line_arg);
                              Rvalue::Aggregate(AggregateKind::Tuple, elems));
         // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
+        self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
                              Rvalue::Ref(region, BorrowKind::Shared, tuple));
         let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, Terminator::Call {
+        self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
             func: Operand::Constant(func),
             args: vec![Operand::Consume(tuple_ref), index, len],
             destination: None,
@@ -476,16 +579,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         let elems = vec![Operand::Constant(message),
                          Operand::Constant(file),
                          Operand::Constant(line)];
+        let scope_id = self.innermost_scope_id();
         // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
         // icache with cold branch code), however to achieve that we either have to rely on rvalue
         // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, span, &tuple, // tuple = (message_arg, file_arg, line_arg);
+        self.cfg.push_assign(block, scope_id, span, &tuple, // [1]
                              Rvalue::Aggregate(AggregateKind::Tuple, elems));
+        // [1] tuple = (message_arg, file_arg, line_arg);
         // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
+        self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
                              Rvalue::Ref(region, BorrowKind::Shared, tuple));
         let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, Terminator::Call {
+        self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
             func: Operand::Constant(func),
             args: vec![Operand::Consume(tuple_ref)],
             cleanup: cleanup,
@@ -544,7 +649,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
             earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
         });
         let next = cfg.start_new_block();
-        cfg.terminate(block, Terminator::Drop {
+        cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop {
             value: drop_data.value.clone(),
             target: next,
             unwind: on_diverge
@@ -556,89 +661,75 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
 
 fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>,
                              cfg: &mut CFG<'tcx>,
-                             unit_temp: Lvalue<'tcx>,
+                             unit_temp: &Lvalue<'tcx>,
                              scope: &mut Scope<'tcx>,
-                             target: Option<BasicBlock>)
-                             -> BasicBlock {
-    debug_assert!(!scope.drops.is_empty() || scope.free.is_some());
-
-    // First, we build the drops, iterating the drops array in reverse. We do that so that as soon
-    // as we find a `cached_block`, we know that we’re finished and don’t need to do anything else.
-    let mut previous = None;
-    let mut last_drop_block = None;
-    for drop_data in scope.drops.iter_mut().rev() {
-        if let Some(cached_block) = drop_data.cached_block {
-            if let Some((previous_block, previous_value)) = previous {
-                cfg.terminate(previous_block, Terminator::Drop {
-                    value: previous_value,
-                    target: cached_block,
-                    unwind: None
-                });
-                return last_drop_block.unwrap();
-            } else {
-                return cached_block;
-            }
-        } else {
-            let block = cfg.start_new_cleanup_block();
-            drop_data.cached_block = Some(block);
-            if let Some((previous_block, previous_value)) = previous {
-                cfg.terminate(previous_block, Terminator::Drop {
-                    value: previous_value,
-                    target: block,
-                    unwind: None
-                });
-            } else {
-                last_drop_block = Some(block);
-            }
-            previous = Some((block, drop_data.value.clone()));
-        }
-    }
-
-    // Prepare the end target for this chain.
-    let mut target = target.unwrap_or_else(||{
-        let b = cfg.start_new_cleanup_block();
-        cfg.terminate(b, Terminator::Resume);
-        b
-    });
+                             mut target: BasicBlock)
+                             -> BasicBlock
+{
+    // Build up the drops in **reverse** order. The end result will
+    // look like:
+    //
+    //    [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
+    //    |                                    |
+    //    +------------------------------------+
+    //     code for scope
+    //
+    // The code in this function reads from right to left. At each
+    // point, we check for cached blocks representing the
+    // remainder. If everything is cached, we'll just walk right to
+    // left reading the cached results but never created anything.
 
-    // Then, build the free branching into the prepared target.
+    // Next, build up any free.
     if let Some(ref mut free_data) = scope.free {
         target = if let Some(cached_block) = free_data.cached_block {
             cached_block
         } else {
             let into = cfg.start_new_cleanup_block();
-            cfg.terminate(into, build_free(tcx, unit_temp, free_data, target));
+            cfg.terminate(into,
+                          scope.id,
+                          free_data.span,
+                          build_free(tcx, unit_temp, free_data, target));
             free_data.cached_block = Some(into);
             into
-        }
-    };
-
-    if let Some((previous_block, previous_value)) = previous {
-        // Finally, branch into that just-built `target` from the `previous_block`.
-        cfg.terminate(previous_block, Terminator::Drop {
-            value: previous_value,
-            target: target,
-            unwind: None
-        });
-        last_drop_block.unwrap()
-    } else {
-        // If `previous.is_none()`, there were no drops in this scope – we return the
-        // target.
-        target
+        };
+    }
+
+    // Next, build up the drops. Here we iterate the vector in
+    // *forward* order, so that we generate drops[0] first (right to
+    // left in diagram above).
+    for drop_data in &mut scope.drops {
+        target = if let Some(cached_block) = drop_data.cached_block {
+            cached_block
+        } else {
+            let block = cfg.start_new_cleanup_block();
+            cfg.terminate(block,
+                          scope.id,
+                          drop_data.span,
+                          TerminatorKind::Drop {
+                              value: drop_data.value.clone(),
+                              target: target,
+                              unwind: None
+                          });
+            drop_data.cached_block = Some(block);
+            block
+        };
     }
+
+    target
 }
 
 fn build_free<'tcx>(tcx: &TyCtxt<'tcx>,
-                    unit_temp: Lvalue<'tcx>,
+                    unit_temp: &Lvalue<'tcx>,
                     data: &FreeData<'tcx>,
-                    target: BasicBlock) -> Terminator<'tcx> {
+                    target: BasicBlock)
+                    -> TerminatorKind<'tcx> {
     let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem)
                        .unwrap_or_else(|e| tcx.sess.fatal(&e));
     let substs = tcx.mk_substs(Substs::new(
         VecPerParamSpace::new(vec![], vec![], vec![data.item_ty]),
         VecPerParamSpace::new(vec![], vec![], vec![])
     ));
-    Terminator::Call {
+    TerminatorKind::Call {
         func: Operand::Constant(Constant {
             span: data.span,
             ty: tcx.lookup_item_type(free_func).ty.subst(tcx, substs),
@@ -648,7 +739,7 @@ fn build_free<'tcx>(tcx: &TyCtxt<'tcx>,
             }
         }),
         args: vec![Operand::Consume(data.value.clone())],
-        destination: Some((unit_temp, target)),
+        destination: Some((unit_temp.clone(), target)),
         cleanup: None
     }
 }
diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs
index 953eb724f50..a5e749ea687 100644
--- a/src/librustc_mir/graphviz.rs
+++ b/src/librustc_mir/graphviz.rs
@@ -83,7 +83,7 @@ pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
     // Terminator head at the bottom, not including the list of successor blocks. Those will be
     // displayed as labels on the edges between blocks.
     let mut terminator_head = String::new();
-    data.terminator().fmt_head(&mut terminator_head).unwrap();
+    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
     write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
 
     fini(w)?;
@@ -104,7 +104,7 @@ fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<(
 /// Write graphviz DOT edges with labels between the given basic block and all of its successors.
 fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
     let terminator = &mir.basic_block_data(source).terminator();
-    let labels = terminator.fmt_successor_labels();
+    let labels = terminator.kind.fmt_successor_labels();
 
     for (&target, label) in terminator.successors().iter().zip(labels) {
         writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label)?;
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 05dbd63ef1a..5a6e2edfdf9 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -22,11 +22,11 @@ extern crate rustc_front;
 use build;
 use rustc::dep_graph::DepNode;
 use rustc::mir::repr::Mir;
+use pretty;
 use hair::cx::Cx;
 
 use rustc::mir::mir_map::MirMap;
 use rustc::middle::infer;
-use rustc::middle::region::CodeExtentData;
 use rustc::middle::traits::ProjectionMode;
 use rustc::middle::ty::{self, Ty, TyCtxt};
 use rustc::util::common::ErrorReported;
@@ -179,11 +179,15 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
             })
             .collect();
 
-    let parameter_scope =
-        cx.tcx().region_maps.lookup_code_extent(
-            CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
-    let mut mir = build::construct(cx, span, implicit_arg_tys, arguments,
-                                  parameter_scope, fn_sig.output, body);
+    let (mut mir, scope_auxiliary) =
+        build::construct(cx,
+                         span,
+                         fn_id,
+                         body.id,
+                         implicit_arg_tys,
+                         arguments,
+                         fn_sig.output,
+                         body);
 
     match cx.tcx().node_id_to_type(fn_id).sty {
         ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
@@ -195,6 +199,13 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
         _ => {}
     }
 
+    pretty::dump_mir(cx.tcx(),
+                     "mir_map",
+                     &0,
+                     fn_id,
+                     &mir,
+                     Some(&scope_auxiliary));
+
     Ok(mir)
 }
 
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
index b8bb1b4ee11..834897f8eae 100644
--- a/src/librustc_mir/pretty.rs
+++ b/src/librustc_mir/pretty.rs
@@ -8,50 +8,214 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use build::{Location, ScopeAuxiliaryVec};
 use rustc::mir::repr::*;
-use rustc::middle::ty;
+use rustc::middle::ty::{self, TyCtxt};
+use rustc_data_structures::fnv::FnvHashMap;
+use std::fmt::Display;
+use std::fs;
 use std::io::{self, Write};
 use syntax::ast::NodeId;
+use syntax::codemap::Span;
 
 const INDENT: &'static str = "    ";
 
+/// If the session is properly configured, dumps a human-readable
+/// representation of the mir into:
+///
+/// ```
+/// rustc.node<node_id>.<pass_name>.<disambiguator>
+/// ```
+///
+/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
+/// where `<filter>` takes the following forms:
+///
+/// - `all` -- dump MIR for all fns, all passes, all everything
+/// - `substring1&substring2,...` -- `&`-separated list of substrings
+///   that can appear in the pass-name or the `item_path_str` for the given
+///   node-id. If any one of the substrings match, the data is dumped out.
+pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
+                          pass_name: &str,
+                          disambiguator: &Display,
+                          node_id: NodeId,
+                          mir: &Mir<'tcx>,
+                          auxiliary: Option<&ScopeAuxiliaryVec>) {
+    let filters = match tcx.sess.opts.debugging_opts.dump_mir {
+        None => return,
+        Some(ref filters) => filters,
+    };
+    let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
+    let is_matched =
+        filters.split("&")
+               .any(|filter| {
+                   filter == "all" ||
+                       pass_name.contains(filter) ||
+                       node_path.contains(filter)
+               });
+    if !is_matched {
+        return;
+    }
+
+    let file_name = format!("rustc.node{}.{}.{}.mir",
+                            node_id, pass_name, disambiguator);
+    let _ = fs::File::create(&file_name).and_then(|mut file| {
+        try!(writeln!(file, "// MIR for `{}`", node_path));
+        try!(writeln!(file, "// node_id = {}", node_id));
+        try!(writeln!(file, "// pass_name = {}", pass_name));
+        try!(writeln!(file, "// disambiguator = {}", disambiguator));
+        try!(writeln!(file, ""));
+        try!(write_mir_fn(tcx, node_id, mir, &mut file, auxiliary));
+        Ok(())
+    });
+}
+
 /// Write out a human-readable textual representation for the given MIR.
-pub fn write_mir_pretty<'a, 't, W, I>(tcx: &ty::TyCtxt<'t>, iter: I, w: &mut W) -> io::Result<()>
-where W: Write, I: Iterator<Item=(&'a NodeId, &'a Mir<'a>)> {
-    for (&nodeid, mir) in iter {
-        write_mir_intro(tcx, nodeid, mir, w)?;
-        // Nodes
-        for block in mir.all_basic_blocks() {
-            write_basic_block(block, mir, w)?;
+pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>,
+                                     iter: I,
+                                     w: &mut Write)
+                                     -> io::Result<()>
+    where I: Iterator<Item=(&'a NodeId, &'a Mir<'tcx>)>, 'tcx: 'a
+{
+    for (&node_id, mir) in iter {
+        write_mir_fn(tcx, node_id, mir, w, None)?;
+    }
+    Ok(())
+}
+
+enum Annotation {
+    EnterScope(ScopeId),
+    ExitScope(ScopeId),
+}
+
+pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>,
+                          node_id: NodeId,
+                          mir: &Mir<'tcx>,
+                          w: &mut Write,
+                          auxiliary: Option<&ScopeAuxiliaryVec>)
+                          -> io::Result<()> {
+    // compute scope/entry exit annotations
+    let mut annotations = FnvHashMap();
+    if let Some(auxiliary) = auxiliary {
+        for (index, auxiliary) in auxiliary.vec.iter().enumerate() {
+            let scope_id = ScopeId::new(index);
+
+            annotations.entry(auxiliary.dom)
+                       .or_insert(vec![])
+                       .push(Annotation::EnterScope(scope_id));
+
+            for &loc in &auxiliary.postdoms {
+                annotations.entry(loc)
+                           .or_insert(vec![])
+                           .push(Annotation::ExitScope(scope_id));
+            }
         }
-        writeln!(w, "}}")?
     }
+
+    write_mir_intro(tcx, node_id, mir, w)?;
+    for block in mir.all_basic_blocks() {
+        write_basic_block(tcx, block, mir, w, &annotations)?;
+    }
+
+    // construct a scope tree and write it out
+    let mut scope_tree: FnvHashMap<Option<ScopeId>, Vec<ScopeId>> = FnvHashMap();
+    for (index, scope_data) in mir.scopes.iter().enumerate() {
+        scope_tree.entry(scope_data.parent_scope)
+                  .or_insert(vec![])
+                  .push(ScopeId::new(index));
+    }
+    write_scope_tree(tcx, mir, auxiliary, &scope_tree, w, None, 1)?;
+
+    writeln!(w, "}}")?;
     Ok(())
 }
 
 /// Write out a human-readable textual representation for the given basic block.
-fn write_basic_block<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+fn write_basic_block(tcx: &TyCtxt,
+                     block: BasicBlock,
+                     mir: &Mir,
+                     w: &mut Write,
+                     annotations: &FnvHashMap<Location, Vec<Annotation>>)
+                     -> io::Result<()> {
     let data = mir.basic_block_data(block);
 
     // Basic block label at the top.
     writeln!(w, "\n{}{:?}: {{", INDENT, block)?;
 
     // List of statements in the middle.
+    let mut current_location = Location { block: block, statement_index: 0 };
     for statement in &data.statements {
-        writeln!(w, "{0}{0}{1:?};", INDENT, statement)?;
+        if let Some(ref annotations) = annotations.get(&current_location) {
+            for annotation in annotations.iter() {
+                match *annotation {
+                    Annotation::EnterScope(id) =>
+                        writeln!(w, "{0}{0}// Enter Scope({1})",
+                                 INDENT, id.index())?,
+                    Annotation::ExitScope(id) =>
+                        writeln!(w, "{0}{0}// Exit Scope({1})",
+                                 INDENT, id.index())?,
+                }
+            }
+        }
+
+        writeln!(w, "{0}{0}{1:?}; // {2}",
+                 INDENT,
+                 statement,
+                 comment(tcx, statement.scope, statement.span))?;
+
+        current_location.statement_index += 1;
     }
 
     // Terminator at the bottom.
-    writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator())?;
+    writeln!(w, "{0}{0}{1:?}; // {2}",
+             INDENT,
+             data.terminator().kind,
+             comment(tcx, data.terminator().scope, data.terminator().span))?;
 
     writeln!(w, "{}}}", INDENT)
 }
 
+fn comment(tcx: &TyCtxt,
+           scope: ScopeId,
+           span: Span)
+           -> String {
+    format!("Scope({}) at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
+}
+
+fn write_scope_tree(tcx: &TyCtxt,
+                    mir: &Mir,
+                    auxiliary: Option<&ScopeAuxiliaryVec>,
+                    scope_tree: &FnvHashMap<Option<ScopeId>, Vec<ScopeId>>,
+                    w: &mut Write,
+                    parent: Option<ScopeId>,
+                    depth: usize)
+                    -> io::Result<()> {
+    for &child in scope_tree.get(&parent).unwrap_or(&vec![]) {
+        let indent = depth * INDENT.len();
+        let data = &mir.scopes[child];
+        assert_eq!(data.parent_scope, parent);
+        writeln!(w, "{0:1$}Scope({2}) {{", "", indent, child.index())?;
+
+        let indent = indent + INDENT.len();
+        if let Some(parent) = parent {
+            writeln!(w, "{0:1$}Parent: Scope({2})", "", indent, parent.index())?;
+        }
+
+        if let Some(auxiliary) = auxiliary {
+            let extent = auxiliary[child].extent;
+            let data = tcx.region_maps.code_extent_data(extent);
+            writeln!(w, "{0:1$}Extent: {2:?}", "", indent, data)?;
+        }
+
+        write_scope_tree(tcx, mir, auxiliary, scope_tree, w,
+                         Some(child), depth + 1)?;
+    }
+    Ok(())
+}
+
 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
 /// local variables (both user-defined bindings and compiler temporaries).
-fn write_mir_intro<W: Write>(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut W)
--> io::Result<()> {
-
+fn write_mir_intro(tcx: &TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write)
+                   -> io::Result<()> {
     write!(w, "fn {}(", tcx.map.path_to_string(nid))?;
 
     // fn argument types.
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index 9ac3749589e..d8aa0d9b725 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -58,17 +58,17 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
     }
 
     fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) {
-        match *terminator {
-            Terminator::Goto { .. } |
-            Terminator::Resume |
-            Terminator::Return |
-            Terminator::If { .. } |
-            Terminator::Switch { .. } |
-            Terminator::Drop { .. } |
-            Terminator::Call { .. } => {
+        match terminator.kind {
+            TerminatorKind::Goto { .. } |
+            TerminatorKind::Resume |
+            TerminatorKind::Return |
+            TerminatorKind::If { .. } |
+            TerminatorKind::Switch { .. } |
+            TerminatorKind::Drop { .. } |
+            TerminatorKind::Call { .. } => {
                 /* nothing to do */
             },
-            Terminator::SwitchInt { ref mut switch_ty, .. } => {
+            TerminatorKind::SwitchInt { ref mut switch_ty, .. } => {
                 *switch_ty = self.tcx.erase_regions(switch_ty);
             },
         }
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index 9caee36e44a..0e9a1810787 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -21,19 +21,19 @@ pub struct NoLandingPads;
 
 impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
     fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) {
-        match *terminator {
-            Terminator::Goto { .. } |
-            Terminator::Resume |
-            Terminator::Return |
-            Terminator::If { .. } |
-            Terminator::Switch { .. } |
-            Terminator::SwitchInt { .. } => {
+        match terminator.kind {
+            TerminatorKind::Goto { .. } |
+            TerminatorKind::Resume |
+            TerminatorKind::Return |
+            TerminatorKind::If { .. } |
+            TerminatorKind::Switch { .. } |
+            TerminatorKind::SwitchInt { .. } => {
                 /* nothing to do */
             },
-            Terminator::Drop { ref mut unwind, .. } => {
+            TerminatorKind::Drop { ref mut unwind, .. } => {
                 unwind.take();
             },
-            Terminator::Call { ref mut cleanup, .. } => {
+            TerminatorKind::Call { ref mut cleanup, .. } => {
                 cleanup.take();
             },
         }
diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs
index 84410bdc57c..21b1d022fda 100644
--- a/src/librustc_mir/transform/simplify_cfg.rs
+++ b/src/librustc_mir/transform/simplify_cfg.rs
@@ -12,6 +12,7 @@ use rustc::middle::const_eval::ConstVal;
 use rustc::middle::ty::TyCtxt;
 use rustc::mir::repr::*;
 use rustc::mir::transform::{MirPass, Pass};
+use pretty;
 use syntax::ast::NodeId;
 
 use super::remove_dead_blocks::RemoveDeadBlocks;
@@ -30,15 +31,22 @@ impl SimplifyCfg {
             let mut seen: Vec<BasicBlock> = Vec::with_capacity(8);
 
             while mir.basic_block_data(target).statements.is_empty() {
-                match mir.basic_block_data(target).terminator {
-                    Some(Terminator::Goto { target: next }) => {
-                        if seen.contains(&next) {
-                            return None;
+                // NB -- terminator may have been swapped with `None`
+                // below, in which case we have a cycle and just want
+                // to stop
+                if let Some(ref terminator) = mir.basic_block_data(target).terminator {
+                    match terminator.kind {
+                        TerminatorKind::Goto { target: next } => {
+                            if seen.contains(&next) {
+                                return None;
+                            }
+                            seen.push(next);
+                            target = next;
                         }
-                        seen.push(next);
-                        target = next;
+                        _ => break
                     }
-                    _ => break
+                } else {
+                    break
                 }
             }
 
@@ -51,6 +59,8 @@ impl SimplifyCfg {
             let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
                                     .expect("invalid terminator state");
 
+            debug!("remove_goto_chains: bb={:?} terminator={:?}", bb, terminator);
+
             for target in terminator.successors_mut() {
                 let new_target = match final_target(mir, *target) {
                     Some(new_target) => new_target,
@@ -71,27 +81,27 @@ impl SimplifyCfg {
         for bb in mir.all_basic_blocks() {
             let basic_block = mir.basic_block_data_mut(bb);
             let mut terminator = basic_block.terminator_mut();
-            *terminator = match *terminator {
-                Terminator::If { ref targets, .. } if targets.0 == targets.1 => {
+            terminator.kind = match terminator.kind {
+                TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => {
                     changed = true;
-                    Terminator::Goto { target: targets.0 }
+                    TerminatorKind::Goto { target: targets.0 }
                 }
 
-                Terminator::If { ref targets, cond: Operand::Constant(Constant {
+                TerminatorKind::If { ref targets, cond: Operand::Constant(Constant {
                     literal: Literal::Value {
                         value: ConstVal::Bool(cond)
                     }, ..
                 }) } => {
                     changed = true;
                     if cond {
-                        Terminator::Goto { target: targets.0 }
+                        TerminatorKind::Goto { target: targets.0 }
                     } else {
-                        Terminator::Goto { target: targets.1 }
+                        TerminatorKind::Goto { target: targets.1 }
                     }
                 }
 
-                Terminator::SwitchInt { ref targets, .. }  if targets.len() == 1 => {
-                    Terminator::Goto { target: targets[0] }
+                TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => {
+                    TerminatorKind::Goto { target: targets[0] }
                 }
                 _ => continue
             }
@@ -103,8 +113,11 @@ impl SimplifyCfg {
 
 impl<'tcx> MirPass<'tcx> for SimplifyCfg {
     fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) {
+        let mut counter = 0;
         let mut changed = true;
         while changed {
+            pretty::dump_mir(tcx, "simplify_cfg", &counter, id, mir, None);
+            counter += 1;
             changed = self.simplify_branches(mir);
             changed |= self.remove_goto_chains(mir);
             RemoveDeadBlocks.run_pass(tcx, id, mir);
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index d99e6ff4bf5..c0605ebe6d4 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -376,15 +376,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         term: &Terminator<'tcx>) {
         debug!("check_terminator: {:?}", term);
         let tcx = self.tcx();
-        match *term {
-            Terminator::Goto { .. } |
-            Terminator::Resume |
-            Terminator::Return |
-            Terminator::Drop { .. } => {
+        match term.kind {
+            TerminatorKind::Goto { .. } |
+            TerminatorKind::Resume |
+            TerminatorKind::Return |
+            TerminatorKind::Drop { .. } => {
                 // no checks needed for these
             }
 
-            Terminator::If { ref cond, .. } => {
+            TerminatorKind::If { ref cond, .. } => {
                 let cond_ty = mir.operand_ty(tcx, cond);
                 match cond_ty.sty {
                     ty::TyBool => {}
@@ -393,7 +393,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                 }
             }
-            Terminator::SwitchInt { ref discr, switch_ty, .. } => {
+            TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
                 let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
                 if let Err(terr) = self.mk_subty(self.last_span, discr_ty, switch_ty) {
                     span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
@@ -406,7 +406,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
                 // FIXME: check the values
             }
-            Terminator::Switch { ref discr, adt_def, ref targets } => {
+            TerminatorKind::Switch { ref discr, adt_def, ref targets } => {
                 let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
                 match discr_ty.sty {
                     ty::TyEnum(def, _)
@@ -418,7 +418,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                 }
             }
-            Terminator::Call { ref func, ref args, ref destination, .. } => {
+            TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
                 let func_ty = mir.operand_ty(tcx, func);
                 debug!("check_terminator: call, func_ty={:?}", func_ty);
                 let func_ty = match func_ty.sty {
diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs
index 080547952a5..7abaeb44c1c 100644
--- a/src/librustc_trans/trans/mir/block.rs
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -56,8 +56,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
         debug!("trans_block: terminator: {:?}", data.terminator());
 
-        match *data.terminator() {
-            mir::Terminator::Resume => {
+        match data.terminator().kind {
+            mir::TerminatorKind::Resume => {
                 if let Some(cleanup_pad) = cleanup_pad {
                     bcx.cleanup_ret(cleanup_pad, None);
                 } else {
@@ -70,18 +70,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             }
 
-            mir::Terminator::Goto { target } => {
+            mir::TerminatorKind::Goto { target } => {
                 funclet_br(bcx, self.llblock(target));
             }
 
-            mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => {
+            mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
                 let cond = self.trans_operand(&bcx, cond);
                 let lltrue = self.llblock(true_bb);
                 let llfalse = self.llblock(false_bb);
                 bcx.cond_br(cond.immediate(), lltrue, llfalse);
             }
 
-            mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => {
+            mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => {
                 let discr_lvalue = self.trans_lvalue(&bcx, discr);
                 let ty = discr_lvalue.ty.to_ty(bcx.tcx());
                 let repr = adt::represent_type(bcx.ccx(), ty);
@@ -103,7 +103,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             }
 
-            mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
+            mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
                 let (otherwise, targets) = targets.split_last().unwrap();
                 let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval);
                 let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty));
@@ -115,13 +115,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             }
 
-            mir::Terminator::Return => {
+            mir::TerminatorKind::Return => {
                 bcx.with_block(|bcx| {
                     self.fcx.build_return_block(bcx, DebugLoc::None);
                 })
             }
 
-            mir::Terminator::Drop { ref value, target, unwind } => {
+            mir::TerminatorKind::Drop { ref value, target, unwind } => {
                 let lvalue = self.trans_lvalue(&bcx, value);
                 let ty = lvalue.ty.to_ty(bcx.tcx());
                 // Double check for necessity to drop
@@ -152,7 +152,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             }
 
-            mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => {
+            mir::TerminatorKind::Call { ref func, ref args, ref destination, ref cleanup } => {
                 // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
                 let callee = self.trans_operand(&bcx, func);