about summary refs log tree commit diff
diff options
context:
space:
mode:
-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);