about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-06-08 13:51:57 -0700
committerbors <bors@rust-lang.org>2016-06-08 13:51:57 -0700
commit0d531bfb881e6d303d09de9f212eaac72a9a218d (patch)
tree1568111b5de55c3d3b744370dc55d2c4c2afd917
parent4b240fe96a8b8fcaa412624c398547dbc447a81e (diff)
parent0c5930ef256131f8d0e4f020a5029a89944cf250 (diff)
downloadrust-0d531bfb881e6d303d09de9f212eaac72a9a218d.tar.gz
rust-0d531bfb881e6d303d09de9f212eaac72a9a218d.zip
Auto merge of #33989 - eddyb:mir-viz, r=nikomatsakis
[MIR] Make scopes debuginfo-specific (visibility scopes).

Fixes #32949 by having MIR (visibility) scopes mimic the lexical structure.
Unlike #33235, this PR also removes all scopes without variable bindings.

Printing of scopes also changed, e.g. for:
```rust
fn foo(x: i32, y: i32) { let a = 0; let b = 0; let c = 0; }
```
Before my changes:
```rust
fn foo(arg0: i32, arg1: i32) -> () {
    let var0: i32;                       // "x" in scope 1 at <anon>:1:8: 1:9
    let var1: i32;                       // "y" in scope 1 at <anon>:1:16: 1:17
    let var2: i32;                       // "a" in scope 3 at <anon>:1:30: 1:31
    let var3: i32;                       // "b" in scope 6 at <anon>:1:41: 1:42
    let var4: i32;                       // "c" in scope 9 at <anon>:1:52: 1:53

    ...

    scope tree:
    0 1 2 3 {
        4 5
        6 {
            7 8
            9 10 11
        }
    }
}
```
After my changes:
```rust
fn foo(arg0: i32, arg1: i32) -> () {
    scope 1 {
        let var0: i32;                   // "x" in scope 1 at <anon>:1:8: 1:9
        let var1: i32;                   // "y" in scope 1 at <anon>:1:16: 1:17
        scope 2 {
            let var2: i32;               // "a" in scope 2 at <anon>:1:30: 1:31
            scope 3 {
                let var3: i32;           // "b" in scope 3 at <anon>:1:41: 1:42
                scope 4 {
                    let var4: i32;       // "c" in scope 4 at <anon>:1:52: 1:53
                }
            }
        }
    }

    ...
}
-rw-r--r--src/librustc/mir/repr.rs56
-rw-r--r--src/librustc/mir/visit.rs63
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs4
-rw-r--r--src/librustc_borrowck/borrowck/mir/elaborate_drops.rs35
-rw-r--r--src/librustc_borrowck/borrowck/mir/patch.rs22
-rw-r--r--src/librustc_mir/build/block.rs43
-rw-r--r--src/librustc_mir/build/cfg.rs23
-rw-r--r--src/librustc_mir/build/expr/as_lvalue.rs8
-rw-r--r--src/librustc_mir/build/expr/as_operand.rs2
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs22
-rw-r--r--src/librustc_mir/build/expr/as_temp.rs6
-rw-r--r--src/librustc_mir/build/expr/into.rs53
-rw-r--r--src/librustc_mir/build/expr/stmt.rs13
-rw-r--r--src/librustc_mir/build/matches/mod.rs118
-rw-r--r--src/librustc_mir/build/matches/test.rs34
-rw-r--r--src/librustc_mir/build/matches/util.rs4
-rw-r--r--src/librustc_mir/build/misc.rs7
-rw-r--r--src/librustc_mir/build/mod.rs60
-rw-r--r--src/librustc_mir/build/scope.rs102
-rw-r--r--src/librustc_mir/pretty.rs141
-rw-r--r--src/librustc_mir/transform/add_call_guards.rs5
-rw-r--r--src/librustc_mir/transform/promote_consts.rs44
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs17
-rw-r--r--src/librustc_mir/transform/type_check.rs6
-rw-r--r--src/librustc_trans/debuginfo/create_scope_map.rs16
-rw-r--r--src/librustc_trans/mir/block.rs14
-rw-r--r--src/librustc_trans/mir/constant.rs7
-rw-r--r--src/librustc_trans/mir/mod.rs27
-rw-r--r--src/librustc_trans/mir/statement.rs6
-rw-r--r--src/test/debuginfo/function-prologue-stepping-no-stack-check.rs13
30 files changed, 478 insertions, 493 deletions
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 9666741d032..e6c22d0ea34 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -32,9 +32,9 @@ 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>,
+    /// List of visibility (lexical) scopes; these are referenced by statements
+    /// and used (eventually) for debuginfo. Indexed by a `VisibilityScope`.
+    pub visibility_scopes: Vec<VisibilityScopeData>,
 
     /// Rvalues promoted from this function, such as borrows of constants.
     /// Each of them is the Mir of a constant with the fn's type parameters
@@ -100,6 +100,18 @@ impl<'tcx> IndexMut<BasicBlock> for Mir<'tcx> {
     }
 }
 
+/// Grouped information about the source code origin of a MIR entity.
+/// Intended to be inspected by diagnostics and debuginfo.
+/// Most passes can work with it as a whole, within a single function.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+pub struct SourceInfo {
+    /// Source span for the AST pertaining to this MIR entity.
+    pub span: Span,
+
+    /// The lexical visibility scope, i.e. which bindings can be seen.
+    pub scope: VisibilityScope
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Mutability and borrow kinds
 
@@ -172,11 +184,8 @@ pub struct VarDecl<'tcx> {
     /// 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,
+    /// source information (span, scope, etc.) for the declaration
+    pub source_info: SourceInfo,
 }
 
 /// A "temp" is a temporary that we place on the stack. They are
@@ -275,8 +284,7 @@ pub struct BasicBlockData<'tcx> {
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct Terminator<'tcx> {
-    pub span: Span,
-    pub scope: ScopeId,
+    pub source_info: SourceInfo,
     pub kind: TerminatorKind<'tcx>
 }
 
@@ -587,8 +595,7 @@ pub enum AssertMessage<'tcx> {
 
 #[derive(Clone, RustcEncodable, RustcDecodable)]
 pub struct Statement<'tcx> {
-    pub span: Span,
-    pub scope: ScopeId,
+    pub source_info: SourceInfo,
     pub kind: StatementKind<'tcx>,
 }
 
@@ -754,29 +761,32 @@ impl<'tcx> Debug for Lvalue<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Scopes
 
-impl Index<ScopeId> for Vec<ScopeData> {
-    type Output = ScopeData;
+impl Index<VisibilityScope> for Vec<VisibilityScopeData> {
+    type Output = VisibilityScopeData;
 
     #[inline]
-    fn index(&self, index: ScopeId) -> &ScopeData {
+    fn index(&self, index: VisibilityScope) -> &VisibilityScopeData {
         &self[index.index()]
     }
 }
 
-impl IndexMut<ScopeId> for Vec<ScopeData> {
+impl IndexMut<VisibilityScope> for Vec<VisibilityScopeData> {
     #[inline]
-    fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData {
+    fn index_mut(&mut self, index: VisibilityScope) -> &mut VisibilityScopeData {
         &mut self[index.index()]
     }
 }
 
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
-pub struct ScopeId(u32);
+pub struct VisibilityScope(u32);
+
+/// The visibility scope all arguments go into.
+pub const ARGUMENT_VISIBILITY_SCOPE: VisibilityScope = VisibilityScope(0);
 
-impl ScopeId {
-    pub fn new(index: usize) -> ScopeId {
+impl VisibilityScope {
+    pub fn new(index: usize) -> VisibilityScope {
         assert!(index < (u32::MAX as usize));
-        ScopeId(index as u32)
+        VisibilityScope(index as u32)
     }
 
     pub fn index(self) -> usize {
@@ -785,9 +795,9 @@ impl ScopeId {
 }
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
-pub struct ScopeData {
+pub struct VisibilityScopeData {
     pub span: Span,
-    pub parent_scope: Option<ScopeId>,
+    pub parent_scope: Option<VisibilityScope>,
 }
 
 ///////////////////////////////////////////////////////////////////////////
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 5c9582b945b..dbe48d6b094 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -97,9 +97,9 @@ 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_visibility_scope_data(&mut self,
+                                           scope_data: & $($mutability)* VisibilityScopeData) {
+                self.super_visibility_scope_data(scope_data);
             }
 
             fn visit_statement(&mut self,
@@ -186,6 +186,11 @@ macro_rules! make_mir_visitor {
                 self.super_span(span);
             }
 
+            fn visit_source_info(&mut self,
+                                 source_info: & $($mutability)* SourceInfo) {
+                self.super_source_info(source_info);
+            }
+
             fn visit_fn_output(&mut self,
                                fn_output: & $($mutability)* FnOutput<'tcx>) {
                 self.super_fn_output(fn_output);
@@ -236,9 +241,9 @@ macro_rules! make_mir_visitor {
                 self.super_arg_decl(arg_decl);
             }
 
-            fn visit_scope_id(&mut self,
-                              scope_id: & $($mutability)* ScopeId) {
-                self.super_scope_id(scope_id);
+            fn visit_visibility_scope(&mut self,
+                                      scope: & $($mutability)* VisibilityScope) {
+                self.super_visibility_scope(scope);
             }
 
             // The `super_xxx` methods comprise the default behavior and are
@@ -248,7 +253,7 @@ macro_rules! make_mir_visitor {
                          mir: & $($mutability)* Mir<'tcx>) {
                 let Mir {
                     ref $($mutability)* basic_blocks,
-                    ref $($mutability)* scopes,
+                    ref $($mutability)* visibility_scopes,
                     promoted: _, // Visited by passes separately.
                     ref $($mutability)* return_ty,
                     ref $($mutability)* var_decls,
@@ -263,8 +268,8 @@ macro_rules! make_mir_visitor {
                     self.visit_basic_block_data(block, data);
                 }
 
-                for scope in scopes {
-                    self.visit_scope_data(scope);
+                for scope in visibility_scopes {
+                    self.visit_visibility_scope_data(scope);
                 }
 
                 self.visit_fn_output(return_ty);
@@ -302,16 +307,16 @@ macro_rules! make_mir_visitor {
                 }
             }
 
-            fn super_scope_data(&mut self,
-                                scope_data: & $($mutability)* ScopeData) {
-                let ScopeData {
+            fn super_visibility_scope_data(&mut self,
+                                           scope_data: & $($mutability)* VisibilityScopeData) {
+                let VisibilityScopeData {
                     ref $($mutability)* span,
                     ref $($mutability)* parent_scope,
                 } = *scope_data;
 
                 self.visit_span(span);
                 if let Some(ref $($mutability)* parent_scope) = *parent_scope {
-                    self.visit_scope_id(parent_scope);
+                    self.visit_visibility_scope(parent_scope);
                 }
             }
 
@@ -319,13 +324,11 @@ macro_rules! make_mir_visitor {
                                block: BasicBlock,
                                statement: & $($mutability)* Statement<'tcx>) {
                 let Statement {
-                    ref $($mutability)* span,
-                    ref $($mutability)* scope,
+                    ref $($mutability)* source_info,
                     ref $($mutability)* kind,
                 } = *statement;
 
-                self.visit_span(span);
-                self.visit_scope_id(scope);
+                self.visit_source_info(source_info);
                 match *kind {
                     StatementKind::Assign(ref $($mutability)* lvalue,
                                           ref $($mutability)* rvalue) => {
@@ -346,13 +349,11 @@ macro_rules! make_mir_visitor {
                                 block: BasicBlock,
                                 terminator: &$($mutability)* Terminator<'tcx>) {
                 let Terminator {
-                    ref $($mutability)* span,
-                    ref $($mutability)* scope,
+                    ref $($mutability)* source_info,
                     ref $($mutability)* kind,
                 } = *terminator;
 
-                self.visit_span(span);
-                self.visit_scope_id(scope);
+                self.visit_source_info(source_info);
                 self.visit_terminator_kind(block, kind);
             }
 
@@ -622,13 +623,11 @@ macro_rules! make_mir_visitor {
                     mutability: _,
                     name: _,
                     ref $($mutability)* ty,
-                    ref $($mutability)* scope,
-                    ref $($mutability)* span,
+                    ref $($mutability)* source_info,
                 } = *var_decl;
 
                 self.visit_ty(ty);
-                self.visit_scope_id(scope);
-                self.visit_span(span);
+                self.visit_source_info(source_info);
             }
 
             fn super_temp_decl(&mut self,
@@ -651,8 +650,8 @@ macro_rules! make_mir_visitor {
                 self.visit_ty(ty);
             }
 
-            fn super_scope_id(&mut self,
-                              _scope_id: & $($mutability)* ScopeId) {
+            fn super_visibility_scope(&mut self,
+                                      _scope: & $($mutability)* VisibilityScope) {
             }
 
             fn super_branch(&mut self,
@@ -707,6 +706,16 @@ macro_rules! make_mir_visitor {
             fn super_span(&mut self, _span: & $($mutability)* Span) {
             }
 
+            fn super_source_info(&mut self, source_info: & $($mutability)* SourceInfo) {
+                let SourceInfo {
+                    ref $($mutability)* span,
+                    ref $($mutability)* scope,
+                } = *source_info;
+
+                self.visit_span(span);
+                self.visit_visibility_scope(scope);
+            }
+
             fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) {
                 match *fn_output {
                     FnOutput::FnConverging(ref $($mutability)* ty) => {
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
index 74dc921b0bb..8c528f10b57 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
@@ -151,7 +151,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                            terminator: &'a Option<repr::Terminator<'tcx>>)
                            -> Option<(&'a [repr::Operand<'tcx>], Span)> {
-    if let Some(repr::Terminator { ref kind, span, .. }) = *terminator {
+    if let Some(repr::Terminator { ref kind, source_info, .. }) = *terminator {
         if let repr::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind
         {
             if let repr::Operand::Constant(ref func) = *oper
@@ -161,7 +161,7 @@ fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     let name = tcx.item_name(def_id);
                     if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
                         if name.as_str() == "rustc_peek" {
-                            return Some((args, span));
+                            return Some((args, source_info.span));
                         }
                     }
                 }
diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
index e783420fa06..b09db70e7b8 100644
--- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
+++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
@@ -124,8 +124,7 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
 
 #[derive(Copy, Clone, Debug)]
 struct DropCtxt<'a, 'tcx: 'a> {
-    span: Span,
-    scope: ScopeId,
+    source_info: SourceInfo,
     is_cleanup: bool,
 
     init_data: &'a InitializationData,
@@ -273,8 +272,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                     let init_data = self.initialization_data_at(loc);
                     let path = self.move_data().rev_lookup.find(location);
                     self.elaborate_drop(&DropCtxt {
-                        span: terminator.span,
-                        scope: terminator.scope,
+                        source_info: terminator.source_info,
                         is_cleanup: data.is_cleanup,
                         init_data: &init_data,
                         lvalue: location,
@@ -329,8 +327,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
         let assign = Statement {
             kind: StatementKind::Assign(location.clone(), Rvalue::Use(value.clone())),
-            span: terminator.span,
-            scope: terminator.scope
+            source_info: terminator.source_info
         };
 
         let unwind = unwind.unwrap_or(self.patch.resume_block());
@@ -367,8 +364,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             let path = self.move_data().rev_lookup.find(location);
 
             self.elaborate_drop(&DropCtxt {
-                span: terminator.span,
-                scope: terminator.scope,
+                source_info: terminator.source_info,
                 is_cleanup: data.is_cleanup,
                 init_data: &init_data,
                 lvalue: location,
@@ -513,8 +509,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                     debug!("drop_ladder: for std field {} ({:?})", i, lv);
 
                     self.elaborated_drop_block(&DropCtxt {
-                        span: c.span,
-                        scope: c.scope,
+                        source_info: c.source_info,
                         is_cleanup: is_cleanup,
                         init_data: c.init_data,
                         lvalue: lv,
@@ -527,8 +522,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                     debug!("drop_ladder: for rest field {} ({:?})", i, lv);
 
                     let blk = self.complete_drop(&DropCtxt {
-                        span: c.span,
-                        scope: c.scope,
+                        source_info: c.source_info,
                         is_cleanup: is_cleanup,
                         init_data: c.init_data,
                         lvalue: lv,
@@ -785,7 +779,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         self.patch.new_block(BasicBlockData {
             statements: vec![],
             terminator: Some(Terminator {
-                scope: c.scope, span: c.span, kind: k
+                source_info: c.source_info, kind: k
             }),
             is_cleanup: is_cleanup
         })
@@ -858,11 +852,10 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         let mut statements = vec![];
         if let Some(&flag) = self.drop_flags.get(&c.path) {
             statements.push(Statement {
-                span: c.span,
-                scope: c.scope,
+                source_info: c.source_info,
                 kind: StatementKind::Assign(
                     Lvalue::Temp(flag),
-                    self.constant_bool(c.span, false)
+                    self.constant_bool(c.source_info.span, false)
                 )
             });
         }
@@ -880,9 +873,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         self.patch.new_block(BasicBlockData {
             statements: statements,
             terminator: Some(Terminator {
-                scope: c.scope, span: c.span, kind: TerminatorKind::Call {
+                source_info: c.source_info, kind: TerminatorKind::Call {
                     func: Operand::Constant(Constant {
-                        span: c.span,
+                        span: c.source_info.span,
                         ty: fty,
                         literal: Literal::Item {
                             def_id: free_func,
@@ -910,7 +903,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             ty::TyStruct(def, _) | ty::TyEnum(def, _) => {
                 if def.has_dtor() {
                     self.tcx.sess.span_warn(
-                        c.span,
+                        c.source_info.span,
                         &format!("dataflow bug??? moving out of type with dtor {:?}",
                                  c));
                     true
@@ -932,7 +925,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
     fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) {
         if let Some(&flag) = self.drop_flags.get(&path) {
-            let span = self.patch.context_for_location(self.mir, loc).0;
+            let span = self.patch.source_info_for_location(self.mir, loc).span;
             let val = self.constant_bool(span, val.value());
             self.patch.add_assign(loc, Lvalue::Temp(flag), val);
         }
@@ -940,7 +933,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
     fn drop_flags_on_init(&mut self) {
         let loc = Location { block: START_BLOCK, index: 0 };
-        let span = self.patch.context_for_location(self.mir, loc).0;
+        let span = self.patch.source_info_for_location(self.mir, loc).span;
         let false_ = self.constant_bool(span, false);
         for flag in self.drop_flags.values() {
             self.patch.add_assign(loc, Lvalue::Temp(*flag), false_.clone());
diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_borrowck/borrowck/mir/patch.rs
index b390c19af1a..b838881251d 100644
--- a/src/librustc_borrowck/borrowck/mir/patch.rs
+++ b/src/librustc_borrowck/borrowck/mir/patch.rs
@@ -11,7 +11,6 @@
 use super::gather_moves::Location;
 use rustc::ty::Ty;
 use rustc::mir::repr::*;
-use syntax::codemap::Span;
 
 use std::iter;
 use std::u32;
@@ -62,8 +61,10 @@ impl<'tcx> MirPatch<'tcx> {
             result.new_block(BasicBlockData {
                 statements: vec![],
                 terminator: Some(Terminator {
-                    span: mir.span,
-                    scope: ScopeId::new(0),
+                    source_info: SourceInfo {
+                        span: mir.span,
+                        scope: ARGUMENT_VISIBILITY_SCOPE
+                    },
                     kind: TerminatorKind::Resume
                 }),
                 is_cleanup: true
@@ -154,31 +155,30 @@ impl<'tcx> MirPatch<'tcx> {
             debug!("MirPatch: adding statement {:?} at loc {:?}+{}",
                    stmt, loc, delta);
             loc.index += delta;
-            let (span, scope) = Self::context_for_index(
+            let source_info = Self::source_info_for_index(
                 mir.basic_block_data(loc.block), loc
             );
             mir.basic_block_data_mut(loc.block).statements.insert(
                 loc.index, Statement {
-                    span: span,
-                    scope: scope,
+                    source_info: source_info,
                     kind: stmt
                 });
             delta += 1;
         }
     }
 
-    pub fn context_for_index(data: &BasicBlockData, loc: Location) -> (Span, ScopeId) {
+    pub fn source_info_for_index(data: &BasicBlockData, loc: Location) -> SourceInfo {
         match data.statements.get(loc.index) {
-            Some(stmt) => (stmt.span, stmt.scope),
-            None => (data.terminator().span, data.terminator().scope)
+            Some(stmt) => stmt.source_info,
+            None => data.terminator().source_info
         }
     }
 
-    pub fn context_for_location(&self, mir: &Mir, loc: Location) -> (Span, ScopeId) {
+    pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
         let data = match loc.block.index().checked_sub(mir.basic_blocks.len()) {
             Some(new) => &self.new_blocks[new],
             None => mir.basic_block_data(loc.block)
         };
-        Self::context_for_index(data, loc)
+        Self::source_info_for_index(data, loc)
     }
 }
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index c1626b93f0c..7e650c5bd3d 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -22,7 +22,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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:
             //
@@ -40,27 +40,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             //
             // First we build all the statements in the block.
             let mut let_extent_stack = Vec::with_capacity(8);
+            let outer_visibility_scope = this.visibility_scope;
             for stmt in stmts {
                 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);
                             this.stmt_expr(block, expr)
                         }));
                     }
                     StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
-                        let remainder_scope_id = this.push_scope(remainder_scope, block);
+                        let tcx = this.hir.tcx();
+
+                        // Enter the remainder scope, i.e. the bindings' destruction scope.
+                        this.push_scope(remainder_scope, block);
                         let_extent_stack.push(remainder_scope);
-                        unpack!(block = this.in_scope(init_scope, block, move |this, _| {
-                            // FIXME #30046                              ^~~~
-                            if let Some(init) = initializer {
-                                this.expr_into_pattern(block, remainder_scope_id, pattern, init)
-                            } else {
-                                this.declare_bindings(remainder_scope_id, &pattern);
-                                block.unit()
-                            }
-                        }));
+
+                        // Declare the bindings, which may create a visibility scope.
+                        let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.map);
+                        let remainder_span = remainder_span.unwrap_or(span);
+                        let scope = this.declare_bindings(None, remainder_span, &pattern);
+
+                        // Evaluate the initializer, if present.
+                        if let Some(init) = initializer {
+                            unpack!(block = this.in_scope(init_scope, block, move |this| {
+                                // FIXME #30046                              ^~~~
+                                this.expr_into_pattern(block, pattern, init)
+                            }));
+                        }
+
+                        // Enter the visibility scope, after evaluating the initializer.
+                        if let Some(visibility_scope) = scope {
+                            this.visibility_scope = visibility_scope;
+                        }
                     }
                 }
             }
@@ -70,14 +83,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 unpack!(block = this.into(destination, block, expr));
             } else if dest_is_unit {
                 // FIXME(#31472)
-                let scope_id = this.innermost_scope_id();
-                this.cfg.push_assign_unit(block, scope_id, span, destination);
+                let source_info = this.source_info(span);
+                this.cfg.push_assign_unit(block, source_info, destination);
             }
             // Finally, we pop all the let scopes before exiting out from the scope of block
             // itself.
             for extent in let_extent_stack.into_iter().rev() {
                 unpack!(block = this.pop_scope(extent, block));
             }
+            // Restore the original visibility scope.
+            this.visibility_scope = outer_visibility_scope;
             block.unit()
         })
     }
diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs
index 4859257f291..95f87bf8326 100644
--- a/src/librustc_mir/build/cfg.rs
+++ b/src/librustc_mir/build/cfg.rs
@@ -15,7 +15,6 @@
 
 use build::{CFG, Location};
 use rustc::mir::repr::*;
-use syntax::codemap::Span;
 
 impl<'tcx> CFG<'tcx> {
     pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
@@ -50,47 +49,41 @@ impl<'tcx> CFG<'tcx> {
 
     pub fn push_assign(&mut self,
                        block: BasicBlock,
-                       scope: ScopeId,
-                       span: Span,
+                       source_info: SourceInfo,
                        lvalue: &Lvalue<'tcx>,
                        rvalue: Rvalue<'tcx>) {
         self.push(block, Statement {
-            scope: scope,
-            span: span,
+            source_info: source_info,
             kind: StatementKind::Assign(lvalue.clone(), rvalue)
         });
     }
 
     pub fn push_assign_constant(&mut self,
                                 block: BasicBlock,
-                                scope: ScopeId,
-                                span: Span,
+                                source_info: SourceInfo,
                                 temp: &Lvalue<'tcx>,
                                 constant: Constant<'tcx>) {
-        self.push_assign(block, scope, span, temp,
+        self.push_assign(block, source_info, temp,
                          Rvalue::Use(Operand::Constant(constant)));
     }
 
     pub fn push_assign_unit(&mut self,
                             block: BasicBlock,
-                            scope: ScopeId,
-                            span: Span,
+                            source_info: SourceInfo,
                             lvalue: &Lvalue<'tcx>) {
-        self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate(
+        self.push_assign(block, source_info, lvalue, Rvalue::Aggregate(
             AggregateKind::Tuple, vec![]
         ));
     }
 
     pub fn terminate(&mut self,
                      block: BasicBlock,
-                     scope: ScopeId,
-                     span: Span,
+                     source_info: SourceInfo,
                      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 {
-            span: span,
-            scope: scope,
+            source_info: source_info,
             kind: kind,
         });
     }
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index bb5aca2d8d7..8e33cfa9b0b 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -34,11 +34,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr);
 
         let this = self;
-        let scope_id = this.innermost_scope_id();
         let expr_span = expr.span;
+        let source_info = this.source_info(expr_span);
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this, _| this.as_lvalue(block, value))
+                this.in_scope(extent, block, |this| this.as_lvalue(block, value))
             }
             ExprKind::Field { lhs, name } => {
                 let lvalue = unpack!(block = this.as_lvalue(block, lhs));
@@ -59,9 +59,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 // bounds check:
                 let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
-                this.cfg.push_assign(block, scope_id, expr_span, // len = len(slice)
+                this.cfg.push_assign(block, source_info, // len = len(slice)
                                      &len, Rvalue::Len(slice.clone()));
-                this.cfg.push_assign(block, scope_id, expr_span, // lt = idx < len
+                this.cfg.push_assign(block, source_info, // lt = idx < len
                                      &lt, Rvalue::BinaryOp(BinOp::Lt,
                                                            idx.clone(),
                                                            Operand::Consume(len.clone())));
diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs
index a059f2bdde9..beb9ca256ab 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, 'gcx, 'tcx> Builder<'a, 'gcx, '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 67cf5473f79..6524124c13b 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -41,12 +41,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr);
 
         let this = self;
-        let scope_id = this.innermost_scope_id();
         let expr_span = expr.span;
+        let source_info = this.source_info(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| {
@@ -86,7 +86,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     let minval = this.minval_literal(expr_span, expr.ty);
                     let is_min = this.temp(bool_ty);
 
-                    this.cfg.push_assign(block, scope_id, expr_span, &is_min,
+                    this.cfg.push_assign(block, source_info, &is_min,
                                          Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval));
 
                     let err = ConstMathErr::Overflow(Op::Neg);
@@ -99,8 +99,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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, scope_id, expr_span, &result, Rvalue::Box(value.ty));
-                this.in_scope(value_extents, block, |this, _| {
+                this.cfg.push_assign(block, source_info, &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:
@@ -245,13 +245,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn build_binary_op(&mut self, mut block: BasicBlock,
                            op: BinOp, span: Span, ty: ty::Ty<'tcx>,
                            lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd<Rvalue<'tcx>> {
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
         let bool_ty = self.hir.bool_ty();
         if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() {
             let result_tup = self.hir.tcx().mk_tup(vec![ty, bool_ty]);
             let result_value = self.temp(result_tup);
 
-            self.cfg.push_assign(block, scope_id, span,
+            self.cfg.push_assign(block, source_info,
                                  &result_value, Rvalue::CheckedBinaryOp(op,
                                                                         lhs,
                                                                         rhs));
@@ -292,7 +292,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // Check for / 0
                 let is_zero = self.temp(bool_ty);
                 let zero = self.zero_literal(span, ty);
-                self.cfg.push_assign(block, scope_id, span, &is_zero,
+                self.cfg.push_assign(block, source_info, &is_zero,
                                      Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero));
 
                 block = self.assert(block, Operand::Consume(is_zero), false,
@@ -310,14 +310,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                     // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
 
-                    self.cfg.push_assign(block, scope_id, span, &is_neg_1,
+                    self.cfg.push_assign(block, source_info, &is_neg_1,
                                          Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), neg_1));
-                    self.cfg.push_assign(block, scope_id, span, &is_min,
+                    self.cfg.push_assign(block, source_info, &is_min,
                                          Rvalue::BinaryOp(BinOp::Eq, lhs.clone(), min));
 
                     let is_neg_1 = Operand::Consume(is_neg_1);
                     let is_min = Operand::Consume(is_min);
-                    self.cfg.push_assign(block, scope_id, span, &of,
+                    self.cfg.push_assign(block, source_info, &of,
                                          Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min));
 
                     block = self.assert(block, Operand::Consume(of), false,
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index f33d3dd5190..da128b8dd56 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, 'gcx, 'tcx> Builder<'a, 'gcx, '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();
@@ -49,8 +49,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             Category::Lvalue => {
                 let lvalue = unpack!(block = this.as_lvalue(block, expr));
                 let rvalue = Rvalue::Use(Operand::Consume(lvalue));
-                let scope_id = this.innermost_scope_id();
-                this.cfg.push_assign(block, scope_id, expr_span, &temp, rvalue);
+                let source_info = this.source_info(expr_span);
+                this.cfg.push_assign(block, source_info, &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 41610c90377..fd9ddc05ab5 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -33,11 +33,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // just use the name `this` uniformly
         let this = self;
         let expr_span = expr.span;
-        let scope_id = this.innermost_scope_id();
+        let source_info = this.source_info(expr_span);
 
         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, expr.ty.is_nil(), block, ast_block)
@@ -50,7 +50,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 let mut then_block = this.cfg.start_new_block();
                 let mut else_block = this.cfg.start_new_block();
-                this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If {
+                this.cfg.terminate(block, source_info, TerminatorKind::If {
                     cond: operand,
                     targets: (then_block, else_block)
                 });
@@ -61,19 +61,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 } else {
                     // Body of the `if` expression without an `else` clause must return `()`, thus
                     // we implicitly generate a `else {}` if it is not specified.
-                    let scope_id = this.innermost_scope_id();
-                    this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination);
+                    this.cfg.push_assign_unit(else_block, source_info, destination);
                     else_block
                 };
 
                 let join_block = this.cfg.start_new_block();
-                this.cfg.terminate(then_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(then_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
-                this.cfg.terminate(else_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(else_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
 
                 join_block.unit()
@@ -100,19 +95,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     LogicalOp::And => (else_block, false_block),
                     LogicalOp::Or => (true_block, else_block),
                 };
-                this.cfg.terminate(block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(block, source_info,
                                    TerminatorKind::If { cond: lhs, targets: blocks });
 
                 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
-                this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If {
+                this.cfg.terminate(else_block, source_info, TerminatorKind::If {
                     cond: rhs,
                     targets: (true_block, false_block)
                 });
 
                 this.cfg.push_assign_constant(
-                    true_block, scope_id, expr_span, destination,
+                    true_block, source_info, destination,
                     Constant {
                         span: expr_span,
                         ty: this.hir.bool_ty(),
@@ -120,20 +113,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     });
 
                 this.cfg.push_assign_constant(
-                    false_block, scope_id, expr_span, destination,
+                    false_block, source_info, destination,
                     Constant {
                         span: expr_span,
                         ty: this.hir.bool_ty(),
                         literal: this.hir.false_literal(),
                     });
 
-                this.cfg.terminate(true_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(true_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
-                this.cfg.terminate(false_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(false_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
 
                 join_block.unit()
@@ -158,9 +147,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let exit_block = this.cfg.start_new_block();
 
                 // start the loop
-                this.cfg.terminate(block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(block, source_info,
                                    TerminatorKind::Goto { target: loop_block });
 
                 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
@@ -173,9 +160,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         let loop_block_end;
                         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,
-                                           scope_id,
-                                           expr_span,
+                        this.cfg.terminate(loop_block_end, source_info,
                                            TerminatorKind::If {
                                                cond: cond,
                                                targets: (body_block, exit_block)
@@ -192,15 +177,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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,
-                                       scope_id,
-                                       expr_span,
+                    this.cfg.terminate(body_block_end, source_info,
                                        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, scope_id, expr_span, destination);
+                    this.cfg.push_assign_unit(exit_block, source_info, destination);
                 }
                 exit_block.unit()
             }
@@ -219,7 +202,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 let success = this.cfg.start_new_block();
                 let cleanup = this.diverge_cleanup();
-                this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call {
+                this.cfg.terminate(block, source_info, TerminatorKind::Call {
                     func: fun,
                     args: args,
                     cleanup: cleanup,
@@ -269,7 +252,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 });
 
                 let rvalue = unpack!(block = this.as_rvalue(block, expr));
-                this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue);
+                this.cfg.push_assign(block, source_info, destination, rvalue);
                 block.unit()
             }
         }
diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs
index 24369aaff3f..ad55a3d8b73 100644
--- a/src/librustc_mir/build/expr/stmt.rs
+++ b/src/librustc_mir/build/expr/stmt.rs
@@ -20,18 +20,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> {
         let this = self;
         let expr_span = expr.span;
-        let scope_id = this.innermost_scope_id();
+        let source_info = this.source_info(expr.span);
         // Handle a number of expressions that don't need a destination at all. This
         // avoids needing a mountain of temporary `()` variables.
         match expr.kind {
             ExprKind::Scope { extent, value } => {
                 let value = this.hir.mirror(value);
-                this.in_scope(extent, block, |this, _| this.stmt_expr(block, value))
+                this.in_scope(extent, block, |this| this.stmt_expr(block, value))
             }
             ExprKind::Assign { lhs, rhs } => {
                 let lhs = this.hir.mirror(lhs);
                 let rhs = this.hir.mirror(rhs);
-                let scope_id = this.innermost_scope_id();
                 let lhs_span = lhs.span;
 
                 // Note: we evaluate assignments right-to-left. This
@@ -50,7 +49,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 } else {
                     let rhs = unpack!(block = this.as_rvalue(block, rhs));
                     let lhs = unpack!(block = this.as_lvalue(block, lhs));
-                    this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs);
+                    this.cfg.push_assign(block, source_info, &lhs, rhs);
                     block.unit()
                 }
             }
@@ -75,7 +74,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // (overloaded ops should be desugared into a call).
                 let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty,
                                                   Operand::Consume(lhs.clone()), rhs));
-                this.cfg.push_assign(block, scope_id, expr_span, &lhs, result);
+                this.cfg.push_assign(block, source_info, &lhs, result);
 
                 block.unit()
             }
@@ -93,8 +92,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block = match value {
                     Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
                     None => {
-                        this.cfg.push_assign_unit(block, scope_id,
-                                                  expr_span, &Lvalue::ReturnPointer);
+                        this.cfg.push_assign_unit(block, source_info, &Lvalue::ReturnPointer);
                         block
                     }
                 };
@@ -104,7 +102,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 this.cfg.start_new_block().unit()
             }
             _ => {
-                let expr_span = expr.span;
                 let expr_ty = expr.ty;
                 let temp = this.temp(expr.ty.clone());
                 unpack!(block = this.into(&temp, block, expr));
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 88d7e41bc61..6104679d650 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -44,21 +44,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         .collect(),
         };
 
-        // Get the body expressions and their scopes, while declaring bindings.
-        let arm_bodies: Vec<_> = arms.iter().enumerate().map(|(i, arm)| {
-            // Assume that all expressions are wrapped in Scope.
+        // Get the arm bodies and their scopes, while declaring bindings.
+        let arm_bodies: Vec<_> = arms.iter().map(|arm| {
             let body = self.hir.mirror(arm.body.clone());
-            match body.kind {
-                ExprKind::Scope { extent, value } => {
-                    let scope_id = self.push_scope(extent, arm_blocks.blocks[i]);
-                    self.declare_bindings(scope_id, &arm.patterns[0]);
-                    (extent, self.scopes.pop().unwrap(), value)
-                }
-                _ => {
-                    span_bug!(body.span, "arm body is not wrapped in Scope {:?}",
-                              body.kind);
-                }
-            }
+            let scope = self.declare_bindings(None, body.span, &arm.patterns[0]);
+            (body, scope.unwrap_or(self.visibility_scope))
         }).collect();
 
         // assemble a list of candidates: there is one candidate per
@@ -99,63 +89,46 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // all the arm blocks will rejoin here
         let end_block = self.cfg.start_new_block();
 
-        let scope_id = self.innermost_scope_id();
-        for (arm_index, (extent, scope, body)) in arm_bodies.into_iter().enumerate() {
+        let outer_source_info = self.source_info(span);
+        for (arm_index, (body, visibility_scope)) in arm_bodies.into_iter().enumerate() {
             let mut arm_block = arm_blocks.blocks[arm_index];
-            // Re-enter the scope we created the bindings in.
-            self.scopes.push(scope);
+            // Re-enter the visibility scope we created the bindings in.
+            self.visibility_scope = visibility_scope;
             unpack!(arm_block = self.into(destination, arm_block, body));
-            unpack!(arm_block = self.pop_scope(extent, arm_block));
-            self.cfg.terminate(arm_block,
-                               scope_id,
-                               span,
+            self.cfg.terminate(arm_block, outer_source_info,
                                TerminatorKind::Goto { target: end_block });
         }
+        self.visibility_scope = outer_source_info.scope;
 
         end_block.unit()
     }
 
     pub fn expr_into_pattern(&mut self,
                              mut block: BasicBlock,
-                             var_scope_id: ScopeId, // lifetime of vars
                              irrefutable_pat: Pattern<'tcx>,
                              initializer: ExprRef<'tcx>)
                              -> BlockAnd<()> {
         // optimize the case of `let x = ...`
         match *irrefutable_pat.kind {
-            PatternKind::Binding { mutability,
-                                   name,
-                                   mode: BindingMode::ByValue,
+            PatternKind::Binding { mode: BindingMode::ByValue,
                                    var,
-                                   ty,
-                                   subpattern: None } => {
-                let index = self.declare_binding(var_scope_id,
-                                                 mutability,
-                                                 name,
-                                                 var,
-                                                 ty,
-                                                 irrefutable_pat.span);
-                let lvalue = Lvalue::Var(index);
+                                   subpattern: None, .. } => {
+                let lvalue = Lvalue::Var(self.var_indices[&var]);
                 return self.into(&lvalue, block, initializer);
             }
             _ => {}
         }
         let lvalue = unpack!(block = self.as_lvalue(block, initializer));
         self.lvalue_into_pattern(block,
-                                 var_scope_id,
                                  irrefutable_pat,
                                  &lvalue)
     }
 
     pub fn lvalue_into_pattern(&mut self,
                                mut block: BasicBlock,
-                               var_scope_id: ScopeId,
                                irrefutable_pat: Pattern<'tcx>,
                                initializer: &Lvalue<'tcx>)
                                -> BlockAnd<()> {
-        // first, creating the bindings
-        self.declare_bindings(var_scope_id, &irrefutable_pat);
-
         // create a dummy candidate
         let mut candidate = Candidate {
             span: irrefutable_pat.span,
@@ -182,32 +155,47 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         block.unit()
     }
 
-    pub fn declare_bindings(&mut self, var_scope_id: ScopeId, pattern: &Pattern<'tcx>) {
+    /// Declares the bindings of the given pattern and returns the visibility scope
+    /// for the bindings in this patterns, if such a scope had to be created.
+    /// NOTE: Declaring the bindings should always be done in their drop scope.
+    pub fn declare_bindings(&mut self,
+                            mut var_scope: Option<VisibilityScope>,
+                            scope_span: Span,
+                            pattern: &Pattern<'tcx>)
+                            -> Option<VisibilityScope> {
         match *pattern.kind {
             PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => {
-                self.declare_binding(var_scope_id, mutability, name, var, ty, pattern.span);
+                if var_scope.is_none() {
+                    var_scope = Some(self.new_visibility_scope(scope_span));
+                }
+                let source_info = SourceInfo {
+                    span: pattern.span,
+                    scope: var_scope.unwrap()
+                };
+                self.declare_binding(source_info, mutability, name, var, ty);
                 if let Some(subpattern) = subpattern.as_ref() {
-                    self.declare_bindings(var_scope_id, subpattern);
+                    var_scope = self.declare_bindings(var_scope, scope_span, 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_scope_id, subpattern);
+                    var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
                 }
             }
             PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
             }
             PatternKind::Deref { ref subpattern } => {
-                self.declare_bindings(var_scope_id, subpattern);
+                var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
             }
             PatternKind::Leaf { ref subpatterns } |
             PatternKind::Variant { ref subpatterns, .. } => {
                 for subpattern in subpatterns {
-                    self.declare_bindings(var_scope_id, &subpattern.pattern);
+                    var_scope = self.declare_bindings(var_scope, scope_span, &subpattern.pattern);
                 }
             }
         }
+        var_scope
     }
 }
 
@@ -396,17 +384,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                              mut otherwise: Vec<BasicBlock>)
                              -> BasicBlock
     {
+        let source_info = self.source_info(span);
         otherwise.sort();
         otherwise.dedup(); // variant switches can introduce duplicate target blocks
-        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,
-                                   scope_id,
-                                   span,
+                self.cfg.terminate(block, source_info,
                                    TerminatorKind::Goto { target: join_block });
             }
             join_block
@@ -585,24 +571,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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 source_info = self.source_info(guard.span);
             let cond = unpack!(block = self.as_operand(block, guard));
             let otherwise = self.cfg.start_new_block();
-            self.cfg.terminate(block,
-                               scope_id,
-                               guard_span,
+            self.cfg.terminate(block, source_info,
                                TerminatorKind::If { cond: cond,
                                                     targets: (arm_block, otherwise)});
             Some(otherwise)
         } else {
-            self.cfg.terminate(block,
-                               scope_id,
-                               candidate.span,
+            let source_info = self.source_info(candidate.span);
+            self.cfg.terminate(block, source_info,
                                TerminatorKind::Goto { target: arm_block });
             None
         }
@@ -628,35 +610,33 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     Rvalue::Ref(region, borrow_kind, binding.source),
             };
 
-            let scope_id = self.innermost_scope_id();
-            self.cfg.push_assign(block, scope_id, binding.span,
+            let source_info = self.source_info(binding.span);
+            self.cfg.push_assign(block, source_info,
                                  &Lvalue::Var(var_index), rvalue);
         }
     }
 
     fn declare_binding(&mut self,
-                       var_scope_id: ScopeId,
+                       source_info: SourceInfo,
                        mutability: Mutability,
                        name: Name,
                        var_id: NodeId,
-                       var_ty: Ty<'tcx>,
-                       span: Span)
+                       var_ty: Ty<'tcx>)
                        -> u32
     {
-        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_scope_id={:?}, span={:?})",
-               var_id, name, var_ty, var_scope_id, span);
+        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?})",
+               var_id, name, var_ty, source_info);
 
         let index = self.var_decls.len();
         self.var_decls.push(VarDecl::<'tcx> {
-            scope: var_scope_id,
+            source_info: source_info,
             mutability: mutability,
             name: name,
             ty: var_ty.clone(),
-            span: span,
         });
         let index = index as u32;
-        let extent = self.scope_auxiliary[var_scope_id].extent;
-        self.schedule_drop(span, extent, &Lvalue::Var(index), var_ty);
+        let extent = self.extent_of_innermost_scope();
+        self.schedule_drop(source_info.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 09c146537e7..1f0c4bd9e0d 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -176,7 +176,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         lvalue: &Lvalue<'tcx>,
                         test: &Test<'tcx>)
                         -> Vec<BasicBlock> {
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(test.span);
         match test.kind {
             TestKind::Switch { adt_def, ref variants } => {
                 let num_enum_variants = self.hir.num_variants(adt_def);
@@ -193,7 +193,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 }).collect();
                 debug!("num_enum_variants: {}, num tested variants: {}, variants: {:?}",
                        num_enum_variants, variants.iter().count(), variants);
-                self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch {
+                self.cfg.terminate(block, source_info, TerminatorKind::Switch {
                     discr: lvalue.clone(),
                     adt_def: adt_def,
                     targets: target_blocks.clone()
@@ -245,10 +245,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     }
                 };
 
-                self.cfg.terminate(block,
-                                   scope_id,
-                                   test.span,
-                                   term);
+                self.cfg.terminate(block, source_info, term);
                 targets
             }
 
@@ -265,7 +262,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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, scope_id, test.span, &val_slice,
+                            self.cfg.push_assign(block, source_info, &val_slice,
                                                  Rvalue::Cast(CastKind::Unsize, val, ty));
                             val = Operand::Consume(val_slice);
                         }
@@ -280,7 +277,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     });
 
                     let slice = self.temp(ty);
-                    self.cfg.push_assign(block, scope_id, test.span, &slice,
+                    self.cfg.push_assign(block, source_info, &slice,
                                          Rvalue::Cast(CastKind::Unsize, array, ty));
                     Operand::Consume(slice)
                 } else {
@@ -301,7 +298,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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, scope_id, test.span, TerminatorKind::Call {
+                    self.cfg.terminate(block, source_info, TerminatorKind::Call {
                         func: Operand::Constant(Constant {
                             span: test.span,
                             ty: mty,
@@ -314,7 +311,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                     // check the result
                     let block = self.cfg.start_new_block();
-                    self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If {
+                    self.cfg.terminate(eq_block, source_info, TerminatorKind::If {
                         cond: Operand::Consume(eq_result),
                         targets: (block, fail),
                     });
@@ -344,17 +341,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
 
                 // actual = len(lvalue)
-                self.cfg.push_assign(block, scope_id, test.span,
+                self.cfg.push_assign(block, source_info,
                                      &actual, Rvalue::Len(lvalue.clone()));
 
                 // expected = <N>
-                let expected = self.push_usize(block, scope_id, test.span, len);
+                let expected = self.push_usize(block, source_info, len);
 
                 // result = actual == expected OR result = actual < expected
-                self.cfg.push_assign(block,
-                                     scope_id,
-                                     test.span,
-                                     &result,
+                self.cfg.push_assign(block, source_info, &result,
                                      Rvalue::BinaryOp(op,
                                                       Operand::Consume(actual),
                                                       Operand::Consume(expected)));
@@ -362,7 +356,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // branch based on result
                 let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
                                                  self.cfg.start_new_block()];
-                self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If {
+                self.cfg.terminate(block, source_info, TerminatorKind::If {
                     cond: Operand::Consume(result),
                     targets: (target_blocks[0], target_blocks[1])
                 });
@@ -383,13 +377,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let result = self.temp(bool_ty);
 
         // result = op(left, right)
-        let scope_id = self.innermost_scope_id();
-        self.cfg.push_assign(block, scope_id, span, &result,
+        let source_info = self.source_info(span);
+        self.cfg.push_assign(block, source_info, &result,
                              Rvalue::BinaryOp(op, left, right));
 
         // branch based on result
         let target_block = self.cfg.start_new_block();
-        self.cfg.terminate(block, scope_id, span, TerminatorKind::If {
+        self.cfg.terminate(block, source_info, TerminatorKind::If {
             cond: Operand::Consume(result),
             targets: (target_block, fail_block)
         });
diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs
index 5eb58f7612d..2e9cc96c046 100644
--- a/src/librustc_mir/build/matches/util.rs
+++ b/src/librustc_mir/build/matches/util.rs
@@ -65,8 +65,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 from_end: suffix_len,
             };
             let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy
-            let scope_id = self.innermost_scope_id();
-            self.cfg.push_assign(block, scope_id, slice.span, &temp, rvalue);
+            let source_info = self.source_info(slice.span);
+            self.cfg.push_assign(block, source_info, &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 c3501140fc0..a9d5c0c4f85 100644
--- a/src/librustc_mir/build/misc.rs
+++ b/src/librustc_mir/build/misc.rs
@@ -103,16 +103,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
     pub fn push_usize(&mut self,
                       block: BasicBlock,
-                      scope_id: ScopeId,
-                      span: Span,
+                      source_info: SourceInfo,
                       value: u64)
                       -> Lvalue<'tcx> {
         let usize_ty = self.hir.usize_ty();
         let temp = self.temp(usize_ty);
         self.cfg.push_assign_constant(
-            block, scope_id, span, &temp,
+            block, source_info, &temp,
             Constant {
-                span: span,
+                span: source_info.span,
                 ty: self.hir.usize_ty(),
                 literal: self.hir.usize_literal(value),
             });
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 9d7818a9ba4..9c1b9fa4437 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -15,6 +15,7 @@ use rustc::mir::repr::*;
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc::hir;
 use std::ops::{Index, IndexMut};
+use std::u32;
 use syntax::abi::Abi;
 use syntax::ast;
 use syntax::codemap::Span;
@@ -43,7 +44,8 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     /// the vector of all scopes that we have created thus far;
     /// we track this for debuginfo later
-    scope_datas: Vec<ScopeData>,
+    visibility_scopes: Vec<VisibilityScopeData>,
+    visibility_scope: VisibilityScope,
 
     var_decls: Vec<VarDecl<'tcx>>,
     var_indices: FnvHashMap<ast::NodeId, u32>,
@@ -61,6 +63,20 @@ struct CFG<'tcx> {
     basic_blocks: Vec<BasicBlockData<'tcx>>,
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+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
+    }
+}
+
 /// 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.
@@ -179,17 +195,16 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
         tcx.region_maps.lookup_code_extent(
             CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
     let mut block = START_BLOCK;
-    let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block,
-                                                         |builder, call_site_scope_id| {
-        let arg_decls = unpack!(block = builder.in_scope(arg_extent, block,
-                                                         |builder, arg_scope_id| {
-            builder.args_and_body(block, return_ty, arguments, arg_scope_id, ast_block)
+    let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
+        let arg_decls = unpack!(block = builder.in_scope(arg_extent, block, |builder| {
+            builder.args_and_body(block, return_ty, arguments, arg_extent, ast_block)
         }));
 
+        let source_info = builder.source_info(span);
         let return_block = builder.return_block();
-        builder.cfg.terminate(block, call_site_scope_id, span,
+        builder.cfg.terminate(block, source_info,
                               TerminatorKind::Goto { target: return_block });
-        builder.cfg.terminate(return_block, call_site_scope_id, span,
+        builder.cfg.terminate(return_block, source_info,
                               TerminatorKind::Return);
         return_block.and(arg_decls)
     }));
@@ -241,14 +256,15 @@ pub fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
 
     let extent = ROOT_CODE_EXTENT;
     let mut block = START_BLOCK;
-    let _ = builder.in_scope(extent, block, |builder, call_site_scope_id| {
+    let _ = builder.in_scope(extent, block, |builder| {
         let expr = builder.hir.mirror(ast_expr);
         unpack!(block = builder.into(&Lvalue::ReturnPointer, block, expr));
 
+        let source_info = builder.source_info(span);
         let return_block = builder.return_block();
-        builder.cfg.terminate(block, call_site_scope_id, span,
+        builder.cfg.terminate(block, source_info,
                               TerminatorKind::Goto { target: return_block });
-        builder.cfg.terminate(return_block, call_site_scope_id, span,
+        builder.cfg.terminate(return_block, source_info,
                               TerminatorKind::Return);
 
         return_block.unit()
@@ -265,7 +281,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             cfg: CFG { basic_blocks: vec![] },
             fn_span: span,
             scopes: vec![],
-            scope_datas: vec![],
+            visibility_scopes: vec![],
+            visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
             scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
             loop_scopes: vec![],
             temp_decls: vec![],
@@ -277,6 +294,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         };
 
         assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
+        assert_eq!(builder.new_visibility_scope(span), ARGUMENT_VISIBILITY_SCOPE);
+        builder.visibility_scopes[ARGUMENT_VISIBILITY_SCOPE].parent_scope = None;
 
         builder
     }
@@ -294,7 +313,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         (Mir {
             basic_blocks: self.cfg.basic_blocks,
-            scopes: self.scope_datas,
+            visibility_scopes: self.visibility_scopes,
             promoted: vec![],
             var_decls: self.var_decls,
             arg_decls: arg_decls,
@@ -309,24 +328,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         mut block: BasicBlock,
                         return_ty: ty::FnOutput<'tcx>,
                         arguments: A,
-                        argument_scope_id: ScopeId,
+                        argument_extent: CodeExtent,
                         ast_block: &'gcx hir::Block)
                         -> BlockAnd<Vec<ArgDecl<'tcx>>>
         where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
     {
         // to start, translate the argument patterns and collect the argument types.
+        let mut scope = None;
         let arg_decls = arguments.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));
+                scope = self.declare_bindings(scope, ast_block.span, &pattern);
+                unpack!(block = self.lvalue_into_pattern(block, 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);
 
@@ -344,6 +361,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         }).collect();
 
+        // Enter the argument pattern bindings visibility scope, if it exists.
+        if let Some(visibility_scope) = scope {
+            self.visibility_scope = visibility_scope;
+        }
+
         // FIXME(#32959): temporary hack for the issue at hand
         let return_is_unit = if let ty::FnConverging(t) = return_ty {
             t.is_nil()
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 209649dd2fd..2b7efa52cbd 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -86,7 +86,7 @@ should go to.
 
 */
 
-use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary};
+use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary, ScopeId};
 use rustc::middle::region::{CodeExtent, CodeExtentData};
 use rustc::middle::lang_items;
 use rustc::ty::subst::{Substs, Subst, VecPerParamSpace};
@@ -98,9 +98,12 @@ use rustc::middle::const_val::ConstVal;
 use rustc_const_math::ConstInt;
 
 pub struct Scope<'tcx> {
-    /// the scope-id within the scope_datas
+    /// the scope-id within the scope_auxiliary
     id: ScopeId,
 
+    /// The visibility scope this scope was created in.
+    visibility_scope: VisibilityScope,
+
     /// the extent of this scope within source code; also stored in
     /// `ScopeAuxiliary`, but kept here for convenience
     extent: CodeExtent,
@@ -204,6 +207,14 @@ impl<'tcx> Scope<'tcx> {
             None
         }
     }
+
+    /// Given a span and this scope's visibility scope, make a SourceInfo.
+    fn source_info(&self, span: Span) -> SourceInfo {
+        SourceInfo {
+            span: span,
+            scope: self.visibility_scope
+        }
+    }
 }
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@@ -237,11 +248,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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, 'gcx, 'tcx>, ScopeId) -> BlockAnd<R>
+        where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
     {
         debug!("in_scope(extent={:?}, block={:?})", extent, block);
-        let id = self.push_scope(extent, block);
-        let rv = unpack!(block = f(self, id));
+        self.push_scope(extent, block);
+        let rv = unpack!(block = f(self));
         unpack!(block = self.pop_scope(extent, block));
         debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
         block.and(rv)
@@ -251,17 +262,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, '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, entry: BasicBlock) -> ScopeId {
+    pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) {
         debug!("push_scope({:?})", extent);
-        let parent_id = self.scopes.last().map(|s| s.id);
-        let id = ScopeId::new(self.scope_datas.len());
-        let tcx = self.hir.tcx();
-        self.scope_datas.push(ScopeData {
-            span: extent.span(&tcx.region_maps, &tcx.map).unwrap_or(DUMMY_SP),
-            parent_scope: parent_id,
-        });
+        let id = ScopeId::new(self.scope_auxiliary.vec.len());
+        let vis_scope = self.visibility_scope;
         self.scopes.push(Scope {
             id: id,
+            visibility_scope: vis_scope,
             extent: extent,
             drops: vec![],
             free: None,
@@ -272,7 +279,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             dom: self.cfg.current_location(entry),
             postdoms: vec![]
         });
-        id
     }
 
     /// Pops a scope, which should have extent `extent`, adding any
@@ -320,7 +326,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             if let Some(ref free_data) = scope.free {
                 let next = self.cfg.start_new_block();
                 let free = build_free(self.hir.tcx(), &tmp, free_data, next);
-                self.cfg.terminate(block, scope.id, span, free);
+                self.cfg.terminate(block, scope.source_info(span), free);
                 block = next;
             }
             self.scope_auxiliary[scope.id]
@@ -334,11 +340,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                             .next()
                                             .unwrap();
         self.cfg.terminate(block,
-                           scope.id,
-                           span,
+                           scope.source_info(span),
                            TerminatorKind::Goto { target: target });
     }
 
+    /// Creates a new visibility scope, nested in the current one.
+    pub fn new_visibility_scope(&mut self, span: Span) -> VisibilityScope {
+        let parent = self.visibility_scope;
+        let scope = VisibilityScope::new(self.visibility_scopes.len());
+        self.visibility_scopes.push(VisibilityScopeData {
+            span: span,
+            parent_scope: Some(parent),
+        });
+        scope
+    }
+
     // Finding scopes
     // ==============
     /// Finds the loop scope for a given label. This is used for
@@ -363,8 +379,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }.unwrap_or_else(|| span_bug!(span, "no enclosing loop scope found?"))
     }
 
-    pub fn innermost_scope_id(&self) -> ScopeId {
-        self.scopes.last().map(|scope| scope.id).unwrap()
+    /// Given a span and the current visibility scope, make a SourceInfo.
+    pub fn source_info(&self, span: Span) -> SourceInfo {
+        SourceInfo {
+            span: span,
+            scope: self.visibility_scope
+        }
     }
 
     pub fn extent_of_innermost_scope(&self) -> CodeExtent {
@@ -481,7 +501,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             target
         } else {
             let resumeblk = cfg.start_new_cleanup_block();
-            cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume);
+            cfg.terminate(resumeblk,
+                          scopes[0].source_info(self.fn_span),
+                          TerminatorKind::Resume);
             *cached_resume_block = Some(resumeblk);
             resumeblk
         };
@@ -502,12 +524,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         if !self.hir.needs_drop(ty) {
             return block.unit();
         }
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
         let next_target = self.cfg.start_new_block();
         let diverge_target = self.diverge_cleanup();
-        self.cfg.terminate(block,
-                           scope_id,
-                           span,
+        self.cfg.terminate(block, source_info,
                            TerminatorKind::Drop {
                                location: location,
                                target: next_target,
@@ -522,12 +542,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                   span: Span,
                                   location: Lvalue<'tcx>,
                                   value: Operand<'tcx>) -> BlockAnd<()> {
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
         let next_target = self.cfg.start_new_block();
         let diverge_target = self.diverge_cleanup();
-        self.cfg.terminate(block,
-                           scope_id,
-                           span,
+        self.cfg.terminate(block, source_info,
                            TerminatorKind::DropAndReplace {
                                location: location,
                                value: value,
@@ -562,18 +580,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let elems = vec![Operand::Constant(message),
                          Operand::Constant(file),
                          Operand::Constant(line)];
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
         // 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, scope_id, span, &tuple, // [1]
+        self.cfg.push_assign(block, source_info, &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, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
+        self.cfg.push_assign(block, source_info, &tuple_ref, // tuple_ref = &tuple;
                              Rvalue::Ref(region, BorrowKind::Shared, tuple));
         let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
+        self.cfg.terminate(block, source_info, TerminatorKind::Call {
             func: Operand::Constant(func),
             args: vec![Operand::Consume(tuple_ref)],
             cleanup: cleanup,
@@ -590,12 +608,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                   msg: AssertMessage<'tcx>,
                   span: Span)
                   -> BasicBlock {
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
 
         let success_block = self.cfg.start_new_block();
         let cleanup = self.diverge_cleanup();
 
-        self.cfg.terminate(block, scope_id, span,
+        self.cfg.terminate(block, source_info,
                            TerminatorKind::Assert {
                                cond: cond,
                                expected: expected,
@@ -658,7 +676,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, scope.id, drop_data.span, TerminatorKind::Drop {
+        cfg.terminate(block, scope.source_info(drop_data.span), TerminatorKind::Drop {
             location: drop_data.location.clone(),
             target: next,
             unwind: on_diverge
@@ -688,15 +706,19 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     // remainder. If everything is cached, we'll just walk right to
     // left reading the cached results but never created anything.
 
+    let visibility_scope = scope.visibility_scope;
+    let source_info = |span| SourceInfo {
+        span: span,
+        scope: visibility_scope
+    };
+
     // 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,
-                          scope.id,
-                          free_data.span,
+            cfg.terminate(into, source_info(free_data.span),
                           build_free(tcx, unit_temp, free_data, target));
             free_data.cached_block = Some(into);
             into
@@ -711,9 +733,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
             cached_block
         } else {
             let block = cfg.start_new_cleanup_block();
-            cfg.terminate(block,
-                          scope.id,
-                          drop_data.span,
+            cfg.terminate(block, source_info(drop_data.span),
                           TerminatorKind::Drop {
                               location: drop_data.location.clone(),
                               target: target,
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
index a6bbd55ffa7..8c21928c251 100644
--- a/src/librustc_mir/pretty.rs
+++ b/src/librustc_mir/pretty.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use build::{Location, ScopeAuxiliaryVec};
+use build::{Location, ScopeAuxiliaryVec, ScopeId};
 use rustc::hir;
 use rustc::mir::repr::*;
 use rustc::mir::transform::MirSource;
@@ -18,7 +18,6 @@ use std::fmt::Display;
 use std::fs;
 use std::io::{self, Write};
 use syntax::ast::NodeId;
-use syntax::codemap::Span;
 
 const INDENT: &'static str = "    ";
 /// Alignment for lining up comments following MIR statements
@@ -139,20 +138,11 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     write_mir_intro(tcx, src, mir, w)?;
     for block in mir.all_basic_blocks() {
         write_basic_block(tcx, block, mir, w, &annotations)?;
+        if block.index() + 1 != mir.basic_blocks.len() {
+            writeln!(w, "")?;
+        }
     }
 
-    // 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));
-    }
-
-    writeln!(w, "{}scope tree:", INDENT)?;
-    write_scope_tree(tcx, mir, auxiliary, &scope_tree, w, None, 1, false)?;
-    writeln!(w, "")?;
-
     writeln!(w, "}}")?;
     Ok(())
 }
@@ -189,7 +179,7 @@ fn write_basic_block(tcx: TyCtxt,
         writeln!(w, "{0:1$} // {2}",
                  indented_mir,
                  ALIGN,
-                 comment(tcx, statement.scope, statement.span))?;
+                 comment(tcx, statement.source_info))?;
 
         current_location.statement_index += 1;
     }
@@ -199,71 +189,64 @@ fn write_basic_block(tcx: TyCtxt,
     writeln!(w, "{0:1$} // {2}",
              indented_terminator,
              ALIGN,
-             comment(tcx, data.terminator().scope, data.terminator().span))?;
+             comment(tcx, data.terminator().source_info))?;
 
     writeln!(w, "{}}}\n", INDENT)
 }
 
-fn comment(tcx: TyCtxt, scope: ScopeId, span: Span) -> String {
+fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> 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>>,
+                    scope_tree: &FnvHashMap<VisibilityScope, Vec<VisibilityScope>>,
                     w: &mut Write,
-                    parent: Option<ScopeId>,
-                    depth: usize,
-                    same_line: bool)
+                    parent: VisibilityScope,
+                    depth: usize)
                     -> io::Result<()> {
-    let indent = if same_line {
-        0
-    } else {
-        depth * INDENT.len()
-    };
+    let indent = depth * INDENT.len();
 
     let children = match scope_tree.get(&parent) {
         Some(childs) => childs,
         None => return Ok(()),
     };
 
-    for (index, &child) in children.iter().enumerate() {
-        if index == 0 && same_line {
-            // We know we're going to output a scope, so prefix it with a space to separate it from
-            // the previous scopes on this line
-            write!(w, " ")?;
-        }
-
-        let data = &mir.scopes[child];
-        assert_eq!(data.parent_scope, parent);
-        write!(w, "{0:1$}{2}", "", indent, child.index())?;
+    for &child in children {
+        let data = &mir.visibility_scopes[child];
+        assert_eq!(data.parent_scope, Some(parent));
+        writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
 
-        let indent = indent + INDENT.len();
+        // User variable types (including the user's name in a comment).
+        for (i, var) in mir.var_decls.iter().enumerate() {
+            // Skip if not declared in this scope.
+            if var.source_info.scope != child {
+                continue;
+            }
 
-        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)?;
+            let mut_str = if var.mutability == Mutability::Mut {
+                "mut "
+            } else {
+                ""
+            };
+
+            let indent = indent + INDENT.len();
+            let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
+                                       INDENT,
+                                       indent,
+                                       mut_str,
+                                       Lvalue::Var(i as u32),
+                                       var.ty);
+            writeln!(w, "{0:1$} // \"{2}\" in {3}",
+                     indented_var,
+                     ALIGN,
+                     var.name,
+                     comment(tcx, var.source_info))?;
         }
 
-        let child_count = scope_tree.get(&Some(child)).map(Vec::len).unwrap_or(0);
-        if child_count < 2 {
-            // Skip the braces when there's no or only a single subscope
-            write_scope_tree(tcx, mir, auxiliary, scope_tree, w,
-                             Some(child), depth, true)?;
-        } else {
-            // 2 or more child scopes? Put them in braces and on new lines.
-            writeln!(w, " {{")?;
-            write_scope_tree(tcx, mir, auxiliary, scope_tree, w,
-                             Some(child), depth + 1, false)?;
-
-            write!(w, "\n{0:1$}}}", "", depth * INDENT.len())?;
-        }
+        write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
 
-        if !same_line && index + 1 < children.len() {
-            writeln!(w, "")?;
-        }
+        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
     }
 
     Ok(())
@@ -278,7 +261,23 @@ fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              -> io::Result<()> {
     write_mir_sig(tcx, src, mir, w)?;
     writeln!(w, " {{")?;
-    write_mir_decls(tcx, mir, w)
+
+    // construct a scope tree and write it out
+    let mut scope_tree: FnvHashMap<VisibilityScope, Vec<VisibilityScope>> = FnvHashMap();
+    for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
+        if let Some(parent) = scope_data.parent_scope {
+            scope_tree.entry(parent)
+                      .or_insert(vec![])
+                      .push(VisibilityScope::new(index));
+        } else {
+            // Only the argument scope has no parent, because it's the root.
+            assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
+        }
+    }
+
+    write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
+
+    write_mir_decls(mir, w)
 }
 
 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
@@ -318,29 +317,7 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
     }
 }
 
-fn write_mir_decls(tcx: TyCtxt, mir: &Mir, w: &mut Write)
-                   -> io::Result<()>
-{
-    // User variable types (including the user's name in a comment).
-    for (i, var) in mir.var_decls.iter().enumerate() {
-        let mut_str = if var.mutability == Mutability::Mut {
-            "mut "
-        } else {
-            ""
-        };
-
-        let indented_var = format!("{}let {}{:?}: {};",
-                                   INDENT,
-                                   mut_str,
-                                   Lvalue::Var(i as u32),
-                                   var.ty);
-        writeln!(w, "{0:1$} // \"{2}\" in {3}",
-                 indented_var,
-                 ALIGN,
-                 var.name,
-                 comment(tcx, var.scope, var.span))?;
-    }
-
+fn write_mir_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
     // Compiler-introduced temporary types.
     for (i, temp) in mir.temp_decls.iter().enumerate() {
         writeln!(w, "{}let mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)?;
diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs
index bcdd62c1899..a90d8e93a50 100644
--- a/src/librustc_mir/transform/add_call_guards.rs
+++ b/src/librustc_mir/transform/add_call_guards.rs
@@ -66,15 +66,14 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards {
                         destination: Some((_, ref mut destination)),
                         cleanup: Some(_),
                         ..
-                    }, span, scope
+                    }, source_info
                 }) if pred_count[destination.index()] > 1 => {
                     // It's a critical edge, break it
                     let call_guard = BasicBlockData {
                         statements: vec![],
                         is_cleanup: data.is_cleanup,
                         terminator: Some(Terminator {
-                            span: span,
-                            scope: scope,
+                            source_info: source_info,
                             kind: TerminatorKind::Goto { target: *destination }
                         })
                     };
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index d81c4e2dfb6..c9c574966c7 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -117,18 +117,16 @@ impl<'tcx> Visitor<'tcx> for TempCollector {
         }
     }
 
+    fn visit_source_info(&mut self, source_info: &SourceInfo) {
+        self.span = source_info.span;
+    }
+
     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
         assert_eq!(self.location.block, bb);
-        self.span = statement.span;
         self.super_statement(bb, statement);
         self.location.statement_index += 1;
     }
 
-    fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
-        self.span = terminator.span;
-        self.super_terminator(bb, terminator);
-    }
-
     fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
         self.location.statement_index = 0;
         self.location.block = bb;
@@ -167,8 +165,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         self.promoted.basic_blocks.push(BasicBlockData {
             statements: vec![],
             terminator: Some(Terminator {
-                span: self.promoted.span,
-                scope: ScopeId::new(0),
+                source_info: SourceInfo {
+                    span: self.promoted.span,
+                    scope: ARGUMENT_VISIBILITY_SCOPE
+                },
                 kind: TerminatorKind::Return
             }),
             is_cleanup: false
@@ -179,8 +179,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
     fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) {
         let data = self.promoted.basic_blocks.last_mut().unwrap();
         data.statements.push(Statement {
-            span: span,
-            scope: ScopeId::new(0),
+            source_info: SourceInfo {
+                span: span,
+                scope: ARGUMENT_VISIBILITY_SCOPE
+            },
             kind: StatementKind::Assign(dest, rvalue)
         });
     }
@@ -214,7 +216,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         // First, take the Rvalue or Call out of the source MIR,
         // or duplicate it, depending on keep_original.
         let (mut rvalue, mut call) = (None, None);
-        let span = if stmt_idx < no_stmts {
+        let source_info = if stmt_idx < no_stmts {
             let statement = &mut self.source[bb].statements[stmt_idx];
             let StatementKind::Assign(_, ref mut rhs) = statement.kind;
             if self.keep_original {
@@ -223,11 +225,11 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
                 rvalue = Some(mem::replace(rhs, unit));
             }
-            statement.span
+            statement.source_info
         } else if self.keep_original {
             let terminator = self.source[bb].terminator().clone();
             call = Some(terminator.kind);
-            terminator.span
+            terminator.source_info
         } else {
             let terminator = self.source[bb].terminator_mut();
             let target = match terminator.kind {
@@ -242,13 +244,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     dest.take().unwrap().1
                 }
                 ref kind => {
-                    span_bug!(terminator.span, "{:?} not promotable", kind);
+                    span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                 }
             };
             call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
                 target: target
             }));
-            terminator.span
+            terminator.source_info
         };
 
         // Then, recurse for components in the Rvalue or Call.
@@ -266,7 +268,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
 
         // Inject the Rvalue or Call into the promoted MIR.
         if stmt_idx < no_stmts {
-            self.assign(new_temp, rvalue.unwrap(), span);
+            self.assign(new_temp, rvalue.unwrap(), source_info.span);
         } else {
             let last = self.promoted.basic_blocks.len() - 1;
             let new_target = self.new_block();
@@ -278,7 +280,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 _ => bug!()
             }
             let terminator = &mut self.promoted.basic_blocks[last].terminator_mut();
-            terminator.span = span;
+            terminator.source_info.span = source_info.span;
             terminator.kind = call;
         }
 
@@ -346,7 +348,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                         continue;
                     }
                 }
-                (statement.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
+                (statement.source_info.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
             }
             Candidate::ShuffleIndices(bb) => {
                 let terminator = mir[bb].terminator();
@@ -355,11 +357,11 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                         mir.operand_ty(tcx, &args[2])
                     }
                     _ => {
-                        span_bug!(terminator.span,
+                        span_bug!(terminator.source_info.span,
                                   "expected simd_shuffleN call to promote");
                     }
                 };
-                (terminator.span, ty)
+                (terminator.source_info.span, ty)
             }
         };
 
@@ -367,7 +369,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
             source: mir,
             promoted: Mir {
                 basic_blocks: vec![],
-                scopes: vec![ScopeData {
+                visibility_scopes: vec![VisibilityScopeData {
                     span: span,
                     parent_scope: None
                 }],
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index b9eec6ecd9c..92ac9b049f1 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -377,11 +377,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                             let stmt_idx = location.statement_index;
 
                             // Get the span for the initialization.
-                            if stmt_idx < data.statements.len() {
-                                self.span = data.statements[stmt_idx].span;
+                            let source_info = if stmt_idx < data.statements.len() {
+                                data.statements[stmt_idx].source_info
                             } else {
-                                self.span = data.terminator().span;
-                            }
+                                data.terminator().source_info
+                            };
+                            self.span = source_info.span;
 
                             // Treat this as a statement in the AST.
                             self.statement_like();
@@ -830,7 +831,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 // Avoid a generic error for other uses of arguments.
                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
                     let decl = &self.mir.var_decls[index as usize];
-                    span_err!(self.tcx.sess, decl.span, E0022,
+                    span_err!(self.tcx.sess, decl.source_info.span, E0022,
                               "arguments of constant functions can only \
                                be immutable by-value bindings");
                     return;
@@ -841,16 +842,18 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
         self.assign(dest);
     }
 
+    fn visit_source_info(&mut self, source_info: &SourceInfo) {
+        self.span = source_info.span;
+    }
+
     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
         assert_eq!(self.location.block, bb);
-        self.span = statement.span;
         self.nest(|this| this.super_statement(bb, statement));
         self.location.statement_index += 1;
     }
 
     fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
         assert_eq!(self.location.block, bb);
-        self.span = terminator.span;
         self.nest(|this| this.super_terminator(bb, terminator));
     }
 
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 019ed670d1f..4bfa3b16efa 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -551,7 +551,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
     {
         let is_cleanup = block.is_cleanup;
-        self.last_span = block.terminator().span;
+        self.last_span = block.terminator().source_info.span;
         match block.terminator().kind {
             TerminatorKind::Goto { target } =>
                 self.assert_iscleanup(mir, block, target, is_cleanup),
@@ -617,8 +617,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         debug!("run_on_mir: {:?}", mir.span);
         for block in &mir.basic_blocks {
             for stmt in &block.statements {
-                if stmt.span != DUMMY_SP {
-                    self.last_span = stmt.span;
+                if stmt.source_info.span != DUMMY_SP {
+                    self.last_span = stmt.source_info.span;
                 }
                 self.check_stmt(mir, stmt);
             }
diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs
index bba0edd5f04..f75f973f68a 100644
--- a/src/librustc_trans/debuginfo/create_scope_map.rs
+++ b/src/librustc_trans/debuginfo/create_scope_map.rs
@@ -16,7 +16,7 @@ use llvm;
 use llvm::debuginfo::{DIScope, DISubprogram};
 use common::{CrateContext, FunctionContext};
 use rustc::hir::pat_util;
-use rustc::mir::repr::{Mir, ScopeId};
+use rustc::mir::repr::{Mir, VisibilityScope};
 use rustc::util::nodemap::NodeMap;
 
 use libc::c_uint;
@@ -71,7 +71,7 @@ pub fn create_scope_map(cx: &CrateContext,
 /// If debuginfo is disabled, the returned vector is empty.
 pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec<DIScope> {
     let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn");
-    let mut scopes = vec![ptr::null_mut(); mir.scopes.len()];
+    let mut scopes = vec![ptr::null_mut(); mir.visibility_scopes.len()];
 
     let fn_metadata = match fcx.debug_context {
         FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata,
@@ -82,14 +82,14 @@ pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec<DIScope> {
     };
 
     // Find all the scopes with variables defined in them.
-    let mut has_variables = BitVector::new(mir.scopes.len());
+    let mut has_variables = BitVector::new(mir.visibility_scopes.len());
     for var in &mir.var_decls {
-        has_variables.insert(var.scope.index());
+        has_variables.insert(var.source_info.scope.index());
     }
 
     // Instantiate all scopes.
-    for idx in 0..mir.scopes.len() {
-        let scope = ScopeId::new(idx);
+    for idx in 0..mir.visibility_scopes.len() {
+        let scope = VisibilityScope::new(idx);
         make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
     }
 
@@ -100,14 +100,14 @@ fn make_mir_scope(ccx: &CrateContext,
                   mir: &Mir,
                   has_variables: &BitVector,
                   fn_metadata: DISubprogram,
-                  scope: ScopeId,
+                  scope: VisibilityScope,
                   scopes: &mut [DIScope]) {
     let idx = scope.index();
     if !scopes[idx].is_null() {
         return;
     }
 
-    let scope_data = &mir.scopes[scope];
+    let scope_data = &mir.visibility_scopes[scope];
     let parent_scope = if let Some(parent) = scope_data.parent_scope {
         make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
         scopes[parent.index()]
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index b404475b584..6f9838a8932 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -106,8 +106,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let terminator = data.terminator();
         debug!("trans_block: terminator: {:?}", terminator);
 
-        let debug_loc = DebugLoc::ScopeAt(self.scopes[terminator.scope.index()],
-                                          terminator.span);
+        let span = terminator.source_info.span;
+        let debug_loc = self.debug_loc(terminator.source_info);
         debug_loc.apply_to_bcx(&bcx);
         debug_loc.apply(bcx.fcx());
         match terminator.kind {
@@ -245,7 +245,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 bcx = panic_block.build();
 
                 // Get the location information.
-                let loc = bcx.sess().codemap().lookup_char_pos(terminator.span.lo);
+                let loc = bcx.sess().codemap().lookup_char_pos(span.lo);
                 let filename = token::intern_and_get_ident(&loc.file.name);
                 let filename = C_str_slice(bcx.ccx(), filename);
                 let line = C_u32(bcx.ccx(), loc.line as u32);
@@ -296,15 +296,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // is also constant, then we can produce a warning.
                 if const_cond == Some(!expected) {
                     if let Some(err) = const_err {
-                        let _ = consts::const_err(bcx.ccx(),
-                                                  terminator.span,
+                        let _ = consts::const_err(bcx.ccx(), span,
                                                   Err::<(), _>(err),
                                                   consts::TrueConst::No);
                     }
                 }
 
                 // Obtain the panic entry point.
-                let def_id = common::langcall(bcx.tcx(), Some(terminator.span), "", lang_item);
+                let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item);
                 let callee = Callee::def(bcx.ccx(), def_id,
                     bcx.ccx().empty_substs_for_def_id(def_id));
                 let llfn = callee.reify(bcx.ccx()).val;
@@ -417,8 +416,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     if is_shuffle && idx == 2 {
                         match *arg {
                             mir::Operand::Consume(_) => {
-                                span_bug!(terminator.span,
-                                          "shuffle indices must be constant");
+                                span_bug!(span, "shuffle indices must be constant");
                             }
                             mir::Operand::Constant(ref constant) => {
                                 let val = self.trans_constant(&bcx, constant);
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index ed1830560ed..4e0cf729d4c 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -281,12 +281,13 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
         loop {
             let data = self.mir.basic_block_data(bb);
             for statement in &data.statements {
+                let span = statement.source_info.span;
                 match statement.kind {
                     mir::StatementKind::Assign(ref dest, ref rvalue) => {
                         let ty = self.mir.lvalue_ty(tcx, dest);
                         let ty = self.monomorphize(&ty).to_ty(tcx);
-                        match self.const_rvalue(rvalue, ty, statement.span) {
-                            Ok(value) => self.store(dest, value, statement.span),
+                        match self.const_rvalue(rvalue, ty, span) {
+                            Ok(value) => self.store(dest, value, span),
                             Err(err) => if failure.is_ok() { failure = Err(err); }
                         }
                     }
@@ -294,7 +295,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
             }
 
             let terminator = data.terminator();
-            let span = terminator.span;
+            let span = terminator.source_info.span;
             bb = match terminator.kind {
                 mir::TerminatorKind::Drop { target, .. } | // No dropping.
                 mir::TerminatorKind::Goto { target } => target,
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index 0e700d61194..b58f5ed650a 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -112,6 +112,12 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     scopes: Vec<DIScope>
 }
 
+impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
+    pub fn debug_loc(&self, source_info: mir::SourceInfo) -> DebugLoc {
+        DebugLoc::ScopeAt(self.scopes[source_info.scope.index()], source_info.span)
+    }
+}
+
 enum TempRef<'tcx> {
     Lvalue(LvalueRef<'tcx>),
     Operand(Option<OperandRef<'tcx>>),
@@ -166,12 +172,12 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
                             .map(|(mty, decl)| {
         let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str());
 
-        let scope = scopes[decl.scope.index()];
+        let scope = scopes[decl.source_info.scope.index()];
         if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
             bcx.with_block(|bcx| {
                 declare_local(bcx, decl.name, mty, scope,
                               VariableAccess::DirectVariable { alloca: lvalue.llval },
-                              VariableKind::LocalVariable, decl.span);
+                              VariableKind::LocalVariable, decl.source_info.span);
             });
         }
 
@@ -271,16 +277,13 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
     let mut idx = 0;
     let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
 
-    // Get the argument scope assuming ScopeId(0) has no parent.
-    let arg_scope = mir.scopes.get(0).and_then(|data| {
-        let scope = scopes[0];
-        if data.parent_scope.is_none() && !scope.is_null() &&
-           bcx.sess().opts.debuginfo == FullDebugInfo {
-            Some(scope)
-        } else {
-            None
-        }
-    });
+    // Get the argument scope, if it exists and if we need it.
+    let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE.index()];
+    let arg_scope = if !arg_scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
+        Some(arg_scope)
+    } else {
+        None
+    };
 
     mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| {
         let arg_ty = bcx.monomorphize(&arg_decl.ty);
diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs
index c9a4e540fa0..544c47e471f 100644
--- a/src/librustc_trans/mir/statement.rs
+++ b/src/librustc_trans/mir/statement.rs
@@ -10,7 +10,6 @@
 
 use rustc::mir::repr as mir;
 use common::{self, BlockAndBuilder};
-use debuginfo::DebugLoc;
 
 use super::MirContext;
 use super::TempRef;
@@ -22,8 +21,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                            -> BlockAndBuilder<'bcx, 'tcx> {
         debug!("trans_statement(statement={:?})", statement);
 
-        let debug_loc = DebugLoc::ScopeAt(self.scopes[statement.scope.index()],
-                                          statement.span);
+        let debug_loc = self.debug_loc(statement.source_info);
         debug_loc.apply_to_bcx(&bcx);
         debug_loc.apply(bcx.fcx());
         match statement.kind {
@@ -46,7 +44,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                 let ty = bcx.monomorphize(&ty.to_ty(bcx.tcx()));
 
                                 if !common::type_is_zero_size(bcx.ccx(), ty) {
-                                    span_bug!(statement.span,
+                                    span_bug!(statement.source_info.span,
                                               "operand {:?} already assigned",
                                               rvalue);
                                 } else {
diff --git a/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs b/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
index f0ecda92993..b5b6ca75727 100644
--- a/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
+++ b/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
@@ -247,11 +247,10 @@
 // lldb-command:continue
 
 #![allow(dead_code, unused_assignments, unused_variables)]
-#![feature(omit_gdb_pretty_printer_section, rustc_attrs)]
+#![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn immediate_args(a: isize, b: bool, c: f64) {
     println!("");
 }
@@ -268,51 +267,43 @@ struct BigStruct {
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn non_immediate_args(a: BigStruct, b: BigStruct) {
     println!("");
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn binding(a: i64, b: u64, c: f64) {
     let x = 0;
     println!("");
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn assignment(mut a: u64, b: u64, c: f64) {
     a = b;
     println!("");
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn function_call(x: u64, y: u64, z: f64) {
     println!("Hi!")
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn identifier(x: u64, y: u64, z: f64) -> u64 {
     x
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn return_expr(x: u64, y: u64, z: f64) -> u64 {
     return x;
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 {
     x + y
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn if_expr(x: u64, y: u64, z: f64) -> u64 {
     if x + y < 1000 {
         x
@@ -322,7 +313,6 @@ fn if_expr(x: u64, y: u64, z: f64) -> u64 {
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
     while x + y < 1000 {
         x += z
@@ -331,7 +321,6 @@ fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 {
     loop {
         x += z;