about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-09-04 11:55:52 -0700
committerbors <bors@rust-lang.org>2013-09-04 11:55:52 -0700
commit45c3ca72bc19230f82775eb4228f1b3f178baade (patch)
treea2641a829e3ef6e0791425bcfa98199aafb8946c
parentd1f90556f20fb5508c4e859da7abf667a00967b4 (diff)
parent5b94ae93f3d00f5afe3f1d957aad76173ed7e705 (diff)
downloadrust-45c3ca72bc19230f82775eb4228f1b3f178baade.tar.gz
rust-45c3ca72bc19230f82775eb4228f1b3f178baade.zip
auto merge of #8855 : michaelwoerister/rust/captured_vars, r=jdm
This pull request includes
* support for variables captured in closures*,
* a fix for issue #8512: arguments of non-immediate type (structs, tuples, etc) passed by value can now be accessed correctly in GDB. (I managed to fix this by using `llvm::DIBuilder::createComplexVariable()`. ~~However, I am not sure if this relies on unstable implementation details of LLVM's debug info handling. I'll try to clarify this on the LLVM mailing list~~).
* simplification of the `debuginfo` module's public interface: the caller of functions like `create_local_var_metadata()` doesn't have to know and catch all cases when it mustn't call the function,
* a cleanup refactoring with unified handling for locals, [self] arguments, captured variables, and match bindings,
* and proper span information for self arguments.

\* However, see comment at https://github.com/michaelwoerister/rust/blob/1d916ace136a27e354d73d65f488603c65f65bd2/src/test/debug-info/var-captured-in-nested-closure.rs#L62 . This is the same problem as with the fix for issue #8512 above: We are probably using `llvm.dbg.declare` in an unsupported way that works today but might not work after the next LLVM update.

Cheers,
Michael

Fixes #8512
Fixes #1341 
-rw-r--r--src/librustc/lib/llvm.rs25
-rw-r--r--src/librustc/middle/moves.rs2
-rw-r--r--src/librustc/middle/trans/_match.rs27
-rw-r--r--src/librustc/middle/trans/base.rs28
-rw-r--r--src/librustc/middle/trans/closure.rs25
-rw-r--r--src/librustc/middle/trans/common.rs2
-rw-r--r--src/librustc/middle/trans/context.rs4
-rw-r--r--src/librustc/middle/trans/debuginfo.rs545
-rw-r--r--src/libsyntax/ast_map.rs8
-rw-r--r--src/rustllvm/RustWrapper.cpp36
-rw-r--r--src/rustllvm/rustllvm.def.in3
-rw-r--r--src/test/debug-info/by-value-non-immediate-argument.rs99
-rw-r--r--src/test/debug-info/by-value-self-argument-in-trait-impl.rs79
-rw-r--r--src/test/debug-info/var-captured-in-managed-closure.rs56
-rw-r--r--src/test/debug-info/var-captured-in-nested-closure.rs87
-rw-r--r--src/test/debug-info/var-captured-in-sendable-closure.rs (renamed from src/test/debug-info/by-value-struct-argument.rs)43
-rw-r--r--src/test/debug-info/var-captured-in-stack-closure.rs59
17 files changed, 896 insertions, 232 deletions
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index c9253026488..9e81ee5e64c 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -2084,6 +2084,31 @@ 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;
+
+        #[fast_ffi]
+        pub fn LLVMIsAArgument(value_ref: ValueRef) -> ValueRef;
+
+        #[fast_ffi]
+        pub fn LLVMIsAAllocaInst(value_ref: ValueRef) -> 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/_match.rs b/src/librustc/middle/trans/_match.rs
index ad0ab95ebf0..39e3e97b489 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -2000,19 +2000,20 @@ pub fn store_arg(mut bcx: @mut Block,
     let arg_ty = node_id_type(bcx, pat.id);
     add_clean(bcx, llval, arg_ty);
 
-    match simple_identifier(pat) {
-        Some(_) => {
-            // Optimized path for `x: T` case. This just adopts
-            // `llval` wholesale as the pointer for `x`, avoiding the
-            // general logic which may copy out of `llval`.
-            bcx.fcx.llargs.insert(pat.id, llval);
-        }
-
-        None => {
-            // General path. Copy out the values that are used in the
-            // pattern.
-            bcx = bind_irrefutable_pat(bcx, pat, llval, BindArgument);
-        }
+    // Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an
+    // alloca, which only is the case on the general path, so lets disable the optimized path when
+    // debug info is enabled.
+    let fast_path = !bcx.ccx().sess.opts.extra_debuginfo && simple_identifier(pat).is_some();
+
+    if fast_path {
+        // Optimized path for `x: T` case. This just adopts
+        // `llval` wholesale as the pointer for `x`, avoiding the
+        // general logic which may copy out of `llval`.
+        bcx.fcx.llargs.insert(pat.id, llval);
+    } else {
+        // General path. Copy out the values that are used in the
+        // pattern.
+        bcx = bind_irrefutable_pat(bcx, pat, llval, BindArgument);
     }
 
     return bcx;
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..690d7343489 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::*;
@@ -307,7 +308,17 @@ pub fn load_environment(fcx: @mut FunctionContext,
     // Load a pointer to the closure data, skipping over the box header:
     let llcdata = opaque_box_body(bcx, cdata_ty, fcx.llenv);
 
-    // Populate the upvars from the environment.
+    // Store the pointer to closure data in an alloca for debug info because that's what the
+    // llvm.dbg.declare intrinsic expects
+    let env_pointer_alloca = if fcx.ccx.sess.opts.extra_debuginfo {
+        let alloc = alloc_ty(bcx, ty::mk_mut_ptr(bcx.tcx(), cdata_ty), "__debuginfo_env_ptr");
+        Store(bcx, llcdata, alloc);
+        Some(alloc)
+    } else {
+        None
+    };
+
+    // Populate the upvars from the environment
     let mut i = 0u;
     for cap_var in cap_vars.iter() {
         let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
@@ -317,6 +328,18 @@ 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);
+
+        for &env_pointer_alloca in env_pointer_alloca.iter() {
+            debuginfo::create_captured_var_metadata(
+                bcx,
+                def_id.node,
+                cdata_ty,
+                env_pointer_alloca,
+                i,
+                sigil,
+                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/context.rs b/src/librustc/middle/trans/context.rs
index e7781e93d8c..59159f61f48 100644
--- a/src/librustc/middle/trans/context.rs
+++ b/src/librustc/middle/trans/context.rs
@@ -111,7 +111,7 @@ pub struct CrateContext {
      // decl_gc_metadata knows whether to link to the module metadata, which
      // is not emitted by LLVM's GC pass when no functions use GC.
      uses_gc: bool,
-     dbg_cx: Option<debuginfo::DebugContext>,
+     dbg_cx: Option<debuginfo::CrateDebugContext>,
      do_not_commit_warning_issued: bool
 }
 
@@ -161,7 +161,7 @@ impl CrateContext {
 
             let crate_map = decl_crate_map(sess, link_meta, llmod);
             let dbg_cx = if sess.opts.debuginfo {
-                Some(debuginfo::DebugContext::new(llmod, name.to_owned()))
+                Some(debuginfo::CrateDebugContext::new(llmod, name.to_owned()))
             } else {
                 None
             };
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index a9e3a869be0..646f71ec28a 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -27,7 +27,7 @@ where possible. This will hopefully ease the adaption of this module to future L
 
 The public API of the module is a set of functions that will insert the correct metadata into the
 LLVM IR when called with the right parameters. The module is thus driven from an outside client with
-functions like `debuginfo::local_var_metadata(bcx: block, local: &ast::local)`.
+functions like `debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`.
 
 Internally the module will try to reuse already created metadata by utilizing a cache. The way to
 get a shared metadata node when needed is thus to just call the corresponding function in this
@@ -37,9 +37,8 @@ module:
 
 The function will take care of probing the cache for an existing node for that exact file path.
 
-All private state used by the module is stored within a DebugContext struct, which in turn is
-contained in the CrateContext.
-
+All private state used by the module is stored within either the CrateDebugContext struct (owned by
+the CrateContext) or the FunctionDebugContext (owned by the FunctionContext).
 
 This file consists of three conceptual sections:
 1. The public interface of the module
@@ -72,7 +71,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;
@@ -92,7 +91,7 @@ static DW_ATE_unsigned_char: c_uint = 0x08;
 //=-------------------------------------------------------------------------------------------------
 
 /// A context object for maintaining all state needed by the debuginfo module.
-pub struct DebugContext {
+pub struct CrateDebugContext {
     priv crate_file: ~str,
     priv llcontext: ContextRef,
     priv builder: DIBuilderRef,
@@ -101,13 +100,13 @@ pub struct DebugContext {
     priv created_types: HashMap<uint, DIType>,
 }
 
-impl DebugContext {
-    pub fn new(llmod: ModuleRef, crate: ~str) -> DebugContext {
-        debug!("DebugContext::new");
+impl CrateDebugContext {
+    pub fn new(llmod: ModuleRef, crate: ~str) -> CrateDebugContext {
+        debug!("CrateDebugContext::new");
         let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) };
         // DIBuilder inherits context from the module, so we'd better use the same one
         let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
-        return DebugContext {
+        return CrateDebugContext {
             crate_file: crate,
             llcontext: llcontext,
             builder: builder,
@@ -118,11 +117,64 @@ 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<'self> {
+    // The llptr given is an alloca containing the variable's value
+    DirectVariable { alloca: ValueRef },
+    // The llptr given is an alloca containing the start of some pointer chain leading to the
+    // variable's content.
+    IndirectVariable { alloca: ValueRef, address_operations: &'self [ValueRef] }
+}
+
+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,8 +204,90 @@ 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,
+                      var_ident,
+                      var_type,
+                      scope_metadata,
+                      DirectVariable { alloca: llptr },
+                      LocalVariable,
+                      span);
+    }
+}
+
+/// Creates debug information for a variable captured in a closure.
+///
+/// Adds the created metadata nodes directly to the crate's IR.
+pub fn create_captured_var_metadata(bcx: @mut Block,
+                                    node_id: ast::NodeId,
+                                    env_data_type: ty::t,
+                                    env_pointer: ValueRef,
+                                    env_index: uint,
+                                    closure_sigil: ast::Sigil,
+                                    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::PatIdent(_, 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;
+
+    let llvm_env_data_type = type_of::type_of(cx, env_data_type);
+    let byte_offset_of_var_in_env = machine::llelement_offset(cx, llvm_env_data_type, env_index);
+
+    let address_operations = unsafe {
+        [llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()),
+         llvm::LLVMDIBuilderCreateOpPlus(Type::i64().to_ref()),
+         C_i64(byte_offset_of_var_in_env as i64),
+         llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref())]
+    };
+
+    let address_op_count = match closure_sigil {
+        ast::BorrowedSigil => {
+            address_operations.len()
+        }
+        ast::ManagedSigil | ast::OwnedSigil => {
+            address_operations.len() - 1
+        }
+    };
+
+    let variable_access = IndirectVariable {
+        alloca: env_pointer,
+        address_operations: address_operations.slice_to(address_op_count)
+    };
+
+    declare_local(bcx,
+                  variable_ident,
+                  variable_type,
+                  scope_metadata,
+                  variable_access,
+                  CapturedVariable,
+                  span);
 }
 
 /// Creates debug information for a local variable introduced in the head of a match-statement arm.
@@ -159,61 +298,84 @@ pub fn create_match_binding_metadata(bcx: @mut Block,
                                      node_id: ast::NodeId,
                                      variable_type: ty::t,
                                      span: Span) {
-    declare_local(bcx, variable_ident, node_id, variable_type, 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,
+                  variable_ident,
+                  variable_type,
+                  scope_metadata,
+                  DirectVariable { alloca: llptr },
+                  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
+        argument_index
     };
 
-    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)
-        }
-    };
+    let address_operations = &[unsafe { llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()) }];
 
-    set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
-    unsafe {
-        let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
-            DIB(cx),
-            llptr,
-            var_metadata,
-            bcx.llbb);
+    let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
+        DirectVariable { alloca: llptr }
+    } else {
+        // This is not stable and may break with future LLVM versions. llptr should really always
+        // be an alloca. Anything else is not supported and just works by chance.
+        IndirectVariable { alloca: llptr, address_operations: address_operations }
+    };
 
-        llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
-    }
-    set_debug_location(cx, UnknownLocation);
+    declare_local(bcx,
+                  special_idents::self_,
+                  type_of_self,
+                  scope_metadata,
+                  variable_access,
+                  ArgumentVariable(argument_index),
+                  span);
 }
 
 /// Creates debug information for the given function argument.
@@ -221,53 +383,17 @@ 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) {
-    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 {
+    if fn_should_be_ignored(bcx.fcx) {
         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);
-
-    do pat_util::pat_bindings(def_map, pattern) |_, node_id, span, path_ref| {
+    let fcx = bcx.fcx;
+    let cx = fcx.ccx;
 
-        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 def_map = cx.tcx.def_map;
+    let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata;
 
-        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)
-            }
-        };
+    do pat_util::pat_bindings(def_map, arg.pat) |_, node_id, span, path_ref| {
 
         let llptr = match bcx.fcx.llargs.find_copy(&node_id) {
             Some(v) => v,
@@ -276,17 +402,28 @@ 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);
-
-            llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
+        if unsafe { llvm::LLVMIsAAllocaInst(llptr) } == ptr::null() {
+            cx.sess.span_bug(span, "debuginfo::create_argument_metadata() - \
+                                    Referenced variable location is not an alloca!");
         }
-        set_debug_location(cx, UnknownLocation);
+
+        let argument_type = node_id_type(bcx, node_id);
+        let argument_ident = ast_util::path_to_ident(path_ref);
+
+        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,
+                      argument_ident,
+                      argument_type,
+                      scope_metadata,
+                      DirectVariable { alloca: llptr },
+                      ArgumentVariable(argument_index),
+                      span);
     }
 }
 
@@ -297,15 +434,15 @@ pub fn create_argument_metadata(bcx: @mut Block,
 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) {
+    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);
 
@@ -315,17 +452,35 @@ pub fn set_source_location(fcx: &FunctionContext,
     }
 }
 
+/// Enables emitting source locations for the given functions.
+///
+/// Since we don't want source locations to be emitted for the function prelude, they are disabled
+/// when beginning to translate a new function. This functions switches source location emitting on
+/// and must therefore be called before the first real statement/expression of the function is
+/// translated.
 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 */ }
     }
 }
 
+/// Creates the function-specific debug context.
+///
+/// Returns the FunctionDebugContext for the function which holds state needed for debug info
+/// creation. The function may also return another variant of the FunctionDebugContext enum which
+/// indicates why no debuginfo should be created for the function.
 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 +550,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 +602,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 +612,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,18 +795,26 @@ 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,
                  variable_type: ty::t,
+                 scope_metadata: DIScope,
+                 variable_access: VariableAccess,
+                 variable_kind: VariableKind,
                  span: Span) {
     let cx: &mut CrateContext = bcx.ccx();
 
@@ -652,41 +824,68 @@ 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 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)
-        }
-    };
+    let argument_index = match variable_kind {
+        ArgumentVariable(index) => index,
+        LocalVariable    |
+        CapturedVariable => 0
+    } as c_uint;
 
-    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 (var_alloca, var_metadata) = do name.with_c_str |name| {
+        match variable_access {
+            DirectVariable { alloca } => (
+                alloca,
+                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 { alloca, address_operations } => (
+                alloca,
+                unsafe {
+                    llvm::LLVMDIBuilderCreateComplexVariable(
+                        DIB(cx),
+                        DW_TAG_auto_variable,
+                        scope_metadata,
+                        name,
+                        file_metadata,
+                        loc.line as c_uint,
+                        type_metadata,
+                        vec::raw::to_ptr(address_operations),
+                        address_operations.len() as c_uint,
+                        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),
-            llptr,
+            var_alloca,
             var_metadata,
             bcx.llbb);
 
         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 +919,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 +1455,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 +1603,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 +1651,6 @@ fn set_debug_location(cx: &mut CrateContext, debug_location: DebugLocation) {
         return;
     }
 
-
     let metadata_node;
 
     match debug_location {
@@ -1507,7 +1699,7 @@ fn bytes_to_bits(bytes: uint) -> c_ulonglong {
 }
 
 #[inline]
-fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut DebugContext {
+fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut CrateDebugContext {
     cx.dbg_cx.get_mut_ref()
 }
 
@@ -1524,6 +1716,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..be67998ac5d 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/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in
index e2cec6a04f3..79cfe8b2851 100644
--- a/src/rustllvm/rustllvm.def.in
+++ b/src/rustllvm/rustllvm.def.in
@@ -608,6 +608,9 @@ LLVMDIBuilderCreateEnumerator
 LLVMDIBuilderCreateEnumerationType
 LLVMDIBuilderCreateUnionType
 LLVMDIBuilderCreateTemplateTypeParameter
+LLVMDIBuilderCreateOpDeref
+LLVMDIBuilderCreateOpPlus
+LLVMDIBuilderCreateComplexVariable
 LLVMSetUnnamedAddr
 LLVMRustAddPass
 LLVMRustAddAnalysisPasses
diff --git a/src/test/debug-info/by-value-non-immediate-argument.rs b/src/test/debug-info/by-value-non-immediate-argument.rs
new file mode 100644
index 00000000000..da9c79a00ed
--- /dev/null
+++ b/src/test/debug-info/by-value-non-immediate-argument.rs
@@ -0,0 +1,99 @@
+// 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.
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// debugger:finish
+// debugger:print s
+// check:$1 = {a = 1, b = 2.5}
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$2 = {a = 3, b = 4.5}
+// debugger:print y
+// check:$3 = 5
+// debugger:print z
+// check:$4 = 6.5
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$5 = {7, 8, 9.5, 10.5}
+// debugger:continue
+
+// debugger:finish
+// debugger:print a
+// check:$6 = {11.5, 12.5, 13, 14}
+// debugger:continue
+
+// debugger:finish
+// debugger:print x
+// check:$7 = {{Case1, x = 0, y = 8970181431921507452}, {Case1, 0, 2088533116, 2088533116}}
+// debugger:continue
+
+#[deriving(Clone)]
+struct Struct {
+    a: int,
+    b: float
+}
+
+#[deriving(Clone)]
+struct StructStruct {
+    a: Struct,
+    b: Struct
+}
+
+fn fun(s: Struct) {
+    zzz();
+}
+
+fn fun_fun(StructStruct { a: x, b: Struct { a: y, b: z } }: StructStruct) {
+    zzz();
+}
+
+fn tup(a: (int, uint, float, float)) {
+    zzz();
+}
+
+struct Newtype(float, float, int, uint);
+
+fn new_type(a: Newtype) {
+    zzz();
+}
+
+// The first element is to ensure proper alignment, irrespective of the machines word size. Since
+// the size of the discriminant value is machine dependent, this has be taken into account when
+// datatype layout should be predictable as in this case.
+enum Enum {
+    Case1 { x: i64, y: i64 },
+    Case2 (i64, i32, i32),
+}
+
+fn by_val_enum(x: Enum) {
+    zzz();
+}
+
+fn main() {
+    fun(Struct { a: 1, b: 2.5 });
+    fun_fun(StructStruct { a: Struct { a: 3, b: 4.5 }, b: Struct { a: 5, b: 6.5 } });
+    tup((7, 8, 9.5, 10.5));
+    new_type(Newtype(11.5, 12.5, 13, 14));
+
+    // 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
+    // 0b01111100011111000111110001111100 = 2088533116
+    // 0b0111110001111100 = 31868
+    // 0b01111100 = 124
+    by_val_enum(Case1 { x: 0, y: 8970181431921507452 });
+}
+
+fn zzz() {()}
diff --git a/src/test/debug-info/by-value-self-argument-in-trait-impl.rs b/src/test/debug-info/by-value-self-argument-in-trait-impl.rs
new file mode 100644
index 00000000000..6e381c74a33
--- /dev/null
+++ b/src/test/debug-info/by-value-self-argument-in-trait-impl.rs
@@ -0,0 +1,79 @@
+// 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.
+
+// compile-flags:-Z extra-debug-info
+// debugger:break zzz
+// debugger:run
+
+// debugger:finish
+// debugger:print self
+// check:$1 = 1111
+// debugger:continue
+
+// debugger:finish
+// debugger:print self
+// check:$2 = {x = 2222, y = 3333}
+// debugger:continue
+
+// debugger:finish
+// debugger:print self
+// check:$3 = {4444.5, 5555, 6666, 7777.5}
+// debugger:continue
+
+// debugger:finish
+// debugger:print self->val
+// check:$4 = 8888
+// debugger:continue
+
+trait Trait {
+    fn method(self) -> Self;
+}
+
+impl Trait for int {
+    fn method(self) -> int {
+        zzz();
+        self
+    }
+}
+
+struct Struct {
+    x: uint,
+    y: uint,
+}
+
+impl Trait for Struct {
+    fn method(self) -> Struct {
+        zzz();
+        self
+    }
+}
+
+impl Trait for (float, int, int, float) {
+    fn method(self) -> (float, int, int, float) {
+        zzz();
+        self
+    }
+}
+
+impl Trait for @int {
+    fn method(self) -> @int {
+        zzz();
+        self
+    }
+}
+
+fn main() {
+    let _ = (1111 as int).method();
+    let _ = Struct { x: 2222, y: 3333 }.method();
+    let _ = (4444.5, 5555, 6666, 7777.5).method();
+    let _ = (@8888).method();
+}
+
+fn zzz() {()}
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..002bfbd2242
--- /dev/null
+++ b/src/test/debug-info/var-captured-in-managed-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.
+
+// 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-nested-closure.rs b/src/test/debug-info/var-captured-in-nested-closure.rs
new file mode 100644
index 00000000000..cd20209ddfd
--- /dev/null
+++ b/src/test/debug-info/var-captured-in-nested-closure.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.
+
+// 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
+// debugger:print closure_local
+// check:$7 = 8
+// debugger:continue
+
+// debugger:finish
+// debugger:print variable
+// check:$8 = 1
+// debugger:print constant
+// check:$9 = 2
+// debugger:print a_struct
+// check:$10 = {a = -3, b = 4.5, c = 5}
+// debugger:print *struct_ref
+// check:$11 = {a = -3, b = 4.5, c = 5}
+// debugger:print *owned
+// check:$12 = 6
+// debugger:print managed->val
+// check:$13 = 7
+// debugger:print closure_local
+// check:$14 = 8
+// debugger:continue
+
+#[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 = || {
+        let closure_local = 8;
+
+        let nested_closure = || {
+            zzz();
+            variable = constant + a_struct.a + struct_ref.a + *owned + *managed + closure_local;
+        };
+
+        zzz();
+
+        nested_closure();
+    };
+
+    closure();
+}
+
+fn zzz() {()}
diff --git a/src/test/debug-info/by-value-struct-argument.rs b/src/test/debug-info/var-captured-in-sendable-closure.rs
index 052b3c6994a..01839ea7835 100644
--- a/src/test/debug-info/by-value-struct-argument.rs
+++ b/src/test/debug-info/var-captured-in-sendable-closure.rs
@@ -8,30 +8,47 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Does not work yet, see issue #8512
-// xfail-test
-
 // compile-flags:-Z extra-debug-info
 // debugger:break zzz
 // debugger:run
-
 // debugger:finish
-// debugger:print s
-// check:$1 = {a = 1, b = 2.5}
-// debugger:continue
 
-#[deriving(Clone)]
+// 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
+    b: float,
+    c: uint
 }
 
-fn fun(s: Struct) {
-    zzz();
+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 main() {
-    fun(Struct { a: 1, b: 2.5 });
+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..3ce7d6fd89b
--- /dev/null
+++ b/src/test/debug-info/var-captured-in-stack-closure.rs
@@ -0,0 +1,59 @@
+// 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.
+
+// 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() {()}