From 33e7d95e9c4ff02b4fb949ea51a26a8bf5e9ae5c Mon Sep 17 00:00:00 2001
From: Michael Woerister <michaelwoerister@gmail>
Date: Mon, 5 Aug 2013 11:12:40 +0200
Subject: [PATCH] debuginfo: Implemented proper handling of lexical scopes and
 variable shadowing.

---
 src/librustc/middle/trans/_match.rs           |  21 +-
 src/librustc/middle/trans/base.rs             |   6 +-
 src/librustc/middle/trans/common.rs           |   8 +-
 src/librustc/middle/trans/controlflow.rs      |   3 -
 src/librustc/middle/trans/debuginfo.rs        | 691 ++++++++++++++----
 src/librustc/middle/trans/expr.rs             |   4 +-
 .../debug-info/lexical-scope-in-for-loop.rs   |  77 ++
 src/test/debug-info/lexical-scope-in-if.rs    | 128 ++++
 src/test/debug-info/lexical-scope-in-match.rs | 148 ++++
 .../lexical-scope-in-unconditional-loop.rs    | 127 ++++
 src/test/debug-info/lexical-scope-in-while.rs | 123 ++++
 .../debug-info/lexical-scope-with-macro.rs    | 129 ++++
 .../lexical-scopes-in-block-expression.rs     | 344 +++++++++
 .../name-shadowing-and-scope-nesting.rs       |  95 +++
 ...variable-scope.rs => shadowed-variable.rs} |  29 +-
 src/test/debug-info/simple-lexical-scope.rs   |  87 +++
 16 files changed, 1874 insertions(+), 146 deletions(-)
 create mode 100644 src/test/debug-info/lexical-scope-in-for-loop.rs
 create mode 100644 src/test/debug-info/lexical-scope-in-if.rs
 create mode 100644 src/test/debug-info/lexical-scope-in-match.rs
 create mode 100644 src/test/debug-info/lexical-scope-in-unconditional-loop.rs
 create mode 100644 src/test/debug-info/lexical-scope-in-while.rs
 create mode 100644 src/test/debug-info/lexical-scope-with-macro.rs
 create mode 100644 src/test/debug-info/lexical-scopes-in-block-expression.rs
 create mode 100644 src/test/debug-info/name-shadowing-and-scope-nesting.rs
 rename src/test/debug-info/{variable-scope.rs => shadowed-variable.rs} (74%)
 create mode 100644 src/test/debug-info/simple-lexical-scope.rs

diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index c98d859337c..bb1e3fa1718 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -214,6 +214,7 @@ use middle::trans::expr;
 use middle::trans::glue;
 use middle::trans::tvec;
 use middle::trans::type_of;
+use middle::trans::debuginfo;
 use middle::ty;
 use util::common::indenter;
 use util::ppaux::{Repr, vec_map_to_str};
@@ -385,6 +386,7 @@ struct BindingInfo {
     llmatch: ValueRef,
     trmode: TransBindingMode,
     id: ast::NodeId,
+    span: span,
     ty: ty::t,
 }
 
@@ -1305,7 +1307,7 @@ fn insert_lllocals(bcx: @mut Block,
         BindArgument => bcx.fcx.llargs
     };
 
-    for (_, &binding_info) in bindings_map.iter() {
+    for (&ident, &binding_info) in bindings_map.iter() {
         let llval = match binding_info.trmode {
             // By value bindings: use the stack slot that we
             // copied/moved the value into
@@ -1325,6 +1327,14 @@ fn insert_lllocals(bcx: @mut Block,
 
         debug!("binding %? to %s", binding_info.id, bcx.val_to_str(llval));
         llmap.insert(binding_info.id, llval);
+
+        if bcx.sess().opts.extra_debuginfo {
+            debuginfo::create_match_binding_metadata(bcx,
+                                                     ident,
+                                                     binding_info.id,
+                                                     binding_info.ty,
+                                                     binding_info.span);
+        }
     }
     return bcx;
 }
@@ -1771,7 +1781,7 @@ fn create_bindings_map(bcx: @mut Block, pat: @ast::pat) -> BindingsMap {
     let ccx = bcx.ccx();
     let tcx = bcx.tcx();
     let mut bindings_map = HashMap::new();
-    do pat_bindings(tcx.def_map, pat) |bm, p_id, _s, path| {
+    do pat_bindings(tcx.def_map, pat) |bm, p_id, span, path| {
         let ident = path_to_ident(path);
         let variable_ty = node_id_type(bcx, p_id);
         let llvariable_ty = type_of::type_of(ccx, variable_ty);
@@ -1793,8 +1803,11 @@ fn create_bindings_map(bcx: @mut Block, pat: @ast::pat) -> BindingsMap {
             }
         };
         bindings_map.insert(ident, BindingInfo {
-            llmatch: llmatch, trmode: trmode,
-            id: p_id, ty: variable_ty
+            llmatch: llmatch,
+            trmode: trmode,
+            id: p_id,
+            span: span,
+            ty: variable_ty
         });
     }
     return bindings_map;
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 1291a586682..db439eceab8 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -1103,7 +1103,6 @@ pub fn trans_stmt(cx: @mut Block, s: &ast::stmt) -> @mut Block {
     }
 
     let mut bcx = cx;
-    debuginfo::update_source_pos(cx, s.span);
 
     match s.node {
         ast::stmt_expr(e, _) | ast::stmt_semi(e, _) => {
@@ -1634,7 +1633,8 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
           param_substs: param_substs,
           span: sp,
           path: path,
-          ccx: ccx
+          ccx: ccx,
+          debug_context: None,
     };
     fcx.llenv = unsafe {
           llvm::LLVMGetParam(llfndecl, fcx.env_arg_pos() as c_uint)
@@ -1933,7 +1933,7 @@ pub fn trans_fn(ccx: @mut CrateContext,
                   attrs,
                   output_type,
                   |fcx| {
-                      if ccx.sess.opts.extra_debuginfo
+                      if ccx.sess.opts.debuginfo
                           && fcx_has_nonzero_span(fcx) {
                           debuginfo::create_function_metadata(fcx);
                       }
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 3253e24ae88..10185a9a795 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -23,6 +23,7 @@ use middle::trans::build;
 use middle::trans::datum;
 use middle::trans::glue;
 use middle::trans::write_guard;
+use middle::trans::debuginfo;
 use middle::ty::substs;
 use middle::ty;
 use middle::typeck;
@@ -226,7 +227,12 @@ pub struct FunctionContext {
     path: path,
 
     // This function's enclosing crate context.
-    ccx: @mut CrateContext
+    ccx: @mut CrateContext,
+
+    // Used and maintained by the debuginfo module.
+    // @jdm: Not sure if the Option-wrapper is a good idea. It allows to save some space in
+    // non-debug builds, but generates quite a bit of noise at usage sites. What's your opinion?
+    debug_context: Option<~debuginfo::FunctionDebugContext>
 }
 
 impl FunctionContext {
diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs
index 46eb3928d59..2a3003812fe 100644
--- a/src/librustc/middle/trans/controlflow.rs
+++ b/src/librustc/middle/trans/controlflow.rs
@@ -19,7 +19,6 @@ use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee;
 use middle::trans::common::*;
-use middle::trans::debuginfo;
 use middle::trans::expr;
 use middle::trans::type_of::*;
 use middle::ty;
@@ -38,12 +37,10 @@ pub fn trans_block(bcx: @mut Block, b: &ast::Block, dest: expr::Dest) -> @mut Bl
     let _icx = push_ctxt("trans_block");
     let mut bcx = bcx;
     for s in b.stmts.iter() {
-        debuginfo::update_source_pos(bcx, b.span);
         bcx = trans_stmt(bcx, *s);
     }
     match b.expr {
         Some(e) => {
-            debuginfo::update_source_pos(bcx, e.span);
             bcx = expr::trans_into(bcx, e, dest);
         }
         None => {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 1fb64d9c671..c315421a779 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -99,9 +99,7 @@ pub struct DebugContext {
     priv created_files: HashMap<~str, DIFile>,
     priv created_functions: HashMap<ast::NodeId, DISubprogram>,
     priv created_blocks: HashMap<ast::NodeId, DILexicalBlock>,
-    priv created_types: HashMap<uint, DIType>,
-    priv last_function_context_id: ast::NodeId,
-    priv argument_counter: uint,
+    priv created_types: HashMap<uint, DIType>
 }
 
 impl DebugContext {
@@ -118,8 +116,21 @@ impl DebugContext {
             created_files: HashMap::new(),
             created_functions: HashMap::new(),
             created_blocks: HashMap::new(),
-            created_types: HashMap::new(),
-            last_function_context_id: -1, // magic value :(
+            created_types: HashMap::new()
+        };
+    }
+}
+
+
+pub struct FunctionDebugContext {
+    priv scope_map: HashMap<ast::NodeId, DIScope>,
+    priv argument_counter: uint,
+}
+
+impl FunctionDebugContext {
+    priv fn new() -> FunctionDebugContext {
+        return FunctionDebugContext {
+            scope_map: HashMap::new(),
             argument_counter: 1,
         };
     }
@@ -141,61 +152,24 @@ pub fn finalize(cx: @mut CrateContext) {
 pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) {
     let cx = bcx.ccx();
     let def_map = cx.tcx.def_map;
-    let pattern = local.pat;
 
-    let scope = match bcx.parent {
-        None => create_function_metadata(bcx.fcx),
-        Some(_) => lexical_block_metadata(bcx)
-    };
+    do pat_util::pat_bindings(def_map, local.pat) |_, node_id, span, path_ref| {
 
-    let filename = span_start(cx, local.span).file.name;
-    let file_metadata = file_metadata(cx, filename);
+        let var_ident = ast_util::path_to_ident(path_ref);
+        let var_type = node_id_type(bcx, node_id);
 
-    do pat_util::pat_bindings(def_map, pattern) |_, node_id, span, path_ref| {
-
-        let ident = ast_util::path_to_ident(path_ref);
-        let name: &str = cx.sess.str_of(ident);
-        debug!("create_local_var_metadata: %s", name);
-        let loc = span_start(cx, span);
-        let ty = node_id_type(bcx, node_id);
-        let type_metadata = type_metadata(cx, ty, span);
-
-        let var_metadata = do name.to_c_str().with_ref |name| {
-            unsafe {
-                llvm::LLVMDIBuilderCreateLocalVariable(
-                    DIB(cx),
-                    DW_TAG_auto_variable,
-                    scope,
-                    name,
-                    file_metadata,
-                    loc.line as c_uint,
-                    type_metadata,
-                    false,
-                    0,
-                    0)
-            }
-        };
-
-        let llptr = match bcx.fcx.lllocals.find_copy(&node_id) {
-            Some(v) => v,
-            None => {
-                bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id));
-            }
-        };
-
-        set_debug_location(cx, lexical_block_metadata(bcx), loc.line, loc.col.to_uint());
-        unsafe {
-            let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
-                DIB(cx),
-                llptr,
-                var_metadata,
-                bcx.llbb);
-
-            llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
-        }
+        declare_local(bcx, var_ident, node_id, var_type, span);
     }
 }
 
+pub fn create_match_binding_metadata(bcx: @mut Block,
+                                     variable_ident: ast::ident,
+                                     node_id: ast::NodeId,
+                                     variable_type: ty::t,
+                                     span: span) {
+    declare_local(bcx, variable_ident, node_id, variable_type, span);
+}
+
 /// Creates debug information for the given function argument.
 ///
 /// Adds the created metadata nodes directly to the crate's IR.
@@ -213,20 +187,6 @@ pub fn create_argument_metadata(bcx: @mut Block,
         return;
     }
 
-    // Limited the scope within which `debug_context` is live,
-    // otherwise => borrowing errors
-    {
-        let debug_context = dbg_cx(cx);
-
-        // If this is a new function, reset the counter. llvm::DIBuilder
-        // wants arguments to be indexed starting from 1.
-        if fcx.id != debug_context.last_function_context_id {
-                    debug_context.argument_counter = 1;
-        }
-        // Keep track of the function we are in
-        debug_context.last_function_context_id = fcx.id;
-    }
-
     let def_map = cx.tcx.def_map;
     let file_metadata = file_metadata(cx, filename);
     let scope = create_function_metadata(fcx);
@@ -241,9 +201,9 @@ pub fn create_argument_metadata(bcx: @mut Block,
         debug!("create_argument_metadata: %s", name);
 
         let argument_index = {
-            let debug_context = dbg_cx(cx);
-            let argument_index = debug_context.argument_counter;
-            debug_context.argument_counter += 1;
+            let counter = &mut fcx.debug_context.get_mut_ref().argument_counter;
+            let argument_index = *counter;
+            *counter += 1;
             argument_index as c_uint
         };
 
@@ -270,7 +230,7 @@ pub fn create_argument_metadata(bcx: @mut Block,
             }
         };
 
-        set_debug_location(cx, lexical_block_metadata(bcx), loc.line, loc.col.to_uint());
+        set_debug_location(cx, scope, loc.line, loc.col.to_uint());
         unsafe {
             let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
                 DIB(cx),
@@ -286,20 +246,28 @@ pub fn create_argument_metadata(bcx: @mut Block,
 /// Sets the current debug location at the beginning of the span
 ///
 /// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...)
-pub fn update_source_pos(bcx: @mut Block, span: span) {
-    if !bcx.sess().opts.debuginfo || (*span.lo == 0 && *span.hi == 0) {
+pub fn update_source_pos(fcx: &FunctionContext,
+                         node_id: ast::NodeId,
+                         span: span) {
+    let cx: &mut CrateContext = fcx.ccx;
+
+    if !cx.sess.opts.debuginfo || (*span.lo == 0 && *span.hi == 0) {
         return;
     }
-    debug!("update_source_pos: %s", bcx.sess().codemap.span_to_str(span));
-    let loc = span_start(bcx.ccx(), span);
-    set_debug_location(bcx.ccx(), lexical_block_metadata(bcx), loc.line, loc.col.to_uint())
+
+    debug!("update_source_pos: %s", cx.sess.codemap.span_to_str(span));
+
+    let loc = span_start(cx, span);
+    let scope = scope_metadata(fcx, node_id, span);
+
+    set_debug_location(cx, scope, loc.line, loc.col.to_uint());
 }
 
 /// Creates debug information for the given function.
 ///
 /// Adds the created metadata nodes directly to the crate's IR.
 /// The return value should be ignored if called from outside of the debuginfo module.
-pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram {
+pub fn create_function_metadata(fcx: &mut FunctionContext) -> DISubprogram {
     let cx = fcx.ccx;
 
     let fnitem = cx.tcx.items.get_copy(&fcx.id);
@@ -349,8 +317,8 @@ pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram {
         _ => fcx.ccx.sess.bug(fmt!("create_function_metadata: unexpected sort of node: %?", fnitem))
     };
 
-    match dbg_cx(cx).created_functions.find(&id) {
-        Some(fn_metadata) => return *fn_metadata,
+    match dbg_cx(cx).created_functions.find_copy(&id) {
+        Some(fn_metadata) => return fn_metadata,
         None => ()
     }
 
@@ -405,6 +373,25 @@ pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram {
             }
         }};
 
+    assert!(fcx.debug_context.is_none());
+
+    let mut fn_debug_context = ~FunctionDebugContext::new();
+    let entry_block_id = fcx.entry_bcx.get_ref().node_info.get_ref().id;
+    let entry_block = cx.tcx.items.get(&entry_block_id);
+
+    match *entry_block {
+        ast_map::node_block(ref block) => {
+            let scope_map = &mut fn_debug_context.scope_map;
+            populate_scope_map(cx, block, fn_metadata, scope_map);
+        }
+        _ => cx.sess.span_bug(span,
+                fmt!("debuginfo::create_function_metadata() - \
+                     FunctionContext::entry_bcx::node_info points to wrong type of ast_map entry. \
+                     Expected: ast_map::node_block, actual: %?", *entry_block))
+    }
+
+    fcx.debug_context = Some(fn_debug_context);
+
     dbg_cx(cx).created_functions.insert(id, fn_metadata);
     return fn_metadata;
 }
@@ -445,6 +432,56 @@ fn compile_unit_metadata(cx: @mut CrateContext) {
     }}}}};
 }
 
+fn declare_local(bcx: @mut Block,
+                 variable_ident: ast::ident,
+                 node_id: ast::NodeId,
+                 variable_type: ty::t,
+                 span: span) {
+    let cx: &mut CrateContext = bcx.ccx();
+
+    let filename = span_start(cx, span).file.name;
+    let file_metadata = file_metadata(cx, filename);
+
+    let name: &str = cx.sess.str_of(variable_ident);
+    let loc = span_start(cx, span);
+    let type_metadata = type_metadata(cx, variable_type, span);
+    let scope = scope_metadata(bcx.fcx, node_id, span);
+
+    let var_metadata = do name.as_c_str |name| {
+        unsafe {
+            llvm::LLVMDIBuilderCreateLocalVariable(
+                DIB(cx),
+                DW_TAG_auto_variable,
+                scope,
+                name,
+                file_metadata,
+                loc.line as c_uint,
+                type_metadata,
+                false,
+                0,
+                0)
+        }
+    };
+
+    let llptr = match bcx.fcx.lllocals.find_copy(&node_id) {
+        Some(v) => v,
+        None => {
+            bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id));
+        }
+    };
+
+    set_debug_location(cx, scope, loc.line, loc.col.to_uint());
+    unsafe {
+        let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
+            DIB(cx),
+            llptr,
+            var_metadata,
+            bcx.llbb);
+
+        llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
+    }
+}
+
 fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile {
     match dbg_cx(cx).created_files.find_equiv(&full_path) {
         Some(file_metadata) => return *file_metadata,
@@ -473,50 +510,26 @@ fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile {
     return file_metadata;
 }
 
-/// Get or create the lexical block metadata node for the given LLVM basic block.
-fn lexical_block_metadata(bcx: @mut Block) -> DILexicalBlock {
-    let cx = bcx.ccx();
-    let mut bcx = bcx;
+/// Finds the scope metadata node for the given AST node.
+fn scope_metadata(fcx: &FunctionContext,
+                  node_id: ast::NodeId,
+                  span: span) -> DIScope {
+    if fcx.debug_context.is_none() {
+        fcx.ccx.sess.span_bug(span, "debuginfo: FunctionDebugContext should be initialized \
+                                     but is not!");
+    }
 
-    // Search up the tree of basic blocks until we find one that knows the containing lexical block.
-    while bcx.node_info.is_none() {
-        match bcx.parent {
-            Some(b) => bcx = b,
-            None => cx.sess.bug("debuginfo: Could not find lexical block for LLVM basic block.")
+    let scope_map = &fcx.debug_context.get_ref().scope_map;
+
+    match scope_map.find_copy(&node_id) {
+        Some(scope_metadata) => scope_metadata,
+        None => {
+            let node = fcx.ccx.tcx.items.get_copy(&node_id);
+
+            fcx.ccx.sess.span_bug(span,
+                fmt!("debuginfo: Could not find scope info for node %?", node));
         }
     }
-
-    let span = bcx.node_info.unwrap().span;
-    let id = bcx.node_info.unwrap().id;
-
-    // Check whether we already have a cache entry for this node id
-    match dbg_cx(cx).created_blocks.find(&id) {
-        Some(block) => return *block,
-        None => ()
-    }
-
-    debug!("lexical_block_metadata: %s", bcx.sess().codemap.span_to_str(span));
-
-    let parent = match bcx.parent {
-        None => create_function_metadata(bcx.fcx),
-        Some(b) => lexical_block_metadata(b)
-    };
-
-    let loc = span_start(cx, span);
-    let file_metadata = file_metadata(cx, loc.file.name);
-
-    let lexical_block_metadata = unsafe {
-        llvm::LLVMDIBuilderCreateLexicalBlock(
-            DIB(cx),
-            parent,
-            file_metadata,
-            loc.line as c_uint,
-            loc.col.to_uint() as c_uint)
-    };
-
-    dbg_cx(cx).created_blocks.insert(id, lexical_block_metadata);
-
-    return lexical_block_metadata;
 }
 
 fn basic_type_metadata(cx: &mut CrateContext, t: ty::t) -> DIType {
@@ -1206,7 +1219,7 @@ fn type_metadata(cx: &mut CrateContext,
     return type_metadata;
 }
 
-fn set_debug_location(cx: @mut CrateContext, scope: DIScope, line: uint, col: uint) {
+fn set_debug_location(cx: &mut CrateContext, scope: DIScope, line: uint, col: uint) {
     if dbg_cx(cx).curr_loc == (line, col) {
         return;
     }
@@ -1224,7 +1237,6 @@ fn set_debug_location(cx: @mut CrateContext, scope: DIScope, line: uint, col: ui
     }
 }
 
-
 //=-------------------------------------------------------------------------------------------------
 //  Utility Functions
 //=-------------------------------------------------------------------------------------------------
@@ -1256,3 +1268,436 @@ fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut DebugContext {
 fn DIB(cx: &CrateContext) -> DIBuilderRef {
     cx.dbg_cx.get_ref().builder
 }
+
+
+// This procedure builds the *scope map* for a given function, which maps any given ast::NodeId in
+// the function's AST to the correct DIScope metadata instance.
+//
+// This builder procedure walks the AST in execution order and keeps track of what belongs to which
+// scope, creating DIScope DIEs along the way, and introducing *artificial* lexical scope
+// descriptors where necessary. These artificial scopes allow GDB to correctly handle name
+// shadowing.
+fn populate_scope_map(cx: &mut CrateContext,
+                      fn_entry_block: &ast::Block,
+                      fn_metadata: DISubprogram,
+                      scope_map: &mut HashMap<ast::NodeId, DIScope>) {
+
+    struct ScopeStackEntry {
+        scope_metadata: DIScope,
+        ident: Option<ast::ident>
+    }
+
+    let mut scope_stack = ~[ScopeStackEntry { scope_metadata: fn_metadata, ident: None }];
+
+    walk_block(cx, fn_entry_block, &mut scope_stack, scope_map);
+
+    // local helper functions for walking the AST.
+
+    fn with_new_scope(cx: &mut CrateContext,
+                      scope_span: span,
+                      scope_stack: &mut ~[ScopeStackEntry],
+                      scope_map: &mut HashMap<ast::NodeId, DIScope>,
+                      inner_walk: &fn(&mut CrateContext,
+                                      &mut ~[ScopeStackEntry],
+                                      &mut HashMap<ast::NodeId, DIScope>)) {
+
+        // Create a new lexical scope and push it onto the stack
+        let loc = cx.sess.codemap.lookup_char_pos(scope_span.lo);
+        let file_metadata = file_metadata(cx, loc.file.name);
+        let parent_scope = scope_stack.last().scope_metadata;
+
+        let scope_metadata = unsafe {
+            llvm::LLVMDIBuilderCreateLexicalBlock(
+                DIB(cx),
+                parent_scope,
+                file_metadata,
+                loc.line as c_uint,
+                loc.col.to_uint() as c_uint)
+        };
+
+        scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, ident: None });
+
+        inner_walk(cx, scope_stack, scope_map);
+
+        // pop artificial scopes
+        while scope_stack.last().ident.is_some() {
+            scope_stack.pop();
+        }
+
+        if scope_stack.last().scope_metadata != scope_metadata {
+            cx.sess.span_bug(scope_span, "debuginfo: Inconsistency in scope management.");
+        }
+
+        scope_stack.pop();
+    }
+
+    fn walk_block(cx: &mut CrateContext,
+                  block: &ast::Block,
+                  scope_stack: &mut ~[ScopeStackEntry],
+                  scope_map: &mut HashMap<ast::NodeId, DIScope>) {
+
+        scope_map.insert(block.id, scope_stack.last().scope_metadata);
+
+        // The interesting things here are statements and the concluding expression.
+        for &@ ref statement in block.stmts.iter() {
+            scope_map.insert(ast_util::stmt_id(statement), scope_stack.last().scope_metadata);
+
+            match statement.node {
+                ast::stmt_decl(@ref decl, _) => walk_decl(cx, decl, scope_stack, scope_map),
+                ast::stmt_expr(@ref exp, _) |
+                ast::stmt_semi(@ref exp, _) => walk_expr(cx, exp, scope_stack, scope_map),
+                ast::stmt_mac(*) => () // ignore macros (which should be expanded anyway)
+            }
+        }
+
+        for &@ref exp in block.expr.iter() {
+            walk_expr(cx, exp, scope_stack, scope_map);
+        }
+    }
+
+    fn walk_decl(cx: &mut CrateContext,
+                 decl: &ast::decl,
+                 scope_stack: &mut ~[ScopeStackEntry],
+                 scope_map: &mut HashMap<ast::NodeId, DIScope>) {
+        match *decl {
+            codemap::spanned { node: ast::decl_local(@ref local), _ } => {
+
+                scope_map.insert(local.id, scope_stack.last().scope_metadata);
+
+                walk_pattern(cx, local.pat, scope_stack, scope_map);
+
+                for &@ref exp in local.init.iter() {
+                    walk_expr(cx, exp, scope_stack, scope_map);
+                }
+            }
+            _ => ()
+        }
+    }
+
+    fn walk_pattern(cx: &mut CrateContext,
+                    pat: @ast::pat,
+                    scope_stack: &mut ~[ScopeStackEntry],
+                    scope_map: &mut HashMap<ast::NodeId, DIScope>) {
+
+        let def_map = cx.tcx.def_map;
+
+        // Unfortunately, we cannot just use pat_util::pat_bindings() or ast_util::walk_pat() here
+        // because we have to visit *all* nodes in order to put them into the scope map. The above
+        // function don't do that.
+        match pat.node {
+            ast::pat_ident(_, ref path_ref, ref sub_pat_opt) => {
+
+                // Check if this is a binding. If so we need to put it on the scope stack and maybe
+                // introduce an articial scope
+                if pat_util::pat_is_binding(def_map, pat) {
+
+                    let ident = ast_util::path_to_ident(path_ref);
+
+                    // LLVM does not properly generate 'DW_AT_start_scope' fields for variable DIEs.
+                    // For this reason we have to introduce an artificial scope at bindings whenever
+                    // a variable with the same name is declared in *any* parent scope.
+                    //
+                    // Otherwise the following error occurs:
+                    //
+                    // let x = 10;
+                    //
+                    // do_something(); // 'gdb print x' correctly prints 10
+                    //
+                    // {
+                    //     do_something(); // 'gdb print x' prints 0, because it already reads the
+                    //                     // uninitialized 'x' from the next line...
+                    //     let x = 100;
+                    //     do_something(); // 'gdb print x' correctly prints 100
+                    // }
+
+                    // Is there already a binding with that name?
+                    let need_new_scope = scope_stack
+                        .rev_iter()
+                        .find_(|entry| entry.ident.iter().any(|i| *i == ident))
+                        .is_some();
+
+                    if need_new_scope {
+                        // Create a new lexical scope and push it onto the stack
+                        let loc = cx.sess.codemap.lookup_char_pos(pat.span.lo);
+                        let file_metadata = file_metadata(cx, loc.file.name);
+                        let parent_scope = scope_stack.last().scope_metadata;
+
+                        let scope_metadata = unsafe {
+                            llvm::LLVMDIBuilderCreateLexicalBlock(
+                                DIB(cx),
+                                parent_scope,
+                                file_metadata,
+                                loc.line as c_uint,
+                                loc.col.to_uint() as c_uint)
+                        };
+
+                        scope_stack.push(ScopeStackEntry {
+                            scope_metadata: scope_metadata,
+                            ident: Some(ident)
+                        });
+
+                    } else {
+                        // Push a new entry anyway so the name can be found
+                        let prev_metadata = scope_stack.last().scope_metadata;
+                        scope_stack.push(ScopeStackEntry {
+                            scope_metadata: prev_metadata,
+                            ident: Some(ident)
+                        });
+                    }
+                }
+
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+
+                for &sub_pat in sub_pat_opt.iter() {
+                    walk_pattern(cx, sub_pat, scope_stack, scope_map);
+                }
+            }
+
+            ast::pat_wild => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+            }
+
+            ast::pat_enum(_, ref sub_pats_opt) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+
+                for ref sub_pats in sub_pats_opt.iter() {
+                    for &p in sub_pats.iter() {
+                        walk_pattern(cx, p, scope_stack, scope_map);
+                    }
+                }
+            }
+
+            ast::pat_struct(_, ref field_pats, _) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+
+                for &ast::field_pat { pat: sub_pat, _ } in field_pats.iter() {
+                    walk_pattern(cx, sub_pat, scope_stack, scope_map);
+                }
+            }
+
+            ast::pat_tup(ref sub_pats) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+
+                for &sub_pat in sub_pats.iter() {
+                    walk_pattern(cx, sub_pat, scope_stack, scope_map);
+                }
+            }
+
+            ast::pat_box(sub_pat)    |
+            ast::pat_uniq(sub_pat)   |
+            ast::pat_region(sub_pat) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+                walk_pattern(cx, sub_pat, scope_stack, scope_map);
+            }
+
+            ast::pat_lit(@ref exp) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+                walk_expr(cx, exp, scope_stack, scope_map);
+            }
+
+            ast::pat_range(@ref exp1, @ref exp2) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+                walk_expr(cx, exp1, scope_stack, scope_map);
+                walk_expr(cx, exp2, scope_stack, scope_map);
+            }
+
+            ast::pat_vec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => {
+                scope_map.insert(pat.id, scope_stack.last().scope_metadata);
+
+                for &sub_pat in front_sub_pats.iter() {
+                    walk_pattern(cx, sub_pat, scope_stack, scope_map);
+                }
+
+                for &sub_pat in middle_sub_pats.iter() {
+                    walk_pattern(cx, sub_pat, scope_stack, scope_map);
+                }
+
+                for &sub_pat in back_sub_pats.iter() {
+                    walk_pattern(cx, sub_pat, scope_stack, scope_map);
+                }
+            }
+        }
+    }
+
+    fn walk_expr(cx: &mut CrateContext,
+                 exp: &ast::expr,
+                 scope_stack: &mut ~[ScopeStackEntry],
+                 scope_map: &mut HashMap<ast::NodeId, DIScope>) {
+
+        scope_map.insert(exp.id, scope_stack.last().scope_metadata);
+
+        match exp.node {
+            ast::expr_self     |
+            ast::expr_lit(_)   |
+            ast::expr_break(_) |
+            ast::expr_again(_) |
+            ast::expr_path(_)  => (),
+
+            ast::expr_vstore(@ref sub_exp, _)   |
+            ast::expr_cast(@ref sub_exp, _)     |
+            ast::expr_addr_of(_, @ref sub_exp)  |
+            ast::expr_field(@ref sub_exp, _, _) |
+            ast::expr_paren(@ref sub_exp)       => walk_expr(cx, sub_exp, scope_stack, scope_map),
+
+            ast::expr_ret(exp_opt) => match exp_opt {
+                Some(@ref sub_exp) => walk_expr(cx, sub_exp, scope_stack, scope_map),
+                None => ()
+            },
+
+            ast::expr_unary(node_id, _, @ref sub_exp) => {
+                scope_map.insert(node_id, scope_stack.last().scope_metadata);
+                walk_expr(cx, sub_exp, scope_stack, scope_map);
+            }
+
+            ast::expr_assign_op(node_id, _, @ref lhs, @ref rhs) |
+            ast::expr_index(node_id, @ref lhs, @ref rhs)        |
+            ast::expr_binary(node_id, _, @ref lhs, @ref rhs)    => {
+                scope_map.insert(node_id, scope_stack.last().scope_metadata);
+                walk_expr(cx, lhs, scope_stack, scope_map);
+                walk_expr(cx, rhs, scope_stack, scope_map);
+            }
+
+            ast::expr_vec(ref init_expressions, _) |
+            ast::expr_tup(ref init_expressions)    => {
+                for &@ref ie in init_expressions.iter() {
+                    walk_expr(cx, ie, scope_stack, scope_map);
+                }
+            }
+
+            ast::expr_assign(@ref sub_exp1, @ref sub_exp2)    |
+            ast::expr_log(@ref sub_exp1, @ref sub_exp2)       |
+            ast::expr_repeat(@ref sub_exp1, @ref sub_exp2, _) => {
+                walk_expr(cx, sub_exp1, scope_stack, scope_map);
+                walk_expr(cx, sub_exp2, scope_stack, scope_map);
+            }
+
+            ast::expr_if(@ref cond_exp, ref then_block, ref opt_else_exp) => {
+                walk_expr(cx, cond_exp, scope_stack, scope_map);
+
+                do with_new_scope(cx, then_block.span, scope_stack, scope_map) |c, s, m| {
+                    walk_block(c, then_block, s, m);
+                }
+
+                match *opt_else_exp {
+                    Some(@ref else_exp) => walk_expr(cx, else_exp, scope_stack, scope_map),
+                    _ => ()
+                }
+            }
+
+            ast::expr_while(@ref cond_exp, ref loop_body) => {
+                walk_expr(cx, cond_exp, scope_stack, scope_map);
+
+                do with_new_scope(cx, loop_body.span, scope_stack, scope_map) |c, s, m| {
+                    walk_block(c, loop_body, s, m);
+                }
+            }
+
+            ast::expr_for_loop(_, _, _) => {
+                cx.sess.span_bug(exp.span, "debuginfo::populate_scope_map() - \
+                                            Found unexpanded for-loop.");
+            }
+
+            ast::expr_mac(_) => {
+                cx.sess.span_bug(exp.span, "debuginfo::populate_scope_map() - \
+                                            Found unexpanded macro.");
+            }
+
+            ast::expr_loop(ref block, _) |
+            ast::expr_block(ref block)   => {
+                do with_new_scope(cx, block.span, scope_stack, scope_map) |c, s, m| {
+                    walk_block(c, block, s, m);
+                }
+            }
+
+            ast::expr_fn_block(ast::fn_decl { inputs: ref inputs, _ }, ref block) => {
+
+                do with_new_scope(cx, block.span, scope_stack, scope_map) |c, s, m| {
+
+                    for &ast::arg { pat: pattern, _ } in inputs.iter() {
+                        walk_pattern(c, pattern, s, m);
+                    }
+
+                    walk_block(c, block, s, m);
+                }
+            }
+
+            // ast::expr_loop_body(@ref inner_exp) |
+            ast::expr_do_body(@ref inner_exp)   => {
+                let inner_expr_is_expr_fn_block = match *inner_exp {
+                    ast::expr { node: ast::expr_fn_block(*), _ } => true,
+                    _ => false
+                };
+
+                if !inner_expr_is_expr_fn_block {
+                    cx.sess.span_bug(inner_exp.span, "debuginfo: Inner expression was expected \
+                                                      to be an ast::expr_fn_block.");
+                }
+
+                walk_expr(cx, inner_exp, scope_stack, scope_map);
+            }
+
+            ast::expr_call(@ref fn_exp, ref args, _) => {
+                walk_expr(cx, fn_exp, scope_stack, scope_map);
+
+                for &@ref arg_exp in args.iter() {
+                    walk_expr(cx, arg_exp, scope_stack, scope_map);
+                }
+            }
+
+            ast::expr_method_call(node_id, @ref receiver_exp, _, _, ref args, _) => {
+                scope_map.insert(node_id, scope_stack.last().scope_metadata);
+                walk_expr(cx, receiver_exp, scope_stack, scope_map);
+
+                for &@ref arg_exp in args.iter() {
+                    walk_expr(cx, arg_exp, scope_stack, scope_map);
+                }
+            }
+
+            ast::expr_match(@ref discriminant_exp, ref arms) => {
+                walk_expr(cx, discriminant_exp, scope_stack, scope_map);
+
+                // for each arm we have to first walk the pattern as these might introduce new
+                // artificial scopes. It should be sufficient to walk only one pattern per arm, as
+                // they all must contain the same binding names
+
+                for arm_ref in arms.iter() {
+                    let arm_span = arm_ref.pats[0].span;
+
+                    do with_new_scope(cx, arm_span, scope_stack, scope_map) |c, s, m| {
+                        walk_pattern(c, arm_ref.pats[0], s, m);
+
+                        for &@ref guard_exp in arm_ref.guard.iter() {
+                            walk_expr(c, guard_exp, s, m)
+                        }
+
+                        walk_block(c, &arm_ref.body, s, m);
+                    }
+                }
+            }
+
+            ast::expr_struct(_, ref fields, ref base_exp) => {
+                for &ast::Field { expr: @ref exp, _ } in fields.iter() {
+                    walk_expr(cx, exp, scope_stack, scope_map);
+                }
+
+                match *base_exp {
+                    Some(@ref exp) => walk_expr(cx, exp, scope_stack, scope_map),
+                    None => ()
+                }
+            }
+
+            ast::expr_inline_asm(ast::inline_asm { inputs: ref inputs,
+                                                   outputs: ref outputs,
+                                                   _ }) => {
+                // inputs, outputs: ~[(@str, @expr)]
+                for &(_, @ref exp) in inputs.iter() {
+                    walk_expr(cx, exp, scope_stack, scope_map);
+                }
+
+                for &(_, @ref exp) in outputs.iter() {
+                    walk_expr(cx, exp, scope_stack, scope_map);
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index cb4a7f364da..d56fcb1081f 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -413,7 +413,7 @@ pub fn trans_into(bcx: @mut Block, expr: @ast::expr, dest: Dest) -> @mut Block {
            dest.to_str(bcx.ccx()));
     let _indenter = indenter();
 
-    debuginfo::update_source_pos(bcx, expr.span);
+    debuginfo::update_source_pos(bcx.fcx, expr.id, expr.span);
 
     let dest = {
         if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
@@ -485,7 +485,7 @@ fn trans_to_datum_unadjusted(bcx: @mut Block, expr: @ast::expr) -> DatumBlock {
     debug!("trans_to_datum_unadjusted(expr=%s)", bcx.expr_to_str(expr));
     let _indenter = indenter();
 
-    debuginfo::update_source_pos(bcx, expr.span);
+    debuginfo::update_source_pos(bcx.fcx, expr.id, expr.span);
 
     match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
         ty::LvalueExpr => {
diff --git a/src/test/debug-info/lexical-scope-in-for-loop.rs b/src/test/debug-info/lexical-scope-in-for-loop.rs
new file mode 100644
index 00000000000..9aae26b50be
--- /dev/null
+++ b/src/test/debug-info/lexical-scope-in-for-loop.rs
@@ -0,0 +1,77 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// FIRST ITERATION
+// debugger:finish
+// debugger:print x
+// check:$1 = 1
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$2 = -1
+// debugger:continue
+
+// SECOND ITERATION
+// debugger:finish
+// debugger:print x
+// check:$3 = 2
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$4 = -2
+// debugger:continue
+
+// THIRD ITERATION
+// debugger:finish
+// debugger:print x
+// check:$5 = 3
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$6 = -3
+// debugger:continue
+
+// AFTER LOOP
+// debugger:finish
+// debugger:print x
+// check:$7 = 1000000
+// debugger:continue
+
+fn main() {
+
+    let range = [1, 2, 3];
+
+    let x = 1000000; // wan meeeljen doollaars!
+
+    for &x in range.iter() {
+        zzz();
+        sentinel();
+
+        let x = -1 * x;
+
+        zzz();
+        sentinel();
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/lexical-scope-in-if.rs b/src/test/debug-info/lexical-scope-in-if.rs
new file mode 100644
index 00000000000..296ad7ca861
--- /dev/null
+++ b/src/test/debug-info/lexical-scope-in-if.rs
@@ -0,0 +1,128 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// BEFORE if
+// debugger:finish
+// debugger:print x
+// check:$1 = 999
+// debugger:print y
+// check:$2 = -1
+// debugger:continue
+
+// AT BEGINNING of 'then' block
+// debugger:finish
+// debugger:print x
+// check:$3 = 999
+// debugger:print y
+// check:$4 = -1
+// debugger:continue
+
+// AFTER 1st redeclaration of 'x'
+// debugger:finish
+// debugger:print x
+// check:$5 = 1001
+// debugger:print y
+// check:$6 = -1
+// debugger:continue
+
+// AFTER 2st redeclaration of 'x'
+// debugger:finish
+// debugger:print x
+// check:$7 = 1002
+// debugger:print y
+// check:$8 = 1003
+// debugger:continue
+
+// AFTER 1st if expression
+// debugger:finish
+// debugger:print x
+// check:$9 = 999
+// debugger:print y
+// check:$10 = -1
+// debugger:continue
+
+// BEGINNING of else branch
+// debugger:finish
+// debugger:print x
+// check:$11 = 999
+// debugger:print y
+// check:$12 = -1
+// debugger:continue
+
+// BEGINNING of else branch
+// debugger:finish
+// debugger:print x
+// check:$13 = 1004
+// debugger:print y
+// check:$14 = 1005
+// debugger:continue
+
+// BEGINNING of else branch
+// debugger:finish
+// debugger:print x
+// check:$15 = 999
+// debugger:print y
+// check:$16 = -1
+// debugger:continue
+
+use std::util;
+
+fn main() {
+
+    let x = 999;
+    let y = -1;
+
+    zzz();
+    sentinel();
+
+    if x < 1000 {
+        zzz();
+        sentinel();
+
+        let x = 1001;
+
+        zzz();
+        sentinel();
+
+        let x = 1002;
+        let y = 1003;
+        zzz();
+        sentinel();
+    } else {
+        util::unreachable();
+    }
+
+    zzz();
+    sentinel();
+
+    if x > 1000 {
+        util::unreachable();
+    } else {
+        zzz();
+        sentinel();
+
+        let x = 1004;
+        let y = 1005;
+        zzz();
+        sentinel();
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/lexical-scope-in-match.rs b/src/test/debug-info/lexical-scope-in-match.rs
new file mode 100644
index 00000000000..ccdb20d8202
--- /dev/null
+++ b/src/test/debug-info/lexical-scope-in-match.rs
@@ -0,0 +1,148 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// debugger:finish
+// debugger:print shadowed
+// check:$1 = 231
+// debugger:print not_shadowed
+// check:$2 = 232
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$3 = 233
+// debugger:print not_shadowed
+// check:$4 = 232
+// debugger:print local_to_arm
+// check:$5 = 234
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$6 = 236
+// debugger:print not_shadowed
+// check:$7 = 232
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$8 = 237
+// debugger:print not_shadowed
+// check:$9 = 232
+// debugger:print local_to_arm
+// check:$10 = 238
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$11 = 239
+// debugger:print not_shadowed
+// check:$12 = 232
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$13 = 241
+// debugger:print not_shadowed
+// check:$14 = 232
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$15 = 243
+// debugger:print *local_to_arm
+// check:$16 = 244
+// debugger:continue
+
+// debugger:finish
+// debugger:print shadowed
+// check:$17 = 231
+// debugger:print not_shadowed
+// check:$18 = 232
+// debugger:continue
+
+struct Struct {
+    x: int,
+    y: int
+}
+
+fn main() {
+
+    let shadowed = 231;
+    let not_shadowed = 232;
+
+    zzz();
+    sentinel();
+
+    match (233, 234) {
+        (shadowed, local_to_arm) => {
+
+            zzz();
+            sentinel();
+        }
+    }
+
+    match (235, 236) {
+        // with literal
+        (235, shadowed) => {
+
+            zzz();
+            sentinel();
+        }
+        _ => {}
+    }
+
+    match Struct { x: 237, y: 238 } {
+        Struct { x: shadowed, y: local_to_arm } => {
+
+            zzz();
+            sentinel();
+        }
+    }
+
+    match Struct { x: 239, y: 240 } {
+        // ignored field
+        Struct { x: shadowed, _ } => {
+
+            zzz();
+            sentinel();
+        }
+    }
+
+    match Struct { x: 241, y: 242 } {
+        // with literal
+        Struct { x: shadowed, y: 242 } => {
+
+            zzz();
+            sentinel();
+        }
+        _ => {}
+    }
+
+    match (243, 244) {
+        (shadowed, ref local_to_arm) => {
+
+            zzz();
+            sentinel();
+        }
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/lexical-scope-in-unconditional-loop.rs b/src/test/debug-info/lexical-scope-in-unconditional-loop.rs
new file mode 100644
index 00000000000..21c4e746685
--- /dev/null
+++ b/src/test/debug-info/lexical-scope-in-unconditional-loop.rs
@@ -0,0 +1,127 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// FIRST ITERATION
+// debugger:finish
+// debugger:print x
+// check:$1 = 0
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$2 = 1
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$3 = 101
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$4 = 101
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$5 = -987
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$6 = 101
+// debugger:continue
+
+
+// SECOND ITERATION
+// debugger:finish
+// debugger:print x
+// check:$7 = 1
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$8 = 2
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$9 = 102
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$10 = 102
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$11 = -987
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$12 = 102
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$13 = 2
+// debugger:continue
+
+fn main() {
+
+    let mut x = 0;
+
+    loop {
+        if x >= 2 {
+            break;
+        }
+
+        zzz();
+        sentinel();
+
+        x += 1;
+        zzz();
+        sentinel();
+
+        // Shadow x
+        let x = x + 100;
+        zzz();
+        sentinel();
+
+        // open scope within loop's top level scope
+        {
+            zzz();
+            sentinel();
+
+            let x = -987;
+
+            zzz();
+            sentinel();
+        }
+
+        // Check that we get the x before the inner scope again
+        zzz();
+        sentinel();
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/lexical-scope-in-while.rs b/src/test/debug-info/lexical-scope-in-while.rs
new file mode 100644
index 00000000000..b350130df8f
--- /dev/null
+++ b/src/test/debug-info/lexical-scope-in-while.rs
@@ -0,0 +1,123 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// FIRST ITERATION
+// debugger:finish
+// debugger:print x
+// check:$1 = 0
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$2 = 1
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$3 = 101
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$4 = 101
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$5 = -987
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$6 = 101
+// debugger:continue
+
+
+// SECOND ITERATION
+// debugger:finish
+// debugger:print x
+// check:$7 = 1
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$8 = 2
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$9 = 102
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$10 = 102
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$11 = -987
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$12 = 102
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$13 = 2
+// debugger:continue
+
+fn main() {
+
+    let mut x = 0;
+
+    while x < 2 {
+        zzz();
+        sentinel();
+
+        x += 1;
+        zzz();
+        sentinel();
+
+        // Shadow x
+        let x = x + 100;
+        zzz();
+        sentinel();
+
+        // open scope within loop's top level scope
+        {
+            zzz();
+            sentinel();
+
+            let x = -987;
+
+            zzz();
+            sentinel();
+        }
+
+        // Check that we get the x before the inner scope again
+        zzz();
+        sentinel();
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/lexical-scope-with-macro.rs b/src/test/debug-info/lexical-scope-with-macro.rs
new file mode 100644
index 00000000000..409ecd5a0e2
--- /dev/null
+++ b/src/test/debug-info/lexical-scope-with-macro.rs
@@ -0,0 +1,129 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// debugger:finish
+// debugger:print a
+// check:$1 = 10
+// debugger:print b
+// check:$2 = 34
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$3 = 890242
+// debugger:print b
+// check:$4 = 34
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$5 = 10
+// debugger:print b
+// check:$6 = 34
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$7 = 102
+// debugger:print b
+// check:$8 = 34
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$9 = 110
+// debugger:print b
+// check:$10 = 34
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$11 = 10
+// debugger:print b
+// check:$12 = 34
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$13 = 10
+// debugger:print b
+// check:$14 = 34
+// debugger:print c
+// check:$15 = 400
+// debugger:continue
+
+macro_rules! trivial(
+    ($e1:expr) => ($e1)
+)
+
+macro_rules! no_new_scope(
+    ($e1:expr) => (($e1 + 2) - 1)
+)
+
+macro_rules! new_scope(
+    () => ({
+        let a = 890242;
+        zzz();
+        sentinel();
+    })
+)
+
+macro_rules! shadow_within_macro(
+    ($e1:expr) => ({
+        let a = $e1 + 2;
+
+        zzz();
+        sentinel();
+
+        let a = $e1 + 10;
+
+        zzz();
+        sentinel();
+    })
+)
+
+
+macro_rules! dup_expr(
+    ($e1:expr) => (($e1) + ($e1))
+)
+
+
+fn main() {
+
+    let a = trivial!(10);
+    let b = no_new_scope!(33);
+
+    zzz();
+    sentinel();
+
+    new_scope!();
+
+    zzz();
+    sentinel();
+
+    shadow_within_macro!(100);
+
+    zzz();
+    sentinel();
+
+    let c = dup_expr!(10 * 20);
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/lexical-scopes-in-block-expression.rs b/src/test/debug-info/lexical-scopes-in-block-expression.rs
new file mode 100644
index 00000000000..03de640053b
--- /dev/null
+++ b/src/test/debug-info/lexical-scopes-in-block-expression.rs
@@ -0,0 +1,344 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// STRUCT EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$1 = -1
+// debugger:print ten
+// check:$2 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$3 = 11
+// debugger:print ten
+// check:$4 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$5 = -1
+// debugger:print ten
+// check:$6 = 10
+// debugger:continue
+
+// FUNCTION CALL
+// debugger:finish
+// debugger:print val
+// check:$7 = -1
+// debugger:print ten
+// check:$8 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$9 = 12
+// debugger:print ten
+// check:$10 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$11 = -1
+// debugger:print ten
+// check:$12 = 10
+// debugger:continue
+
+// TUPLE EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$13 = -1
+// debugger:print ten
+// check:$14 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$15 = 13
+// debugger:print ten
+// check:$16 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$17 = -1
+// debugger:print ten
+// check:$18 = 10
+// debugger:continue
+
+// VEC EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$19 = -1
+// debugger:print ten
+// check:$20 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$21 = 14
+// debugger:print ten
+// check:$22 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$23 = -1
+// debugger:print ten
+// check:$24 = 10
+// debugger:continue
+
+// REPEAT VEC EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$25 = -1
+// debugger:print ten
+// check:$26 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$27 = 15
+// debugger:print ten
+// check:$28 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$29 = -1
+// debugger:print ten
+// check:$30 = 10
+// debugger:continue
+
+// ASSIGNMENT EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$31 = -1
+// debugger:print ten
+// check:$32 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$33 = 16
+// debugger:print ten
+// check:$34 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$35 = -1
+// debugger:print ten
+// check:$36 = 10
+// debugger:continue
+
+
+// ARITHMETIC EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$37 = -1
+// debugger:print ten
+// check:$38 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$39 = 17
+// debugger:print ten
+// check:$40 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$41 = -1
+// debugger:print ten
+// check:$42 = 10
+// debugger:continue
+
+// INDEX EXPRESSION
+// debugger:finish
+// debugger:print val
+// check:$43 = -1
+// debugger:print ten
+// check:$44 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$45 = 18
+// debugger:print ten
+// check:$46 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print val
+// check:$47 = -1
+// debugger:print ten
+// check:$48 = 10
+// debugger:continue
+
+struct Point {
+    x: int,
+    y: int
+}
+
+fn a_function(x: int) -> int {
+    x + 1
+}
+
+fn main() {
+
+    let val = -1;
+    let ten = 10;
+
+    // surrounded by struct expression
+    let point = Point {
+        x: {
+            zzz();
+            sentinel();
+
+            let val = ten + 1;
+
+            zzz();
+            sentinel();
+
+            val
+        },
+        y: 10
+    };
+
+    zzz();
+    sentinel();
+
+    // surrounded by function call
+    let _ = a_function({
+        zzz();
+        sentinel();
+
+        let val = ten + 2;
+
+        zzz();
+        sentinel();
+
+        val
+    });
+
+    zzz();
+    sentinel();
+
+
+    // surrounded by tup
+    let _ = ({
+        zzz();
+        sentinel();
+
+        let val = ten + 3;
+
+        zzz();
+        sentinel();
+
+        val
+    }, 0);
+
+    zzz();
+    sentinel();
+
+    // surrounded by vec
+    let _ = [{
+        zzz();
+        sentinel();
+
+        let val = ten + 4;
+
+        zzz();
+        sentinel();
+
+        val
+    }, 0, 0];
+
+    zzz();
+    sentinel();
+
+    // surrounded by repeat vec
+    let _ = [{
+        zzz();
+        sentinel();
+
+        let val = ten + 5;
+
+        zzz();
+        sentinel();
+
+        val
+    }, ..10];
+
+    zzz();
+    sentinel();
+
+    // assignment expression
+    let mut var = 0;
+    var = {
+        zzz();
+        sentinel();
+
+        let val = ten + 6;
+
+        zzz();
+        sentinel();
+
+        val
+    };
+
+    zzz();
+    sentinel();
+
+    // arithmetic expression
+    var = 10 + -{
+        zzz();
+        sentinel();
+
+        let val = ten + 7;
+
+        zzz();
+        sentinel();
+
+        val
+    } * 5;
+
+    zzz();
+    sentinel();
+
+    // index expression
+    let a_vector = [10, ..20];
+    let _ = a_vector[{
+        zzz();
+        sentinel();
+
+        let val = ten + 8;
+
+        zzz();
+        sentinel();
+
+        val
+    }];
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/name-shadowing-and-scope-nesting.rs b/src/test/debug-info/name-shadowing-and-scope-nesting.rs
new file mode 100644
index 00000000000..ba8c08e46fd
--- /dev/null
+++ b/src/test/debug-info/name-shadowing-and-scope-nesting.rs
@@ -0,0 +1,95 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// debugger:finish
+// debugger:print x
+// check:$1 = false
+// debugger:print y
+// check:$2 = true
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$3 = 10
+// debugger:print y
+// check:$4 = true
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$5 = 10.5
+// debugger:print y
+// check:$6 = 20
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$7 = true
+// debugger:print y
+// check:$8 = 2220
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$9 = 203203.5
+// debugger:print y
+// check:$10 = 2220
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$11 = 10.5
+// debugger:print y
+// check:$12 = 20
+// debugger:continue
+
+fn main() {
+    let x = false;
+    let y = true;
+
+    zzz();
+    sentinel();
+
+    let x = 10;
+
+    zzz();
+    sentinel();
+
+    let x = 10.5;
+    let y = 20;
+
+    zzz();
+    sentinel();
+
+    {
+        let x = true;
+        let y = 2220;
+
+        zzz();
+        sentinel();
+
+        let x = 203203.5;
+
+        zzz();
+        sentinel();
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/variable-scope.rs b/src/test/debug-info/shadowed-variable.rs
similarity index 74%
rename from src/test/debug-info/variable-scope.rs
rename to src/test/debug-info/shadowed-variable.rs
index dd3a1671b78..a532f7fbc2e 100644
--- a/src/test/debug-info/variable-scope.rs
+++ b/src/test/debug-info/shadowed-variable.rs
@@ -8,42 +8,51 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-test
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
 
 // compile-flags:-Z extra-debug-info
 // debugger:break zzz
 // debugger:run
+
 // debugger:finish
 // debugger:print x
 // check:$1 = false
 // debugger:print y
 // check:$2 = true
-
 // debugger:continue
+
 // debugger:finish
 // debugger:print x
 // check:$3 = 10
-
+// debugger:print y
+// check:$4 = true
 // debugger:continue
+
 // debugger:finish
 // debugger:print x
-// check:$4 = false
+// check:$5 = 10.5
 // debugger:print y
-// check:$5 = 11
+// check:$6 = 20
+// debugger:continue
 
 fn main() {
     let x = false;
     let y = true;
 
     zzz();
+    sentinel();
 
-    {
-        let x = 10;
-        zzz();
-    }
+    let x = 10;
 
-    let y = 11;
     zzz();
+    sentinel();
+
+    let x = 10.5;
+    let y = 20;
+
+    zzz();
+    sentinel();
 }
 
 fn zzz() {()}
+fn sentinel() {()}
diff --git a/src/test/debug-info/simple-lexical-scope.rs b/src/test/debug-info/simple-lexical-scope.rs
new file mode 100644
index 00000000000..96a394b5ed7
--- /dev/null
+++ b/src/test/debug-info/simple-lexical-scope.rs
@@ -0,0 +1,87 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// debugger:finish
+// debugger:print x
+// check:$1 = false
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$2 = false
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$3 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$4 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$5 = 10.5
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$6 = 10
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$7 = false
+// debugger:continue
+
+
+fn main() {
+    let x = false;
+
+    zzz();
+    sentinel();
+
+    {
+        zzz();
+        sentinel();
+
+        let x = 10;
+
+        zzz();
+        sentinel();
+
+        {
+            zzz();
+            sentinel();
+
+            let x = 10.5;
+
+            zzz();
+            sentinel();
+        }
+
+        zzz();
+        sentinel();
+    }
+
+    zzz();
+    sentinel();
+}
+
+fn zzz() {()}
+fn sentinel() {()}