about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Woerister <michaelwoerister@gmail>2013-08-23 18:45:02 +0200
committerMichael Woerister <michaelwoerister@gmail>2013-09-04 18:38:46 +0200
commitb81ea86530dfd9dff69815b099ba10be274830ea (patch)
tree157b642e9a6d196adf91f9bc3358d514193e6c8e /src
parent67555d9bd40a36d93e193fe2d178713481ad445e (diff)
downloadrust-b81ea86530dfd9dff69815b099ba10be274830ea.tar.gz
rust-b81ea86530dfd9dff69815b099ba10be274830ea.zip
debuginfo: Support for variables captured in closures and closure type descriptions.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/lib/llvm.rs19
-rw-r--r--src/librustc/middle/moves.rs2
-rw-r--r--src/librustc/middle/trans/base.rs28
-rw-r--r--src/librustc/middle/trans/closure.rs6
-rw-r--r--src/librustc/middle/trans/common.rs2
-rw-r--r--src/librustc/middle/trans/debuginfo.rs475
-rw-r--r--src/libsyntax/ast_map.rs8
-rw-r--r--src/rustllvm/RustWrapper.cpp36
-rw-r--r--src/test/debug-info/var-captured-in-managed-closure.rs58
-rw-r--r--src/test/debug-info/var-captured-in-sendable-closure.rs56
-rw-r--r--src/test/debug-info/var-captured-in-stack-closure.rs61
11 files changed, 559 insertions, 192 deletions
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index c9253026488..7903e6f02fa 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -2084,6 +2084,25 @@ pub mod llvm {
                                                         ColumnNo: c_uint)
                                                         -> ValueRef;
 
+        #[fast_ffi]
+        pub fn LLVMDIBuilderCreateOpDeref(IntType: TypeRef) -> ValueRef;
+
+        #[fast_ffi]
+        pub fn LLVMDIBuilderCreateOpPlus(IntType: TypeRef) -> ValueRef;
+
+        #[fast_ffi]
+        pub fn LLVMDIBuilderCreateComplexVariable(Builder: DIBuilderRef,
+            Tag: c_uint,
+            Scope: ValueRef,
+            Name: *c_char,
+            File: ValueRef,
+            LineNo: c_uint,
+            Ty: ValueRef,
+            AddrOps: *ValueRef,
+            AddrOpsCount: c_uint,
+            ArgNo: c_uint)
+            -> ValueRef;
+
         pub fn LLVMInitializeX86TargetInfo();
         pub fn LLVMInitializeX86Target();
         pub fn LLVMInitializeX86TargetMC();
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
index ba05ab1f3c3..cb672947774 100644
--- a/src/librustc/middle/moves.rs
+++ b/src/librustc/middle/moves.rs
@@ -143,7 +143,7 @@ use syntax::visit;
 use syntax::visit::Visitor;
 use syntax::codemap::Span;
 
-#[deriving(Encodable, Decodable)]
+#[deriving(Eq, Encodable, Decodable)]
 pub enum CaptureMode {
     CapCopy, // Copy the value into the closure.
     CapMove, // Move the value into the closure.
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 2ed9af42248..92aedbdef84 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -131,20 +131,6 @@ pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
     _InsnCtxt { _x: () }
 }
 
-fn fcx_has_nonzero_span(fcx: &FunctionContext) -> bool {
-    match fcx.span {
-        None => false,
-        Some(span) => *span.lo != 0 || *span.hi != 0
-    }
-}
-
-fn span_is_empty(opt_span: &Option<Span>) -> bool {
-    match *opt_span {
-        None => true,
-        Some(span) => *span.lo == 0 && *span.hi == 0
-    }
-}
-
 struct StatRecorder<'self> {
     ccx: @mut CrateContext,
     name: &'self str,
@@ -1132,8 +1118,7 @@ pub fn trans_stmt(cx: @mut Block, s: &ast::Stmt) -> @mut Block {
             match d.node {
                 ast::DeclLocal(ref local) => {
                     bcx = init_local(bcx, *local);
-                    if cx.sess().opts.extra_debuginfo
-                        && fcx_has_nonzero_span(bcx.fcx) {
+                    if cx.sess().opts.extra_debuginfo {
                         debuginfo::create_local_var_metadata(bcx, *local);
                     }
                 }
@@ -1633,12 +1618,7 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
         }
     };
     let uses_outptr = type_of::return_uses_outptr(ccx.tcx, substd_output_type);
-
-    let debug_context = if id != -1 && ccx.sess.opts.debuginfo && !span_is_empty(&sp) {
-        Some(debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl))
-    } else {
-        None
-    };
+    let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
 
     let fcx = @mut FunctionContext {
           llfn: llfndecl,
@@ -1784,7 +1764,7 @@ pub fn copy_args_to_allocas(fcx: @mut FunctionContext,
             fcx.llself = Some(ValSelfData {v: self_val, ..slf});
             add_clean(bcx, self_val, slf.t);
 
-            if fcx.ccx.sess.opts.extra_debuginfo && fcx_has_nonzero_span(fcx) {
+            if fcx.ccx.sess.opts.extra_debuginfo {
                 debuginfo::create_self_argument_metadata(bcx, slf.t, self_val);
             }
         }
@@ -1811,7 +1791,7 @@ pub fn copy_args_to_allocas(fcx: @mut FunctionContext,
         };
         bcx = _match::store_arg(bcx, args[arg_n].pat, llarg);
 
-        if fcx.ccx.sess.opts.extra_debuginfo && fcx_has_nonzero_span(fcx) {
+        if fcx.ccx.sess.opts.extra_debuginfo {
             debuginfo::create_argument_metadata(bcx, &args[arg_n]);
         }
     }
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index 645f8157766..d25fedf2d8e 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -17,6 +17,7 @@ use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
 use middle::trans::datum::{Datum, INIT};
+use middle::trans::debuginfo;
 use middle::trans::expr;
 use middle::trans::glue;
 use middle::trans::type_of::*;
@@ -317,6 +318,11 @@ pub fn load_environment(fcx: @mut FunctionContext,
         }
         let def_id = ast_util::def_id_of_def(cap_var.def);
         fcx.llupvars.insert(def_id.node, upvarptr);
+
+        if fcx.ccx.sess.opts.extra_debuginfo {
+            debuginfo::create_captured_var_metadata(bcx, def_id.node, upvarptr, cap_var.span);
+        }
+
         i += 1u;
     }
 }
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index fb80cfe3485..d64f221cb9e 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -228,7 +228,7 @@ pub struct FunctionContext {
     ccx: @mut CrateContext,
 
     // Used and maintained by the debuginfo module.
-    debug_context: Option<~debuginfo::FunctionDebugContext>
+    debug_context: debuginfo::FunctionDebugContext,
 }
 
 impl FunctionContext {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index a9e3a869be0..049dee6bfea 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -72,7 +72,7 @@ use syntax::codemap::Span;
 use syntax::{ast, codemap, ast_util, ast_map, opt_vec};
 use syntax::parse::token::special_idents;
 
-static DW_LANG_RUST: int = 0x9000;
+static DW_LANG_RUST: c_uint = 0x9000;
 
 static DW_TAG_auto_variable: c_uint = 0x100;
 static DW_TAG_arg_variable: c_uint = 0x101;
@@ -118,11 +118,63 @@ impl DebugContext {
     }
 }
 
-pub struct FunctionDebugContext {
-    priv scope_map: HashMap<ast::NodeId, DIScope>,
-    priv fn_metadata: DISubprogram,
-    priv argument_counter: uint,
-    priv source_locations_enabled: bool,
+pub enum FunctionDebugContext {
+    priv FunctionDebugContext(~FunctionDebugContextData),
+    priv DebugInfoDisabled,
+    priv FunctionWithoutDebugInfo,
+}
+
+impl FunctionDebugContext {
+    fn get_ref<'a>(&'a self, cx: &CrateContext, span: span) -> &'a FunctionDebugContextData {
+        match *self {
+            FunctionDebugContext(~ref data) => data,
+            DebugInfoDisabled => {
+                cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
+                                        although debug info is disabled!");
+            }
+            FunctionWithoutDebugInfo => {
+                cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
+                                        for function that should be ignored by debug info!");
+            }
+        }
+    }
+
+    fn get_mut_ref<'a>(&'a mut self,
+                       cx: &CrateContext,
+                       span: span)
+                    -> &'a mut FunctionDebugContextData {
+        match *self {
+            FunctionDebugContext(~ref mut data) => data,
+            DebugInfoDisabled => {
+                cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
+                                        although debug info is disabled!");
+            }
+            FunctionWithoutDebugInfo => {
+                cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
+                                        for function that should be ignored by debug info!");
+            }
+        }
+    }
+}
+
+struct FunctionDebugContextData {
+    scope_map: HashMap<ast::NodeId, DIScope>,
+    fn_metadata: DISubprogram,
+    argument_counter: uint,
+    source_locations_enabled: bool,
+}
+
+enum VariableAccess {
+    // The value given is a pointer to data
+    DirectVariable,
+    // The value given has to be dereferenced once to get the pointer to data
+    IndirectVariable
+}
+
+enum VariableKind {
+    ArgumentVariable(uint /*index*/),
+    LocalVariable,
+    CapturedVariable,
 }
 
 /// Create any deferred debug metadata nodes
@@ -138,7 +190,12 @@ pub fn finalize(cx: @mut CrateContext) {
 /// Creates debug information for the given local variable.
 ///
 /// Adds the created metadata nodes directly to the crate's IR.
-pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) {
+pub fn create_local_var_metadata(bcx: @mut Block,
+                                 local: &ast::Local) {
+    if fn_should_be_ignored(bcx.fcx) {
+        return;
+    }
+
     let cx = bcx.ccx();
     let def_map = cx.tcx.def_map;
 
@@ -147,73 +204,143 @@ pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) {
         let var_ident = ast_util::path_to_ident(path_ref);
         let var_type = node_id_type(bcx, node_id);
 
-        declare_local(bcx, var_ident, node_id, var_type, span);
+        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));
+            }
+        };
+
+        let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
+
+        declare_local(bcx,
+                      llptr,
+                      var_ident,
+                      var_type,
+                      scope_metadata,
+                      DirectVariable,
+                      LocalVariable,
+                      span);
     }
 }
 
 /// Creates debug information for a local variable introduced in the head of a match-statement arm.
 ///
+// /// Adds the created metadata nodes directly to the crate's IR.
+pub fn create_captured_var_metadata(bcx: @mut Block,
+                                    node_id: ast::NodeId,
+                                    llptr: ValueRef,
+                                    span: span) {
+    if fn_should_be_ignored(bcx.fcx) {
+        return;
+    }
+
+    let cx = bcx.ccx();
+
+    let ast_item = cx.tcx.items.find_copy(&node_id);
+    let variable_ident = match ast_item {
+        None => {
+            cx.sess.span_bug(span, "debuginfo::create_captured_var_metadata() - NodeId not found");
+        }
+        Some(ast_map::node_local(ident)) => ident,
+        Some(ast_map::node_arg(@ast::pat { node: ast::pat_ident(_, ref path, _), _ })) => {
+            ast_util::path_to_ident(path)
+        }
+        _ => {
+            cx.sess.span_bug(span, fmt!("debuginfo::create_captured_var_metadata() - \
+                Captured var-id refers to unexpected ast_map variant: %?", ast_item));
+        }
+    };
+    let variable_type = node_id_type(bcx, node_id);
+    let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata;
+
+    declare_local(bcx,
+                  llptr,
+                  variable_ident,
+                  variable_type,
+                  scope_metadata,
+                  IndirectVariable,
+                  CapturedVariable,
+                  span);
+}
+
+/// Creates debug information for a local variable introduced in the head of a match-statement arm.
+///
 /// Adds the created metadata nodes directly to the crate's IR.
 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);
+                                     span: span) {
+    if fn_should_be_ignored(bcx.fcx) {
+        return;
+    }
+
+    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));
+        }
+    };
+
+    let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
+
+    declare_local(bcx,
+                  llptr,
+                  variable_ident,
+                  variable_type,
+                  scope_metadata,
+                  DirectVariable,
+                  LocalVariable,
+                  span);
 }
 
 /// Creates debug information for the self argument of a method.
 ///
 /// Adds the created metadata nodes directly to the crate's IR.
 pub fn create_self_argument_metadata(bcx: @mut Block,
-                                     variable_type: ty::t,
+                                     type_of_self: ty::t,
                                      llptr: ValueRef) {
-    assert_fcx_has_span(bcx.fcx);
-    let span = bcx.fcx.span.unwrap();
-
-    let cx = bcx.ccx();
+    if fn_should_be_ignored(bcx.fcx) {
+        return;
+    }
 
-    let filename = span_start(cx, span).file.name;
-    let file_metadata = file_metadata(cx, filename);
+    // Extract the span of the self argument from the method's AST
+    let fnitem = bcx.ccx().tcx.items.get_copy(&bcx.fcx.id);
+    let span = match fnitem {
+        ast_map::node_method(@ast::method { explicit_self: explicit_self, _ }, _, _) => {
+            explicit_self.span
+        }
+        ast_map::node_trait_method(
+            @ast::provided(
+                @ast::method {
+                    explicit_self: explicit_self,
+                    _
+                }),
+            _,
+            _) => {
+            explicit_self.span
+        }
+        _ => bcx.ccx().sess.bug(fmt!("create_self_argument_metadata: unexpected sort of node: %?", fnitem))
+    };
 
-    let loc = span_start(cx, span);
-    let type_metadata = type_metadata(cx, variable_type, span);
-    let scope = bcx.fcx.debug_context.get_ref().fn_metadata;
+    let scope_metadata = bcx.fcx.debug_context.get_ref(bcx.ccx(), span).fn_metadata;
 
     let argument_index = {
-        let counter = &mut bcx.fcx.debug_context.get_mut_ref().argument_counter;
+        let counter = &mut bcx.fcx.debug_context.get_mut_ref(bcx.ccx(), span).argument_counter;
         let argument_index = *counter;
         *counter += 1;
-        argument_index as c_uint
-    };
-
-    let var_metadata = do cx.sess.str_of(special_idents::self_).to_c_str().with_ref |name| {
-        unsafe {
-            llvm::LLVMDIBuilderCreateLocalVariable(
-                DIB(cx),
-                DW_TAG_arg_variable,
-                scope,
-                name,
-                file_metadata,
-                loc.line as c_uint,
-                type_metadata,
-                false,
-                0,
-                argument_index)
-        }
+        argument_index
     };
 
-    set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
-    unsafe {
-        let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
-            DIB(cx),
-            llptr,
-            var_metadata,
-            bcx.llbb);
-
-        llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
-    }
-    set_debug_location(cx, UnknownLocation);
+    declare_local(bcx,
+                  llptr,
+                  special_idents::self_,
+                  type_of_self,
+                  scope_metadata,
+                  DirectVariable,
+                  ArgumentVariable(argument_index),
+                  span);
 }
 
 /// Creates debug information for the given function argument.
@@ -221,54 +348,22 @@ pub fn create_self_argument_metadata(bcx: @mut Block,
 /// Adds the created metadata nodes directly to the crate's IR.
 pub fn create_argument_metadata(bcx: @mut Block,
                                 arg: &ast::arg) {
+    if fn_should_be_ignored(bcx.fcx) {
+        return;
+    }
+
     let fcx = bcx.fcx;
     let cx = fcx.ccx;
 
     let pattern = arg.pat;
     let filename = span_start(cx, pattern.span).file.name;
 
-    if fcx.id == -1 ||
-       fcx.span.is_none() ||
-       "<intrinsic>" == filename {
-        return;
-    }
-
     let def_map = cx.tcx.def_map;
     let file_metadata = file_metadata(cx, filename);
-    let scope = bcx.fcx.debug_context.get_ref().fn_metadata;//create_function_metadata(fcx);
+    let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata;
 
     do pat_util::pat_bindings(def_map, pattern) |_, node_id, span, path_ref| {
 
-        let ty = node_id_type(bcx, node_id);
-        let type_metadata = type_metadata(cx, ty, codemap::dummy_sp());
-        let loc = span_start(cx, span);
-        let ident = ast_util::path_to_ident(path_ref);
-        let name: &str = cx.sess.str_of(ident);
-        debug!("create_argument_metadata: %s", name);
-
-        let argument_index = {
-            let counter = &mut fcx.debug_context.get_mut_ref().argument_counter;
-            let argument_index = *counter;
-            *counter += 1;
-            argument_index as c_uint
-        };
-
-        let arg_metadata = do name.with_c_str |name| {
-            unsafe {
-                llvm::LLVMDIBuilderCreateLocalVariable(
-                    DIB(cx),
-                    DW_TAG_arg_variable,
-                    scope,
-                    name,
-                    file_metadata,
-                    loc.line as c_uint,
-                    type_metadata,
-                    false,
-                    0,
-                    argument_index)
-            }
-        };
-
         let llptr = match bcx.fcx.llargs.find_copy(&node_id) {
             Some(v) => v,
             None => {
@@ -276,17 +371,24 @@ pub fn create_argument_metadata(bcx: @mut Block,
             }
         };
 
-        set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
-        unsafe {
-            let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
-                DIB(cx),
-                llptr,
-                arg_metadata,
-                bcx.llbb);
+        let argument_type = node_id_type(bcx, node_id);
+        let argument_ident = ast_util::path_to_ident(path_ref);
 
-            llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
-        }
-        set_debug_location(cx, UnknownLocation);
+        let argument_index = {
+            let counter = &mut fcx.debug_context.get_mut_ref(cx, span).argument_counter;
+            let argument_index = *counter;
+            *counter += 1;
+            argument_index
+        };
+
+        declare_local(bcx,
+                      llptr,
+                      argument_ident,
+                      argument_type,
+                      scope_metadata,
+                      DirectVariable,
+                      ArgumentVariable(argument_index),
+                      span);
     }
 }
 
@@ -296,16 +398,16 @@ pub fn create_argument_metadata(bcx: @mut Block,
 /// reliably find the correct visibility scope for the code position.
 pub fn set_source_location(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) {
+                           span: span) {
+    if fn_should_be_ignored(fcx) {
         return;
     }
 
+    let cx = fcx.ccx;
+
     debug!("set_source_location: %s", cx.sess.codemap.span_to_str(span));
 
-    if fcx.debug_context.get_ref().source_locations_enabled {
+    if fcx.debug_context.get_ref(cx, span).source_locations_enabled {
         let loc = span_start(cx, span);
         let scope = scope_metadata(fcx, node_id, span);
 
@@ -316,16 +418,23 @@ pub fn set_source_location(fcx: &FunctionContext,
 }
 
 pub fn start_emitting_source_locations(fcx: &mut FunctionContext) {
-    for debug_context in fcx.debug_context.mut_iter() {
-        debug_context.source_locations_enabled = true;
+    match fcx.debug_context {
+        FunctionDebugContext(~ref mut data) => data.source_locations_enabled = true,
+        _ => { /* safe to ignore */}
     }
 }
 
 pub fn create_function_debug_context(cx: &mut CrateContext,
                                      fn_ast_id: ast::NodeId,
                                      param_substs: Option<@param_substs>,
-                                     llfn: ValueRef) -> ~FunctionDebugContext {
-    assert!(fn_ast_id != -1);
+                                     llfn: ValueRef) -> FunctionDebugContext {
+    if !cx.sess.opts.debuginfo {
+        return DebugInfoDisabled;
+    }
+
+    if fn_ast_id == -1 {
+        return FunctionWithoutDebugInfo;
+    }
 
     let empty_generics = ast::Generics { lifetimes: opt_vec::Empty, ty_params: opt_vec::Empty };
 
@@ -395,9 +504,18 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
             _) => {
             (ident, fn_decl, generics, None, span)
         }
+        ast_map::node_variant(*)     |
+        ast_map::node_struct_ctor(*) => {
+            return FunctionWithoutDebugInfo;
+        }
         _ => cx.sess.bug(fmt!("create_function_debug_context: unexpected sort of node: %?", fnitem))
     };
 
+    // This can be the case for functions inlined from another crate
+    if span == codemap::dummy_sp() {
+        return FunctionWithoutDebugInfo;
+    }
+
     let loc = span_start(cx, span);
     let file_metadata = file_metadata(cx, loc.file.name);
 
@@ -438,7 +556,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
     };
 
     // Initialize fn debug context (including scope map)
-    let mut fn_debug_context = ~FunctionDebugContext {
+    let mut fn_debug_context = ~FunctionDebugContextData {
         scope_map: HashMap::new(),
         fn_metadata: fn_metadata,
         argument_counter: 1,
@@ -448,7 +566,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
     let arg_pats = do fn_decl.inputs.map |arg_ref| { arg_ref.pat };
     populate_scope_map(cx, arg_pats, top_level_block, fn_metadata, &mut fn_debug_context.scope_map);
 
-    return fn_debug_context;
+    return FunctionDebugContext(fn_debug_context);
 
     fn get_function_signature(cx: &mut CrateContext,
                               fn_ast_id: ast::NodeId,
@@ -631,19 +749,28 @@ fn compile_unit_metadata(cx: @mut CrateContext) {
     do "".with_c_str |flags| {
     do "".with_c_str |split_name| {
         unsafe {
-            llvm::LLVMDIBuilderCreateCompileUnit(dcx.builder,
-                DW_LANG_RUST as c_uint, crate_name, work_dir, producer,
+            llvm::LLVMDIBuilderCreateCompileUnit(
+                dcx.builder,
+                DW_LANG_RUST,
+                crate_name,
+                work_dir,
+                producer,
                 cx.sess.opts.optimize != session::No,
-                flags, 0, split_name);
+                flags,
+                0,
+                split_name);
         }
     }}}}};
 }
 
 fn declare_local(bcx: @mut Block,
-                 variable_ident: ast::Ident,
-                 node_id: ast::NodeId,
+                 llptr: ValueRef,
+                 variable_ident: ast::ident,
                  variable_type: ty::t,
-                 span: Span) {
+                 scope_metadata: DIScope,
+                 variable_access: VariableAccess,
+                 variable_kind: VariableKind,
+                 span: span) {
     let cx: &mut CrateContext = bcx.ccx();
 
     let filename = span_start(cx, span).file.name;
@@ -652,32 +779,48 @@ fn declare_local(bcx: @mut Block,
     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 argument_index = match variable_kind {
+        ArgumentVariable(index) => index,
+        LocalVariable    |
+        CapturedVariable => 0
+    } as c_uint;
 
     let var_metadata = do name.with_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)
-        }
-    };
+        match variable_access {
+            DirectVariable => unsafe {
+                llvm::LLVMDIBuilderCreateLocalVariable(
+                    DIB(cx),
+                    DW_TAG_auto_variable,
+                    scope_metadata,
+                    name,
+                    file_metadata,
+                    loc.line as c_uint,
+                    type_metadata,
+                    cx.sess.opts.optimize != session::No,
+                    0,
+                    argument_index)
+            },
+            IndirectVariable => unsafe {
+                let address_op = llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref());
+                let address_op_count = 1;
 
-    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));
+                llvm::LLVMDIBuilderCreateComplexVariable(
+                    DIB(cx),
+                    DW_TAG_auto_variable,
+                    scope_metadata,
+                    name,
+                    file_metadata,
+                    loc.line as c_uint,
+                    type_metadata,
+                    ptr::to_unsafe_ptr(&address_op),
+                    address_op_count,
+                    argument_index)
+            }
         }
     };
 
-    set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
+    set_debug_location(cx, DebugLocation::new(scope_metadata, loc.line, *loc.col));
     unsafe {
         let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
             DIB(cx),
@@ -687,6 +830,14 @@ fn declare_local(bcx: @mut Block,
 
         llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
     }
+
+    match variable_kind {
+        ArgumentVariable(_) | CapturedVariable => {
+            assert!(!bcx.fcx.debug_context.get_ref(cx, span).source_locations_enabled);
+            set_debug_location(cx, UnknownLocation);
+        }
+        _ => { /* fallthrough */ }
+    }
 }
 
 fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile {
@@ -720,13 +871,9 @@ fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile {
 /// 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!");
-    }
-
-    let scope_map = &fcx.debug_context.get_ref().scope_map;
+                  span: span)
+               -> DIScope {
+    let scope_map = &fcx.debug_context.get_ref(fcx.ccx, span).scope_map;
 
     match scope_map.find_copy(&node_id) {
         Some(scope_metadata) => scope_metadata,
@@ -1260,30 +1407,31 @@ fn vec_slice_metadata(cx: &mut CrateContext,
     }
 }
 
-fn bare_fn_metadata(cx: &mut CrateContext,
-                    _fn_ty: ty::t,
-                    inputs: ~[ty::t],
-                    output: ty::t,
-                    span: Span)
-                 -> DICompositeType {
-
-    debug!("bare_fn_metadata: %?", ty::get(_fn_ty));
-
+fn subroutine_type_metadata(cx: &mut CrateContext,
+                            signature: &ty::FnSig,
+                            span: span)
+                         -> DICompositeType {
     let loc = span_start(cx, span);
     let file_metadata = file_metadata(cx, loc.file.name);
 
-    let nil_pointer_type_metadata = type_metadata(cx, ty::mk_nil_ptr(cx.tcx), span);
-    let output_metadata = type_metadata(cx, output, span);
-    let output_ptr_metadata = pointer_type_metadata(cx, output, output_metadata);
+    let mut signature_metadata: ~[DIType] = vec::with_capacity(signature.inputs.len() + 1);
+
+    // return type
+    signature_metadata.push(match ty::get(signature.output).sty {
+        ty::ty_nil => ptr::null(),
+        _ => type_metadata(cx, signature.output, span)
+    });
 
-    let inputs_vals = do inputs.map |arg| { type_metadata(cx, *arg, span) };
-    let members = ~[output_ptr_metadata, nil_pointer_type_metadata] + inputs_vals;
+    // regular arguments
+    for &argument_type in signature.inputs.iter() {
+        signature_metadata.push(type_metadata(cx, argument_type, span));
+    }
 
     return unsafe {
         llvm::LLVMDIBuilderCreateSubroutineType(
             DIB(cx),
             file_metadata,
-            create_DIArray(DIB(cx), members))
+            create_DIArray(DIB(cx), signature_metadata))
     };
 }
 
@@ -1407,13 +1555,10 @@ fn type_metadata(cx: &mut CrateContext,
             pointer_type_metadata(cx, t, pointee)
         },
         ty::ty_bare_fn(ref barefnty) => {
-            let inputs = barefnty.sig.inputs.map(|a| *a);
-            let output = barefnty.sig.output;
-            bare_fn_metadata(cx, t, inputs, output, span)
+            subroutine_type_metadata(cx, &barefnty.sig, span)
         },
-        ty::ty_closure(ref _closurety) => {
-            cx.sess.span_note(span, "debuginfo for closure NYI");
-            unimplemented_type_metadata(cx, t)
+        ty::ty_closure(ref closurety) => {
+            subroutine_type_metadata(cx, &closurety.sig, span)
         },
         ty::ty_trait(_did, ref _substs, ref _vstore, _, _bounds) => {
             cx.sess.span_note(span, "debuginfo for trait NYI");
@@ -1458,7 +1603,6 @@ fn set_debug_location(cx: &mut CrateContext, debug_location: DebugLocation) {
         return;
     }
 
-
     let metadata_node;
 
     match debug_location {
@@ -1524,6 +1668,13 @@ fn assert_fcx_has_span(fcx: &FunctionContext) {
     }
 }
 
+fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
+    match fcx.debug_context {
+        FunctionDebugContext(_) => false,
+        _ => true
+    }
+}
+
 // 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.
 //
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index e3023b919f8..518d0fb0c38 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -73,7 +73,7 @@ pub enum ast_node {
     node_variant(variant, @item, @path),
     node_expr(@Expr),
     node_stmt(@Stmt),
-    node_arg,
+    node_arg(@pat),
     node_local(Ident),
     node_block(Block),
     node_struct_ctor(@struct_def, @item, @path),
@@ -171,7 +171,7 @@ impl Ctx {
               sp: codemap::Span,
               id: NodeId) {
         for a in decl.inputs.iter() {
-            self.map.insert(a.id, node_arg);
+            self.map.insert(a.id, node_arg(a.pat));
         }
         visit::walk_fn(self, fk, decl, body, sp, id, ());
     }
@@ -487,8 +487,8 @@ pub fn node_id_to_str(map: map, id: NodeId, itr: @ident_interner) -> ~str {
         fmt!("stmt %s (id=%?)",
              pprust::stmt_to_str(stmt, itr), id)
       }
-      Some(&node_arg) => {
-        fmt!("arg (id=%?)", id)
+      Some(&node_arg(pat)) => {
+        fmt!("arg %s (id=%?)", pprust::pat_to_str(pat, itr), id)
       }
       Some(&node_local(ident)) => {
         fmt!("local (id=%?, name=%s)", id, itr.get(ident.name))
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 54af6fe7e73..376adf24e25 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -724,3 +724,39 @@ extern "C" LLVMValueRef LLVMDIBuilderCreateTemplateTypeParameter(
       LineNo,
       ColumnNo));
 }
+
+extern "C" LLVMValueRef LLVMDIBuilderCreateOpDeref(LLVMTypeRef IntTy)
+{
+    return LLVMConstInt(IntTy, DIBuilder::OpDeref, true);
+}
+
+extern "C" LLVMValueRef LLVMDIBuilderCreateOpPlus(LLVMTypeRef IntTy)
+{
+    return LLVMConstInt(IntTy, DIBuilder::OpPlus, true);
+}
+
+extern "C" LLVMValueRef LLVMDIBuilderCreateComplexVariable(
+    DIBuilderRef Builder,
+    unsigned Tag,
+    LLVMValueRef Scope,
+    const char *Name,
+    LLVMValueRef File,
+    unsigned LineNo,
+    LLVMValueRef Ty,
+    LLVMValueRef* AddrOps,
+    unsigned AddrOpsCount,
+    unsigned ArgNo)
+{
+    llvm::ArrayRef<llvm::Value*> addr_ops((llvm::Value**)AddrOps, AddrOpsCount);
+
+    return wrap(Builder->createComplexVariable(
+        Tag,
+        unwrapDI<DIDescriptor>(Scope),
+        Name,
+        unwrapDI<DIFile>(File),
+        LineNo,
+        unwrapDI<DIType>(Ty),
+        addr_ops,
+        ArgNo
+    ));
+}
diff --git a/src/test/debug-info/var-captured-in-managed-closure.rs b/src/test/debug-info/var-captured-in-managed-closure.rs
new file mode 100644
index 00000000000..37f5fef471b
--- /dev/null
+++ b/src/test/debug-info/var-captured-in-managed-closure.rs
@@ -0,0 +1,58 @@
+// 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 constant
+// check:$1 = 1
+// debugger:print a_struct
+// check:$2 = {a = -2, b = 3.5, c = 4}
+// debugger:print *owned
+// check:$3 = 5
+// debugger:print managed->val
+// check:$4 = 6
+
+#[allow(unused_variable)];
+
+struct Struct {
+    a: int,
+    b: float,
+    c: uint
+}
+
+fn main() {
+    let constant = 1;
+
+    let a_struct = Struct {
+        a: -2,
+        b: 3.5,
+        c: 4
+    };
+
+    let owned = ~5;
+    let managed = @6;
+
+    let closure: @fn() = || {
+        zzz();
+        do_something(&constant, &a_struct.a, owned, managed);
+    };
+
+    closure();
+}
+
+fn do_something(_: &int, _:&int, _:&int, _:&int) {
+}
+
+fn zzz() {()}
diff --git a/src/test/debug-info/var-captured-in-sendable-closure.rs b/src/test/debug-info/var-captured-in-sendable-closure.rs
new file mode 100644
index 00000000000..c4568bd592f
--- /dev/null
+++ b/src/test/debug-info/var-captured-in-sendable-closure.rs
@@ -0,0 +1,56 @@
+// 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 constant
+// check:$1 = 1
+// debugger:print a_struct
+// check:$2 = {a = -2, b = 3.5, c = 4}
+// debugger:print *owned
+// check:$3 = 5
+
+#[allow(unused_variable)];
+
+struct Struct {
+    a: int,
+    b: float,
+    c: uint
+}
+
+fn main() {
+    let constant = 1;
+
+    let a_struct = Struct {
+        a: -2,
+        b: 3.5,
+        c: 4
+    };
+
+    let owned = ~5;
+
+    let closure: ~fn() = || {
+        zzz();
+        do_something(&constant, &a_struct.a, owned);
+    };
+
+    closure();
+}
+
+fn do_something(_: &int, _:&int, _:&int) {
+
+}
+
+fn zzz() {()}
diff --git a/src/test/debug-info/var-captured-in-stack-closure.rs b/src/test/debug-info/var-captured-in-stack-closure.rs
new file mode 100644
index 00000000000..6694d5111a8
--- /dev/null
+++ b/src/test/debug-info/var-captured-in-stack-closure.rs
@@ -0,0 +1,61 @@
+// 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 variable
+// check:$1 = 1
+// debugger:print constant
+// check:$2 = 2
+// debugger:print a_struct
+// check:$3 = {a = -3, b = 4.5, c = 5}
+// debugger:print *struct_ref
+// check:$4 = {a = -3, b = 4.5, c = 5}
+// debugger:print *owned
+// check:$5 = 6
+// debugger:print managed->val
+// check:$6 = 7
+
+#[allow(unused_variable)];
+
+struct Struct {
+    a: int,
+    b: float,
+    c: uint
+}
+
+fn main() {
+    let mut variable = 1;
+    let constant = 2;
+
+    let a_struct = Struct {
+        a: -3,
+        b: 4.5,
+        c: 5
+    };
+
+    let struct_ref = &a_struct;
+    let owned = ~6;
+    let managed = @7;
+
+    let closure = || {
+        zzz();
+        variable = constant + a_struct.a + struct_ref.a + *owned + *managed;
+    };
+
+    closure();
+}
+
+fn zzz() {()}