about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-04-12 19:22:32 -0700
committerbors <bors@rust-lang.org>2016-04-12 19:22:32 -0700
commit7c27cce9e59bbbc1e556de257de85122dad88dcd (patch)
tree8408e6afd554330489cdbefa02c9b05019804632
parenta43eb4e774f6d51b7012bba3d25212819ab0e3dc (diff)
parent373b6ec935fb64767d03da9a79d4614ccbb3f084 (diff)
downloadrust-7c27cce9e59bbbc1e556de257de85122dad88dcd.tar.gz
rust-7c27cce9e59bbbc1e556de257de85122dad88dcd.zip
Auto merge of #32803 - eddyb:mir-debuginfo, r=nikomatsakis
Initial implementation of debuginfo in MIR trans.

Progress is made towards #31005, but several issues remain, such as #32790.
-rw-r--r--src/compiletest/runtest.rs18
-rw-r--r--src/librustc/mir/repr.rs1
-rw-r--r--src/librustc/mir/visit.rs2
-rw-r--r--src/librustc_mir/build/scope.rs2
-rw-r--r--src/librustc_mir/pretty.rs6
-rw-r--r--src/librustc_trans/base.rs76
-rw-r--r--src/librustc_trans/callee.rs4
-rw-r--r--src/librustc_trans/closure.rs8
-rw-r--r--src/librustc_trans/controlflow.rs8
-rw-r--r--src/librustc_trans/debuginfo/create_scope_map.rs81
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs38
-rw-r--r--src/librustc_trans/debuginfo/mod.rs373
-rw-r--r--src/librustc_trans/debuginfo/namespace.rs160
-rw-r--r--src/librustc_trans/debuginfo/source_loc.rs104
-rw-r--r--src/librustc_trans/debuginfo/utils.rs36
-rw-r--r--src/librustc_trans/expr.rs10
-rw-r--r--src/librustc_trans/glue.rs3
-rw-r--r--src/librustc_trans/intrinsic.rs10
-rw-r--r--src/librustc_trans/meth.rs3
-rw-r--r--src/librustc_trans/mir/block.rs33
-rw-r--r--src/librustc_trans/mir/mod.rs91
-rw-r--r--src/librustc_trans/mir/rvalue.rs14
-rw-r--r--src/librustc_trans/mir/statement.rs12
-rw-r--r--src/test/compile-fail/bad-intrinsic-monomorphization.rs5
-rw-r--r--src/test/compile-fail/non-interger-atomic.rs18
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs3
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-cast.rs3
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-comparison.rs3
-rw-r--r--src/test/compile-fail/simd-intrinsic-generic-elements.rs1
-rw-r--r--src/test/debuginfo/associated-types.rs3
-rw-r--r--src/test/debuginfo/c-style-enum.rs3
-rw-r--r--src/test/debuginfo/function-prologue-stepping-no-stack-check.rs13
-rw-r--r--src/test/debuginfo/no-debug-attribute.rs3
-rw-r--r--src/test/debuginfo/var-captured-in-nested-closure.rs11
-rw-r--r--src/test/debuginfo/var-captured-in-sendable-closure.rs11
-rw-r--r--src/test/debuginfo/var-captured-in-stack-closure.rs11
-rw-r--r--src/test/run-pass/backtrace-debuginfo-aux.rs2
-rw-r--r--src/test/run-pass/backtrace-debuginfo.rs6
-rw-r--r--src/test/run-pass/backtrace.rs6
39 files changed, 560 insertions, 635 deletions
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index 6773c34c7d7..75866e3cdaa 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -868,27 +868,11 @@ fn cleanup_debug_info_options(options: &Option<String>) -> Option<String> {
         "-g".to_owned(),
         "--debuginfo".to_owned()
     ];
-    let mut new_options =
+    let new_options =
         split_maybe_args(options).into_iter()
                                  .filter(|x| !options_to_remove.contains(x))
                                  .collect::<Vec<String>>();
 
-    let mut i = 0;
-    while i + 1 < new_options.len() {
-        if new_options[i] == "-Z" {
-            // FIXME #31005 MIR missing debuginfo currently.
-            if new_options[i + 1] == "orbit" {
-                // Remove "-Z" and "orbit".
-                new_options.remove(i);
-                new_options.remove(i);
-                continue;
-            }
-            // Always skip over -Z's argument.
-            i += 1;
-        }
-        i += 1;
-    }
-
     Some(new_options.join(" "))
 }
 
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 28437fa1336..aacb3aae81c 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -698,6 +698,7 @@ impl ScopeId {
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct ScopeData {
+    pub span: Span,
     pub parent_scope: Option<ScopeId>,
 }
 
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 450d25b6067..87f01391049 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -298,9 +298,11 @@ macro_rules! make_mir_visitor {
             fn super_scope_data(&mut self,
                                 scope_data: & $($mutability)* ScopeData) {
                 let ScopeData {
+                    ref $($mutability)* span,
                     ref $($mutability)* parent_scope,
                 } = *scope_data;
 
+                self.visit_span(span);
                 if let Some(ref $($mutability)* parent_scope) = *parent_scope {
                     self.visit_scope_id(parent_scope);
                 }
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index bda9cf058f6..84fda62067d 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -255,7 +255,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         debug!("push_scope({:?})", extent);
         let parent_id = self.scopes.last().map(|s| s.id);
         let id = ScopeId::new(self.scope_datas.len());
+        let tcx = self.hir.tcx();
         self.scope_datas.push(ScopeData {
+            span: extent.span(&tcx.region_maps, &tcx.map).unwrap_or(DUMMY_SP),
             parent_scope: parent_id,
         });
         self.scopes.push(Scope {
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
index d0c66b340d9..e9c9edd1183 100644
--- a/src/librustc_mir/pretty.rs
+++ b/src/librustc_mir/pretty.rs
@@ -242,7 +242,11 @@ fn write_mir_intro(tcx: &TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write)
         if var.mutability == Mutability::Mut {
             write!(w, "mut ")?;
         }
-        writeln!(w, "{:?}: {}; // {}", Lvalue::Var(i as u32), var.ty, var.name)?;
+        writeln!(w, "{:?}: {}; // {} in {}",
+                 Lvalue::Var(i as u32),
+                 var.ty,
+                 var.name,
+                 comment(tcx, var.scope, var.span))?;
     }
 
     // Compiler-introduced temporary types.
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 17230eff6e6..464e5c0cf1c 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -1276,7 +1276,7 @@ pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef {
             return llvm::LLVMGetUndef(ty.ptr_to().to_ref());
         }
     }
-    debuginfo::clear_source_location(cx.fcx);
+    DebugLoc::None.apply(cx.fcx);
     Alloca(cx, ty, name)
 }
 
@@ -1400,23 +1400,23 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
     pub fn new(ccx: &'blk CrateContext<'blk, 'tcx>,
                llfndecl: ValueRef,
                fn_ty: FnType,
-               def_id: Option<DefId>,
-               param_substs: &'tcx Substs<'tcx>,
+               definition: Option<(Instance<'tcx>, &ty::FnSig<'tcx>, Abi)>,
                block_arena: &'blk TypedArena<common::BlockS<'blk, 'tcx>>)
                -> FunctionContext<'blk, 'tcx> {
-        common::validate_substs(param_substs);
+        let (param_substs, def_id) = match definition {
+            Some((instance, _, _)) => {
+                common::validate_substs(instance.substs);
+                (instance.substs, Some(instance.def))
+            }
+            None => (ccx.tcx().mk_substs(Substs::empty()), None)
+        };
 
         let inlined_did = def_id.and_then(|def_id| inline::get_local_instance(ccx, def_id));
         let inlined_id = inlined_did.and_then(|id| ccx.tcx().map.as_local_node_id(id));
         let local_id = def_id.and_then(|id| ccx.tcx().map.as_local_node_id(id));
 
-        debug!("FunctionContext::new(path={}, def_id={:?}, param_substs={:?})",
-            inlined_id.map_or(String::new(), |id| ccx.tcx().node_path_str(id)),
-            def_id,
-            param_substs);
-
-        let debug_context = debuginfo::create_function_debug_context(ccx,
-            inlined_id.unwrap_or(ast::DUMMY_NODE_ID), param_substs, llfndecl);
+        debug!("FunctionContext::new({})",
+               definition.map_or(String::new(), |d| d.0.to_string()));
 
         let cfg = inlined_id.map(|id| build_cfg(ccx.tcx(), id));
         let nested_returns = if let Some((blk_id, Some(ref cfg))) = cfg {
@@ -1428,10 +1428,11 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
         let check_attrs = |attrs: &[ast::Attribute]| {
             let default_to_mir = ccx.sess().opts.debugging_opts.orbit;
             let invert = if default_to_mir { "rustc_no_mir" } else { "rustc_mir" };
-            default_to_mir ^ attrs.iter().any(|item| item.check_name(invert))
+            (default_to_mir ^ attrs.iter().any(|item| item.check_name(invert)),
+             attrs.iter().any(|item| item.check_name("no_debug")))
         };
 
-        let use_mir = if let Some(id) = local_id {
+        let (use_mir, no_debug) = if let Some(id) = local_id {
             check_attrs(ccx.tcx().map.attrs(id))
         } else if let Some(def_id) = def_id {
             check_attrs(&ccx.sess().cstore.item_attrs(def_id))
@@ -1445,6 +1446,13 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
             None
         };
 
+        let debug_context = if let (false, Some(definition)) = (no_debug, definition) {
+            let (instance, sig, abi) = definition;
+            debuginfo::create_function_debug_context(ccx, instance, sig, abi, llfndecl)
+        } else {
+            debuginfo::empty_function_debug_context(ccx)
+        };
+
         FunctionContext {
             needs_ret_allocas: nested_returns && mir.is_none(),
             mir: mir,
@@ -1731,7 +1739,7 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
 
         self.build_return_block(ret_cx, ret_debug_loc);
 
-        debuginfo::clear_source_location(self);
+        DebugLoc::None.apply(self);
         self.cleanup();
     }
 
@@ -1810,32 +1818,34 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                decl: &hir::FnDecl,
                                body: &hir::Block,
                                llfndecl: ValueRef,
-                               param_substs: &'tcx Substs<'tcx>,
-                               def_id: DefId,
+                               instance: Instance<'tcx>,
                                inlined_id: ast::NodeId,
-                               fn_ty: FnType,
+                               sig: &ty::FnSig<'tcx>,
                                abi: Abi,
                                closure_env: closure::ClosureEnv) {
     ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
 
     if collector::collecting_debug_information(ccx) {
-        ccx.record_translation_item_as_generated(
-            TransItem::Fn(Instance::new(def_id, param_substs)));
+        ccx.record_translation_item_as_generated(TransItem::Fn(instance));
     }
 
     let _icx = push_ctxt("trans_closure");
     attributes::emit_uwtable(llfndecl, true);
 
-    debug!("trans_closure(..., param_substs={:?})", param_substs);
+    debug!("trans_closure(..., {})", instance);
+
+    let fn_ty = FnType::new(ccx, abi, sig, &[]);
 
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfndecl, fn_ty, Some(def_id), param_substs, &arena);
+    fcx = FunctionContext::new(ccx, llfndecl, fn_ty, Some((instance, sig, abi)), &arena);
 
     if fcx.mir.is_some() {
         return mir::trans_mir(&fcx);
     }
 
+    debuginfo::fill_scope_map_for_function(&fcx, decl, body, inlined_id);
+
     // cleanup scope for the incoming arguments
     let fn_cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(
         ccx, inlined_id, body.span, true);
@@ -1890,10 +1900,8 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
     }
 
-    let ret_debug_loc = DebugLoc::At(fn_cleanup_debug_loc.id, fn_cleanup_debug_loc.span);
-
     // Insert the mandatory first few basic blocks before lltop.
-    fcx.finish(bcx, ret_debug_loc);
+    fcx.finish(bcx, fn_cleanup_debug_loc.debug_loc());
 }
 
 /// Creates an LLVM function corresponding to a source language function.
@@ -1906,25 +1914,23 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let _s = StatRecorder::new(ccx, ccx.tcx().node_path_str(id));
     debug!("trans_fn(param_substs={:?})", param_substs);
     let _icx = push_ctxt("trans_fn");
-    let fn_ty = ccx.tcx().node_id_to_type(id);
-    let fn_ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fn_ty);
-    let sig = ccx.tcx().erase_late_bound_regions(fn_ty.fn_sig());
-    let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
-    let abi = fn_ty.fn_abi();
-    let fn_ty = FnType::new(ccx, abi, &sig, &[]);
     let def_id = if let Some(&def_id) = ccx.external_srcs().borrow().get(&id) {
         def_id
     } else {
         ccx.tcx().map.local_def_id(id)
     };
+    let fn_ty = ccx.tcx().lookup_item_type(def_id).ty;
+    let fn_ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, &fn_ty);
+    let sig = ccx.tcx().erase_late_bound_regions(fn_ty.fn_sig());
+    let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
+    let abi = fn_ty.fn_abi();
     trans_closure(ccx,
                   decl,
                   body,
                   llfndecl,
-                  param_substs,
-                  def_id,
+                  Instance::new(def_id, param_substs),
                   id,
-                  fn_ty,
+                  &sig,
                   abi,
                   closure::ClosureEnv::NotClosure);
 }
@@ -2015,9 +2021,7 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfndecl, fn_ty,
-                               Some(ccx.tcx().map.local_def_id(ctor_id)),
-                               param_substs, &arena);
+    fcx = FunctionContext::new(ccx, llfndecl, fn_ty, None, &arena);
     let bcx = fcx.init(false, None);
 
     assert!(!fcx.needs_ret_allocas);
diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs
index 392c40a6015..7675e1de958 100644
--- a/src/librustc_trans/callee.rs
+++ b/src/librustc_trans/callee.rs
@@ -24,7 +24,6 @@ use middle::cstore::LOCAL_CRATE;
 use rustc::hir::def_id::DefId;
 use rustc::infer;
 use rustc::ty::subst;
-use rustc::ty::subst::{Substs};
 use rustc::traits;
 use rustc::hir::map as hir_map;
 use abi::{Abi, FnType};
@@ -385,10 +384,9 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
 
     //
-    let empty_substs = tcx.mk_substs(Substs::empty());
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena);
     let mut bcx = fcx.init(false, None);
 
     let llargs = get_params(fcx.llfn);
diff --git a/src/librustc_trans/closure.rs b/src/librustc_trans/closure.rs
index 6bd5fd355a7..c2031638044 100644
--- a/src/librustc_trans/closure.rs
+++ b/src/librustc_trans/closure.rs
@@ -234,16 +234,14 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
         output: sig.output,
         variadic: false
     };
-    let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]);
 
     trans_closure(ccx,
                   decl,
                   body,
                   llfn,
-                  param_substs,
-                  closure_def_id,
+                  Instance::new(closure_def_id, param_substs),
                   id,
-                  fn_ty,
+                  &sig,
                   Abi::RustCall,
                   ClosureEnv::Closure(closure_def_id, id));
 
@@ -387,7 +385,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
 
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, substs.func_substs, &block_arena);
+    fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
     let mut bcx = fcx.init(false, None);
 
 
diff --git a/src/librustc_trans/controlflow.rs b/src/librustc_trans/controlflow.rs
index 58971dec8a9..f793f0a6d55 100644
--- a/src/librustc_trans/controlflow.rs
+++ b/src/librustc_trans/controlflow.rs
@@ -167,11 +167,11 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         if cv == 1 {
             // if true { .. } [else { .. }]
             bcx = trans_block(bcx, &thn, dest);
-            debuginfo::clear_source_location(bcx.fcx);
+            DebugLoc::None.apply(bcx.fcx);
         } else {
             if let Some(elexpr) = els {
                 bcx = expr::trans_into(bcx, &elexpr, dest);
-                debuginfo::clear_source_location(bcx.fcx);
+                DebugLoc::None.apply(bcx.fcx);
             }
         }
 
@@ -181,7 +181,7 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let name = format!("then-block-{}-", thn.id);
     let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id);
     let then_bcx_out = trans_block(then_bcx_in, &thn, dest);
-    debuginfo::clear_source_location(bcx.fcx);
+    DebugLoc::None.apply(bcx.fcx);
 
     let cond_source_loc = cond.debug_loc();
 
@@ -204,7 +204,7 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Clear the source location because it is still set to whatever has been translated
     // right before.
-    debuginfo::clear_source_location(next_bcx.fcx);
+    DebugLoc::None.apply(next_bcx.fcx);
 
     next_bcx
 }
diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs
index b1cfeb8125b..3eebd1f5ea2 100644
--- a/src/librustc_trans/debuginfo/create_scope_map.rs
+++ b/src/librustc_trans/debuginfo/create_scope_map.rs
@@ -8,19 +8,24 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use super::FunctionDebugContext;
 use super::metadata::file_metadata;
-use super::utils::DIB;
+use super::utils::{DIB, span_start};
 
 use llvm;
 use llvm::debuginfo::{DIScope, DISubprogram};
-use common::CrateContext;
+use common::{CrateContext, FunctionContext};
 use rustc::hir::pat_util;
+use rustc::mir::repr::{Mir, ScopeId};
 use rustc::util::nodemap::NodeMap;
 
 use libc::c_uint;
+use std::ptr;
+
 use syntax::codemap::{Span, Pos};
 use syntax::{ast, codemap};
 
+use rustc_data_structures::bitvec::BitVector;
 use rustc::hir::{self, PatKind};
 
 // This procedure builds the *scope map* for a given function, which maps any
@@ -65,6 +70,74 @@ pub fn create_scope_map(cx: &CrateContext,
     return scope_map;
 }
 
+/// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
+/// If debuginfo is disabled, the returned vector is empty.
+pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec<DIScope> {
+    let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn");
+    let mut scopes = vec![ptr::null_mut(); mir.scopes.len()];
+
+    let fn_metadata = match fcx.debug_context {
+        FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata,
+        FunctionDebugContext::DebugInfoDisabled |
+        FunctionDebugContext::FunctionWithoutDebugInfo => {
+            return scopes;
+        }
+    };
+
+    // Find all the scopes with variables defined in them.
+    let mut has_variables = BitVector::new(mir.scopes.len());
+    for var in &mir.var_decls {
+        has_variables.insert(var.scope.index());
+    }
+
+    // Instantiate all scopes.
+    for idx in 0..mir.scopes.len() {
+        let scope = ScopeId::new(idx);
+        make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
+    }
+
+    scopes
+}
+
+fn make_mir_scope(ccx: &CrateContext,
+                  mir: &Mir,
+                  has_variables: &BitVector,
+                  fn_metadata: DISubprogram,
+                  scope: ScopeId,
+                  scopes: &mut [DIScope]) {
+    let idx = scope.index();
+    if !scopes[idx].is_null() {
+        return;
+    }
+
+    let scope_data = &mir.scopes[scope];
+    let parent_scope = if let Some(parent) = scope_data.parent_scope {
+        make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
+        scopes[parent.index()]
+    } else {
+        // The root is the function itself.
+        scopes[idx] = fn_metadata;
+        return;
+    };
+
+    scopes[idx] = if !has_variables.contains(idx) {
+        // Do not create a DIScope if there are no variables
+        // defined in this MIR Scope, to avoid debuginfo bloat.
+        parent_scope
+    } else {
+        let loc = span_start(ccx, scope_data.span);
+        let file_metadata = file_metadata(ccx, &loc.file.name);
+        unsafe {
+            llvm::LLVMDIBuilderCreateLexicalBlock(
+                DIB(ccx),
+                parent_scope,
+                file_metadata,
+                loc.line as c_uint,
+                loc.col.to_usize() as c_uint)
+        }
+    };
+}
+
 // local helper functions for walking the AST.
 fn with_new_scope<F>(cx: &CrateContext,
                      scope_span: Span,
@@ -74,7 +147,7 @@ fn with_new_scope<F>(cx: &CrateContext,
     F: FnOnce(&CrateContext, &mut Vec<ScopeStackEntry>, &mut NodeMap<DIScope>),
 {
     // Create a new lexical scope and push it onto the stack
-    let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo);
+    let loc = span_start(cx, scope_span);
     let file_metadata = file_metadata(cx, &loc.file.name);
     let parent_scope = scope_stack.last().unwrap().scope_metadata;
 
@@ -199,7 +272,7 @@ fn walk_pattern(cx: &CrateContext,
 
                 if need_new_scope {
                     // Create a new lexical scope and push it onto the stack
-                    let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo);
+                    let loc = span_start(cx, pat.span);
                     let file_metadata = file_metadata(cx, &loc.file.name);
                     let parent_scope = scope_stack.last().unwrap().scope_metadata;
 
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index 8471b6a274c..de403732269 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -16,7 +16,7 @@ use self::EnumDiscriminantInfo::*;
 use super::utils::{debug_context, DIB, span_start, bytes_to_bits, size_and_align_of,
                    get_namespace_and_span_for_item, create_DIArray,
                    fn_should_be_ignored, is_node_local_to_unit};
-use super::namespace::namespace_for_item;
+use super::namespace::mangled_name_of_item;
 use super::type_names::{compute_debuginfo_type_name, push_debuginfo_type_name};
 use super::{declare_local, VariableKind, VariableAccess};
 
@@ -68,8 +68,8 @@ pub const UNKNOWN_LINE_NUMBER: c_uint = 0;
 pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0;
 
 // ptr::null() doesn't work :(
-const NO_FILE_METADATA: DIFile = (0 as DIFile);
-const NO_SCOPE_METADATA: DIScope = (0 as DIScope);
+pub const NO_FILE_METADATA: DIFile = (0 as DIFile);
+pub const NO_SCOPE_METADATA: DIScope = (0 as DIScope);
 
 const FLAGS_NONE: c_uint = 0;
 
@@ -1846,28 +1846,8 @@ pub fn create_global_var_metadata(cx: &CrateContext,
         return;
     }
 
-    let var_item = cx.tcx().map.get(node_id);
-
-    let (name, span) = match var_item {
-        hir_map::NodeItem(item) => {
-            match item.node {
-                hir::ItemStatic(..) => (item.name, item.span),
-                hir::ItemConst(..) => (item.name, item.span),
-                _ => {
-                    span_bug!(item.span,
-                              "debuginfo::\
-                               create_global_var_metadata() -
-                               Captured var-id refers to \
-                               unexpected ast_item variant: {:?}",
-                              var_item)
-                }
-            }
-        },
-        _ => bug!("debuginfo::create_global_var_metadata() \
-                   - Captured var-id refers to unexpected \
-                   hir_map variant: {:?}",
-                  var_item)
-    };
+    let node_def_id = cx.tcx().map.local_def_id(node_id);
+    let (var_scope, span) = get_namespace_and_span_for_item(cx, node_def_id);
 
     let (file_metadata, line_number) = if span != codemap::DUMMY_SP {
         let loc = span_start(cx, span);
@@ -1879,12 +1859,8 @@ pub fn create_global_var_metadata(cx: &CrateContext,
     let is_local_to_unit = is_node_local_to_unit(cx, node_id);
     let variable_type = cx.tcx().node_id_to_type(node_id);
     let type_metadata = type_metadata(cx, variable_type, span);
-    let node_def_id = cx.tcx().map.local_def_id(node_id);
-    let namespace_node = namespace_for_item(cx, node_def_id);
-    let var_name = name.to_string();
-    let linkage_name =
-        namespace_node.mangled_name_of_contained_item(&var_name[..]);
-    let var_scope = namespace_node.scope;
+    let var_name = cx.tcx().item_name(node_def_id).to_string();
+    let linkage_name = mangled_name_of_item(cx, node_def_id, "");
 
     let var_name = CString::new(var_name).unwrap();
     let linkage_name = CString::new(linkage_name).unwrap();
diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs
index bb999c31ffb..371f6b5efaa 100644
--- a/src/librustc_trans/debuginfo/mod.rs
+++ b/src/librustc_trans/debuginfo/mod.rs
@@ -14,42 +14,38 @@ mod doc;
 use self::VariableAccess::*;
 use self::VariableKind::*;
 
-use self::utils::{DIB, span_start, assert_type_for_node_id, contains_nodebug_attribute,
-                  create_DIArray, is_node_local_to_unit};
-use self::namespace::{namespace_for_item, NamespaceTreeNode};
+use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit,
+                  get_namespace_and_span_for_item};
+use self::namespace::mangled_name_of_item;
 use self::type_names::compute_debuginfo_type_name;
 use self::metadata::{type_metadata, diverging_type_metadata};
 use self::metadata::{file_metadata, scope_metadata, TypeMap, compile_unit_metadata};
-use self::source_loc::InternalDebugLocation;
+use self::source_loc::InternalDebugLocation::{self, UnknownLocation};
 
 use llvm;
 use llvm::{ModuleRef, ContextRef, ValueRef};
 use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray,
-                      DIDescriptor, FlagPrototyped};
+                      FlagPrototyped};
 use rustc::hir::def_id::DefId;
-use rustc::infer::normalize_associated_type;
-use rustc::ty::subst::{self, Substs};
+use rustc::hir::map::DefPathData;
+use rustc::ty::subst::Substs;
 use rustc::hir;
 
 use abi::Abi;
-use common::{NodeIdAndSpan, CrateContext, FunctionContext, Block};
-use monomorphize;
-use rustc::infer;
+use common::{NodeIdAndSpan, CrateContext, FunctionContext, Block, BlockAndBuilder};
+use monomorphize::Instance;
 use rustc::ty::{self, Ty};
 use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
-use util::nodemap::{NodeMap, FnvHashMap, FnvHashSet};
-use rustc::hir::map as hir_map;
+use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet};
 
 use libc::c_uint;
 use std::cell::{Cell, RefCell};
 use std::ffi::CString;
 use std::ptr;
-use std::rc::Rc;
 
 use syntax::codemap::{Span, Pos};
 use syntax::{ast, codemap};
 use syntax::attr::IntType;
-use syntax::parse::token::{self, special_idents};
 
 pub mod gdb;
 mod utils;
@@ -59,8 +55,7 @@ mod metadata;
 mod create_scope_map;
 mod source_loc;
 
-pub use self::source_loc::set_source_location;
-pub use self::source_loc::clear_source_location;
+pub use self::create_scope_map::create_mir_scopes;
 pub use self::source_loc::start_emitting_source_locations;
 pub use self::source_loc::get_cleanup_debug_loc_for_ast_node;
 pub use self::source_loc::with_source_location_override;
@@ -84,7 +79,7 @@ pub struct CrateDebugContext<'tcx> {
     created_enum_disr_types: RefCell<FnvHashMap<(DefId, IntType), DIType>>,
 
     type_map: RefCell<TypeMap<'tcx>>,
-    namespace_map: RefCell<FnvHashMap<Vec<ast::Name>, Rc<NamespaceTreeNode>>>,
+    namespace_map: RefCell<DefIdMap<DIScope>>,
 
     // This collection is used to assert that composite types (structs, enums,
     // ...) have their members only set once:
@@ -104,7 +99,7 @@ impl<'tcx> CrateDebugContext<'tcx> {
             created_files: RefCell::new(FnvHashMap()),
             created_enum_disr_types: RefCell::new(FnvHashMap()),
             type_map: RefCell::new(TypeMap::new()),
-            namespace_map: RefCell::new(FnvHashMap()),
+            namespace_map: RefCell::new(DefIdMap()),
             composite_types_completed: RefCell::new(FnvHashSet()),
         };
     }
@@ -214,6 +209,18 @@ pub fn finalize(cx: &CrateContext) {
     };
 }
 
+/// Creates a function-specific debug context for a function w/o debuginfo.
+pub fn empty_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>)
+                                              -> FunctionDebugContext {
+    if cx.sess().opts.debuginfo == NoDebugInfo {
+        return FunctionDebugContext::DebugInfoDisabled;
+    }
+
+    // Clear the debug location so we don't assign them in the function prelude.
+    source_loc::set_debug_location(cx, None, UnknownLocation);
+    FunctionDebugContext::FunctionWithoutDebugInfo
+}
+
 /// Creates the function-specific debug context.
 ///
 /// Returns the FunctionDebugContext for the function which holds state needed
@@ -221,8 +228,9 @@ pub fn finalize(cx: &CrateContext) {
 /// FunctionDebugContext enum which indicates why no debuginfo should be created
 /// for the function.
 pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                                               fn_ast_id: ast::NodeId,
-                                               param_substs: &Substs<'tcx>,
+                                               instance: Instance<'tcx>,
+                                               sig: &ty::FnSig<'tcx>,
+                                               abi: Abi,
                                                llfn: ValueRef) -> FunctionDebugContext {
     if cx.sess().opts.debuginfo == NoDebugInfo {
         return FunctionDebugContext::DebugInfoDisabled;
@@ -230,105 +238,10 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
     // Clear the debug location so we don't assign them in the function prelude.
     // Do this here already, in case we do an early exit from this function.
-    source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation);
-
-    if fn_ast_id == ast::DUMMY_NODE_ID {
-        // This is a function not linked to any source location, so don't
-        // generate debuginfo for it.
-        return FunctionDebugContext::FunctionWithoutDebugInfo;
-    }
-
-    let empty_generics = hir::Generics::empty();
-
-    let fnitem = cx.tcx().map.get(fn_ast_id);
-
-    let (name, fn_decl, generics, top_level_block, span, has_path) = match fnitem {
-        hir_map::NodeItem(ref item) => {
-            if contains_nodebug_attribute(&item.attrs) {
-                return FunctionDebugContext::FunctionWithoutDebugInfo;
-            }
-
-            match item.node {
-                hir::ItemFn(ref fn_decl, _, _, _, ref generics, ref top_level_block) => {
-                    (item.name, fn_decl, generics, top_level_block, item.span, true)
-                }
-                _ => {
-                    span_bug!(item.span,
-                        "create_function_debug_context: item bound to non-function");
-                }
-            }
-        }
-        hir_map::NodeImplItem(impl_item) => {
-            match impl_item.node {
-                hir::ImplItemKind::Method(ref sig, ref body) => {
-                    if contains_nodebug_attribute(&impl_item.attrs) {
-                        return FunctionDebugContext::FunctionWithoutDebugInfo;
-                    }
-
-                    (impl_item.name,
-                     &sig.decl,
-                     &sig.generics,
-                     body,
-                     impl_item.span,
-                     true)
-                }
-                _ => {
-                    span_bug!(impl_item.span,
-                              "create_function_debug_context() \
-                               called on non-method impl item?!")
-                }
-            }
-        }
-        hir_map::NodeExpr(ref expr) => {
-            match expr.node {
-                hir::ExprClosure(_, ref fn_decl, ref top_level_block) => {
-                    let name = format!("fn{}", token::gensym("fn"));
-                    let name = token::intern(&name[..]);
-                    (name, fn_decl,
-                        // This is not quite right. It should actually inherit
-                        // the generics of the enclosing function.
-                        &empty_generics,
-                        top_level_block,
-                        expr.span,
-                        // Don't try to lookup the item path:
-                        false)
-                }
-                _ => span_bug!(expr.span,
-                        "create_function_debug_context: expected an expr_fn_block here")
-            }
-        }
-        hir_map::NodeTraitItem(trait_item) => {
-            match trait_item.node {
-                hir::MethodTraitItem(ref sig, Some(ref body)) => {
-                    if contains_nodebug_attribute(&trait_item.attrs) {
-                        return FunctionDebugContext::FunctionWithoutDebugInfo;
-                    }
-
-                    (trait_item.name,
-                     &sig.decl,
-                     &sig.generics,
-                     body,
-                     trait_item.span,
-                     true)
-                }
-                _ => {
-                    bug!("create_function_debug_context: \
-                          unexpected sort of node: {:?}",
-                         fnitem)
-                }
-            }
-        }
-        hir_map::NodeForeignItem(..) |
-        hir_map::NodeVariant(..) |
-        hir_map::NodeStructCtor(..) => {
-            return FunctionDebugContext::FunctionWithoutDebugInfo;
-        }
-        _ => bug!("create_function_debug_context: \
-                   unexpected sort of node: {:?}",
-                  fnitem)
-    };
+    source_loc::set_debug_location(cx, None, UnknownLocation);
 
     // This can be the case for functions inlined from another crate
+    let (containing_scope, span) = get_namespace_and_span_for_item(cx, instance.def);
     if span == codemap::DUMMY_SP {
         return FunctionDebugContext::FunctionWithoutDebugInfo;
     }
@@ -337,44 +250,38 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     let file_metadata = file_metadata(cx, &loc.file.name);
 
     let function_type_metadata = unsafe {
-        let fn_signature = get_function_signature(cx,
-                                                  fn_ast_id,
-                                                  param_substs,
-                                                  span);
+        let fn_signature = get_function_signature(cx, sig, abi);
         llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature)
     };
 
+    // Find the enclosing function, in case this is a closure.
+    let mut fn_def_id = instance.def;
+    let mut def_key = cx.tcx().def_key(fn_def_id);
+    let mut name = def_key.disambiguated_data.data.to_string();
+    let name_len = name.len();
+    while def_key.disambiguated_data.data == DefPathData::ClosureExpr {
+        fn_def_id.index = def_key.parent.expect("closure without a parent?");
+        def_key = cx.tcx().def_key(fn_def_id);
+    }
+
     // Get_template_parameters() will append a `<...>` clause to the function
     // name if necessary.
-    let mut function_name = name.to_string();
+    let generics = cx.tcx().lookup_item_type(fn_def_id).generics;
     let template_parameters = get_template_parameters(cx,
-                                                      generics,
-                                                      param_substs,
+                                                      &generics,
+                                                      instance.substs,
                                                       file_metadata,
-                                                      &mut function_name);
-
-    // There is no hir_map::Path for hir::ExprClosure-type functions. For now,
-    // just don't put them into a namespace. In the future this could be improved
-    // somehow (storing a path in the hir_map, or construct a path using the
-    // enclosing function).
-    let (linkage_name, containing_scope) = if has_path {
-        let fn_ast_def_id = cx.tcx().map.local_def_id(fn_ast_id);
-        let namespace_node = namespace_for_item(cx, fn_ast_def_id);
-        let linkage_name = namespace_node.mangled_name_of_contained_item(
-            &function_name[..]);
-        let containing_scope = namespace_node.scope;
-        (linkage_name, containing_scope)
-    } else {
-        (function_name.clone(), file_metadata)
-    };
+                                                      &mut name);
+
+    // Build the linkage_name out of the item path and "template" parameters.
+    let linkage_name = mangled_name_of_item(cx, instance.def, &name[name_len..]);
 
-    // Clang sets this parameter to the opening brace of the function's block,
-    // so let's do this too.
-    let scope_line = span_start(cx, top_level_block.span).line;
+    let scope_line = span_start(cx, span).line;
 
-    let is_local_to_unit = is_node_local_to_unit(cx, fn_ast_id);
+    let local_id = cx.tcx().map.as_local_node_id(instance.def);
+    let is_local_to_unit = local_id.map_or(false, |id| is_node_local_to_unit(cx, id));
 
-    let function_name = CString::new(function_name).unwrap();
+    let function_name = CString::new(name).unwrap();
     let linkage_name = CString::new(linkage_name).unwrap();
     let fn_metadata = unsafe {
         llvm::LLVMDIBuilderCreateFunction(
@@ -395,54 +302,24 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             ptr::null_mut())
     };
 
-    let scope_map = create_scope_map::create_scope_map(cx,
-                                                       &fn_decl.inputs,
-                                                       &top_level_block,
-                                                       fn_metadata,
-                                                       fn_ast_id);
-
     // Initialize fn debug context (including scope map and namespace map)
     let fn_debug_context = box FunctionDebugContextData {
-        scope_map: RefCell::new(scope_map),
+        scope_map: RefCell::new(NodeMap()),
         fn_metadata: fn_metadata,
         argument_counter: Cell::new(1),
         source_locations_enabled: Cell::new(false),
         source_location_override: Cell::new(false),
     };
 
-
-
     return FunctionDebugContext::RegularContext(fn_debug_context);
 
     fn get_function_signature<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                                        fn_ast_id: ast::NodeId,
-                                        param_substs: &Substs<'tcx>,
-                                        error_reporting_span: Span) -> DIArray {
+                                        sig: &ty::FnSig<'tcx>,
+                                        abi: Abi) -> DIArray {
         if cx.sess().opts.debuginfo == LimitedDebugInfo {
             return create_DIArray(DIB(cx), &[]);
         }
 
-        // Return type -- llvm::DIBuilder wants this at index 0
-        assert_type_for_node_id(cx, fn_ast_id, error_reporting_span);
-        let fn_type = cx.tcx().node_id_to_type(fn_ast_id);
-        let fn_type = monomorphize::apply_param_substs(cx.tcx(), param_substs, &fn_type);
-
-        let (sig, abi) = match fn_type.sty {
-            ty::TyFnDef(_, _, ref barefnty) | ty::TyFnPtr(ref barefnty) => {
-                let sig = cx.tcx().erase_late_bound_regions(&barefnty.sig);
-                let sig = infer::normalize_associated_type(cx.tcx(), &sig);
-                (sig, barefnty.abi)
-            }
-            ty::TyClosure(def_id, ref substs) => {
-                let closure_type = cx.tcx().closure_type(def_id, substs);
-                let sig = cx.tcx().erase_late_bound_regions(&closure_type.sig);
-                let sig = infer::normalize_associated_type(cx.tcx(), &sig);
-                (sig, closure_type.abi)
-            }
-
-            _ => bug!("get_function_metdata: Expected a function type!")
-        };
-
         let mut signature = Vec::with_capacity(sig.inputs.len() + 1);
 
         // Return type -- llvm::DIBuilder wants this at index 0
@@ -477,86 +354,39 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     }
 
     fn get_template_parameters<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
-                                         generics: &hir::Generics,
+                                         generics: &ty::Generics<'tcx>,
                                          param_substs: &Substs<'tcx>,
                                          file_metadata: DIFile,
                                          name_to_append_suffix_to: &mut String)
                                          -> DIArray
     {
-        let self_type = param_substs.self_ty();
-        let self_type = normalize_associated_type(cx.tcx(), &self_type);
-
-        // Only true for static default methods:
-        let has_self_type = self_type.is_some();
+        let actual_types = param_substs.types.as_slice();
 
-        if !generics.is_type_parameterized() && !has_self_type {
+        if actual_types.is_empty() {
             return create_DIArray(DIB(cx), &[]);
         }
 
         name_to_append_suffix_to.push('<');
-
-        // The list to be filled with template parameters:
-        let mut template_params: Vec<DIDescriptor> =
-            Vec::with_capacity(generics.ty_params.len() + 1);
-
-        // Handle self type
-        if has_self_type {
-            let actual_self_type = self_type.unwrap();
-            // Add self type name to <...> clause of function name
-            let actual_self_type_name = compute_debuginfo_type_name(
-                cx,
-                actual_self_type,
-                true);
-
-            name_to_append_suffix_to.push_str(&actual_self_type_name[..]);
-
-            if generics.is_type_parameterized() {
-                name_to_append_suffix_to.push_str(",");
-            }
-
-            // Only create type information if full debuginfo is enabled
-            if cx.sess().opts.debuginfo == FullDebugInfo {
-                let actual_self_type_metadata = type_metadata(cx,
-                                                              actual_self_type,
-                                                              codemap::DUMMY_SP);
-
-                let name = special_idents::type_self.name.as_str();
-
-                let name = CString::new(name.as_bytes()).unwrap();
-                let param_metadata = unsafe {
-                    llvm::LLVMDIBuilderCreateTemplateTypeParameter(
-                        DIB(cx),
-                        ptr::null_mut(),
-                        name.as_ptr(),
-                        actual_self_type_metadata,
-                        file_metadata,
-                        0,
-                        0)
-                };
-
-                template_params.push(param_metadata);
-            }
-        }
-
-        // Handle other generic parameters
-        let actual_types = param_substs.types.get_slice(subst::FnSpace);
-        for (index, &hir::TyParam{ name, .. }) in generics.ty_params.iter().enumerate() {
-            let actual_type = actual_types[index];
+        for (i, &actual_type) in actual_types.iter().enumerate() {
             // Add actual type name to <...> clause of function name
             let actual_type_name = compute_debuginfo_type_name(cx,
                                                                actual_type,
                                                                true);
             name_to_append_suffix_to.push_str(&actual_type_name[..]);
 
-            if index != generics.ty_params.len() - 1 {
+            if i != actual_types.len() - 1 {
                 name_to_append_suffix_to.push_str(",");
             }
+        }
+        name_to_append_suffix_to.push('>');
 
-            // Again, only create type information if full debuginfo is enabled
-            if cx.sess().opts.debuginfo == FullDebugInfo {
+        // Again, only create type information if full debuginfo is enabled
+        let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo {
+            generics.types.as_slice().iter().enumerate().map(|(i, param)| {
+                let actual_type = actual_types[i];
                 let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP);
-                let name = CString::new(name.as_str().as_bytes()).unwrap();
-                let param_metadata = unsafe {
+                let name = CString::new(param.name.as_str().as_bytes()).unwrap();
+                unsafe {
                     llvm::LLVMDIBuilderCreateTemplateTypeParameter(
                         DIB(cx),
                         ptr::null_mut(),
@@ -565,24 +395,42 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                         file_metadata,
                         0,
                         0)
-                };
-                template_params.push(param_metadata);
-            }
-        }
-
-        name_to_append_suffix_to.push('>');
+                }
+            }).collect()
+        } else {
+            vec![]
+        };
 
         return create_DIArray(DIB(cx), &template_params[..]);
     }
 }
 
-fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                             variable_name: ast::Name,
-                             variable_type: Ty<'tcx>,
-                             scope_metadata: DIScope,
-                             variable_access: VariableAccess,
-                             variable_kind: VariableKind,
-                             span: Span) {
+/// Computes the scope map for a function given its declaration and body.
+pub fn fill_scope_map_for_function<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
+                                             fn_decl: &hir::FnDecl,
+                                             top_level_block: &hir::Block,
+                                             fn_ast_id: ast::NodeId) {
+    match fcx.debug_context {
+        FunctionDebugContext::RegularContext(box ref data) => {
+            let scope_map = create_scope_map::create_scope_map(fcx.ccx,
+                                                               &fn_decl.inputs,
+                                                               top_level_block,
+                                                               data.fn_metadata,
+                                                               fn_ast_id);
+            *data.scope_map.borrow_mut() = scope_map;
+        }
+        FunctionDebugContext::DebugInfoDisabled |
+        FunctionDebugContext::FunctionWithoutDebugInfo => {}
+    }
+}
+
+pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                                 variable_name: ast::Name,
+                                 variable_type: Ty<'tcx>,
+                                 scope_metadata: DIScope,
+                                 variable_access: VariableAccess,
+                                 variable_kind: VariableKind,
+                                 span: Span) {
     let cx: &CrateContext = bcx.ccx();
 
     let filename = span_start(cx, span).file.name.clone();
@@ -616,9 +464,8 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     address_operations.len() as c_uint,
                     argument_index)
             };
-            source_loc::set_debug_location(cx, InternalDebugLocation::new(scope_metadata,
-                                                                          loc.line,
-                                                                          loc.col.to_usize()));
+            source_loc::set_debug_location(cx, None,
+                InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize()));
             unsafe {
                 let debug_loc = llvm::LLVMGetCurrentDebugLocation(cx.raw_builder());
                 let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
@@ -642,7 +489,7 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                         .get_ref(span)
                         .source_locations_enabled
                         .get());
-            source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation);
+            source_loc::set_debug_location(cx, None, UnknownLocation);
         }
         _ => { /* nothing to do */ }
     }
@@ -651,19 +498,17 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum DebugLoc {
     At(ast::NodeId, Span),
+    ScopeAt(DIScope, Span),
     None
 }
 
 impl DebugLoc {
-    pub fn apply(&self, fcx: &FunctionContext) {
-        match *self {
-            DebugLoc::At(node_id, span) => {
-                source_loc::set_source_location(fcx, node_id, span);
-            }
-            DebugLoc::None => {
-                source_loc::clear_source_location(fcx);
-            }
-        }
+    pub fn apply(self, fcx: &FunctionContext) {
+        source_loc::set_source_location(fcx, None, self);
+    }
+
+    pub fn apply_to_bcx(self, bcx: &BlockAndBuilder) {
+        source_loc::set_source_location(bcx.fcx(), Some(bcx), self);
     }
 }
 
diff --git a/src/librustc_trans/debuginfo/namespace.rs b/src/librustc_trans/debuginfo/namespace.rs
index 5272a4fbbb7..fc31eaa4e74 100644
--- a/src/librustc_trans/debuginfo/namespace.rs
+++ b/src/librustc_trans/debuginfo/namespace.rs
@@ -10,118 +10,82 @@
 
 // Namespace Handling.
 
-use super::utils::{DIB, debug_context};
+use super::metadata::{file_metadata, NO_FILE_METADATA, UNKNOWN_LINE_NUMBER};
+use super::utils::{DIB, debug_context, span_start};
 
 use llvm;
 use llvm::debuginfo::DIScope;
 use rustc::hir::def_id::DefId;
-use rustc::hir::map as hir_map;
+use rustc::hir::map::DefPathData;
 use common::CrateContext;
 
+use libc::c_uint;
 use std::ffi::CString;
-use std::iter::once;
 use std::ptr;
-use std::rc::{Rc, Weak};
-use syntax::ast;
-use syntax::parse::token;
-
-pub struct NamespaceTreeNode {
-    pub name: ast::Name,
-    pub scope: DIScope,
-    pub parent: Option<Weak<NamespaceTreeNode>>,
-}
-
-impl NamespaceTreeNode {
-    pub fn mangled_name_of_contained_item(&self, item_name: &str) -> String {
-        fn fill_nested(node: &NamespaceTreeNode, output: &mut String) {
-            match node.parent {
-                Some(ref parent) => fill_nested(&parent.upgrade().unwrap(), output),
-                None => {}
-            }
-            let string = node.name.as_str();
-            output.push_str(&string.len().to_string());
-            output.push_str(&string);
+use syntax::codemap::DUMMY_SP;
+
+pub fn mangled_name_of_item(ccx: &CrateContext, def_id: DefId, extra: &str) -> String {
+    fn fill_nested(ccx: &CrateContext, def_id: DefId, extra: &str, output: &mut String) {
+        let def_key = ccx.tcx().def_key(def_id);
+        if let Some(parent) = def_key.parent {
+            fill_nested(ccx, DefId {
+                krate: def_id.krate,
+                index: parent
+            }, "", output);
         }
 
-        let mut name = String::from("_ZN");
-        fill_nested(self, &mut name);
-        name.push_str(&item_name.len().to_string());
-        name.push_str(item_name);
-        name.push('E');
-        name
-    }
-}
-
-pub fn namespace_for_item(cx: &CrateContext, def_id: DefId) -> Rc<NamespaceTreeNode> {
-    // prepend crate name.
-    // This shouldn't need a roundtrip through InternedString.
-    let krate = token::intern(&cx.tcx().crate_name(def_id.krate));
-    let krate = hir_map::DefPathData::TypeNs(krate);
-    let path = cx.tcx().def_path(def_id).data;
-    let mut path = once(krate).chain(path.into_iter().map(|e| e.data)).peekable();
-
-    let mut current_key = Vec::new();
-    let mut parent_node: Option<Rc<NamespaceTreeNode>> = None;
-
-    // Create/Lookup namespace for each element of the path.
-    loop {
-        // Emulate a for loop so we can use peek below.
-        let path_element = match path.next() {
-            Some(e) => e,
-            None => break
+        let name = match def_key.disambiguated_data.data {
+            DefPathData::CrateRoot => ccx.tcx().crate_name(def_id.krate),
+            data => data.as_interned_str()
         };
-        // Ignore the name of the item (the last path element).
-        if path.peek().is_none() {
-            break;
-        }
 
-        // This shouldn't need a roundtrip through InternedString.
-        let namespace_name = path_element.as_interned_str();
-        let name = token::intern(&namespace_name);
-        current_key.push(name);
-
-        let existing_node = debug_context(cx).namespace_map.borrow()
-                                             .get(&current_key).cloned();
-        let current_node = match existing_node {
-            Some(existing_node) => existing_node,
-            None => {
-                // create and insert
-                let parent_scope = match parent_node {
-                    Some(ref node) => node.scope,
-                    None => ptr::null_mut()
-                };
-                let namespace_name = CString::new(namespace_name.as_bytes()).unwrap();
-                let scope = unsafe {
-                    llvm::LLVMDIBuilderCreateNameSpace(
-                        DIB(cx),
-                        parent_scope,
-                        namespace_name.as_ptr(),
-                        // cannot reconstruct file ...
-                        ptr::null_mut(),
-                        // ... or line information, but that's not so important.
-                        0)
-                };
-
-                let node = Rc::new(NamespaceTreeNode {
-                    name: name,
-                    scope: scope,
-                    parent: parent_node.map(|parent| Rc::downgrade(&parent)),
-                });
-
-                debug_context(cx).namespace_map.borrow_mut()
-                                 .insert(current_key.clone(), node.clone());
+        output.push_str(&(name.len() + extra.len()).to_string());
+        output.push_str(&name);
+        output.push_str(extra);
+    }
 
-                node
-            }
-        };
+    let mut name = String::from("_ZN");
+    fill_nested(ccx, def_id, extra, &mut name);
+    name.push('E');
+    name
+}
 
-        parent_node = Some(current_node);
+pub fn item_namespace(ccx: &CrateContext, def_id: DefId) -> DIScope {
+    if let Some(&scope) = debug_context(ccx).namespace_map.borrow().get(&def_id) {
+        return scope;
     }
 
-    match parent_node {
-        Some(node) => node,
-        None => {
-            bug!("debuginfo::namespace_for_item: path too short for {:?}", def_id);
-        }
-    }
+    let def_key = ccx.tcx().def_key(def_id);
+    let parent_scope = def_key.parent.map_or(ptr::null_mut(), |parent| {
+        item_namespace(ccx, DefId {
+            krate: def_id.krate,
+            index: parent
+        })
+    });
+
+    let namespace_name = match def_key.disambiguated_data.data {
+        DefPathData::CrateRoot => ccx.tcx().crate_name(def_id.krate),
+        data => data.as_interned_str()
+    };
+
+    let namespace_name = CString::new(namespace_name.as_bytes()).unwrap();
+    let span = ccx.tcx().map.def_id_span(def_id, DUMMY_SP);
+    let (file, line) = if span != DUMMY_SP {
+        let loc = span_start(ccx, span);
+        (file_metadata(ccx, &loc.file.name), loc.line as c_uint)
+    } else {
+        (NO_FILE_METADATA, UNKNOWN_LINE_NUMBER)
+    };
+
+    let scope = unsafe {
+        llvm::LLVMDIBuilderCreateNameSpace(
+            DIB(ccx),
+            parent_scope,
+            namespace_name.as_ptr(),
+            file,
+            line as c_uint)
+    };
+
+    debug_context(ccx).namespace_map.borrow_mut().insert(def_id, scope);
+    scope
 }
diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs
index 2879da7d03b..6b00c1bb1a8 100644
--- a/src/librustc_trans/debuginfo/source_loc.rs
+++ b/src/librustc_trans/debuginfo/source_loc.rs
@@ -10,12 +10,13 @@
 
 use self::InternalDebugLocation::*;
 
-use super::utils::{debug_context, span_start, fn_should_be_ignored};
+use super::utils::{debug_context, span_start};
 use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER};
 use super::{FunctionDebugContext, DebugLoc};
 
 use llvm;
 use llvm::debuginfo::DIScope;
+use builder::Builder;
 use common::{NodeIdAndSpan, CrateContext, FunctionContext};
 
 use libc::c_uint;
@@ -86,41 +87,46 @@ pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
 /// Sets the current debug location at the beginning of the span.
 ///
-/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id
-/// parameter is used to reliably find the correct visibility scope for the code
-/// position.
+/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...).
 pub fn set_source_location(fcx: &FunctionContext,
-                           node_id: ast::NodeId,
-                           span: Span) {
-    match fcx.debug_context {
+                           builder: Option<&Builder>,
+                           debug_loc: DebugLoc) {
+    let builder = builder.map(|b| b.llbuilder);
+    let function_debug_context = match fcx.debug_context {
         FunctionDebugContext::DebugInfoDisabled => return,
         FunctionDebugContext::FunctionWithoutDebugInfo => {
-            set_debug_location(fcx.ccx, UnknownLocation);
+            set_debug_location(fcx.ccx, builder, UnknownLocation);
             return;
         }
-        FunctionDebugContext::RegularContext(box ref function_debug_context) => {
-            if function_debug_context.source_location_override.get() {
-                // Just ignore any attempts to set a new debug location while
-                // the override is active.
-                return;
-            }
-
-            let cx = fcx.ccx;
-
-            debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span));
+        FunctionDebugContext::RegularContext(box ref data) => data
+    };
 
-            if function_debug_context.source_locations_enabled.get() {
-                let loc = span_start(cx, span);
-                let scope = scope_metadata(fcx, node_id, span);
+    if function_debug_context.source_location_override.get() {
+        // Just ignore any attempts to set a new debug location while
+        // the override is active.
+        return;
+    }
 
-                set_debug_location(cx, InternalDebugLocation::new(scope,
-                                                                  loc.line,
-                                                                  loc.col.to_usize()));
-            } else {
-                set_debug_location(cx, UnknownLocation);
+    let dbg_loc = if function_debug_context.source_locations_enabled.get() {
+        let (scope, span) = match debug_loc {
+            DebugLoc::At(node_id, span) => {
+                (scope_metadata(fcx, node_id, span), span)
             }
-        }
-    }
+            DebugLoc::ScopeAt(scope, span) => (scope, span),
+            DebugLoc::None => {
+                set_debug_location(fcx.ccx, builder, UnknownLocation);
+                return;
+            }
+        };
+
+        debug!("set_source_location: {}",
+               fcx.ccx.sess().codemap().span_to_string(span));
+        let loc = span_start(fcx.ccx, span);
+        InternalDebugLocation::new(scope, loc.line, loc.col.to_usize())
+    } else {
+        UnknownLocation
+    };
+    set_debug_location(fcx.ccx, builder, dbg_loc);
 }
 
 /// This function makes sure that all debug locations emitted while executing
@@ -135,7 +141,7 @@ pub fn with_source_location_override<F, R>(fcx: &FunctionContext,
             wrapped_function()
         }
         FunctionDebugContext::FunctionWithoutDebugInfo => {
-            set_debug_location(fcx.ccx, UnknownLocation);
+            set_debug_location(fcx.ccx, None, UnknownLocation);
             wrapped_function()
         }
         FunctionDebugContext::RegularContext(box ref function_debug_context) => {
@@ -152,17 +158,6 @@ pub fn with_source_location_override<F, R>(fcx: &FunctionContext,
     }
 }
 
-/// Clears the current debug location.
-///
-/// Instructions generated hereafter won't be assigned a source location.
-pub fn clear_source_location(fcx: &FunctionContext) {
-    if fn_should_be_ignored(fcx) {
-        return;
-    }
-
-    set_debug_location(fcx.ccx, UnknownLocation);
-}
-
 /// Enables emitting source locations for the given functions.
 ///
 /// Since we don't want source locations to be emitted for the function prelude,
@@ -195,37 +190,42 @@ impl InternalDebugLocation {
     }
 }
 
-pub fn set_debug_location(cx: &CrateContext, debug_location: InternalDebugLocation) {
-    if debug_location == debug_context(cx).current_debug_location.get() {
-        return;
+pub fn set_debug_location(cx: &CrateContext,
+                          builder: Option<llvm::BuilderRef>,
+                          debug_location: InternalDebugLocation) {
+    if builder.is_none() {
+        if debug_location == debug_context(cx).current_debug_location.get() {
+            return;
+        }
     }
 
-    let metadata_node;
-
-    match debug_location {
+    let metadata_node = match debug_location {
         KnownLocation { scope, line, .. } => {
             // Always set the column to zero like Clang and GCC
             let col = UNKNOWN_COLUMN_NUMBER;
             debug!("setting debug location to {} {}", line, col);
 
             unsafe {
-                metadata_node = llvm::LLVMDIBuilderCreateDebugLocation(
+                llvm::LLVMDIBuilderCreateDebugLocation(
                     debug_context(cx).llcontext,
                     line as c_uint,
                     col as c_uint,
                     scope,
-                    ptr::null_mut());
+                    ptr::null_mut())
             }
         }
         UnknownLocation => {
             debug!("clearing debug location ");
-            metadata_node = ptr::null_mut();
+            ptr::null_mut()
         }
     };
 
-    unsafe {
-        llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node);
+    if builder.is_none() {
+        debug_context(cx).current_debug_location.set(debug_location);
     }
 
-    debug_context(cx).current_debug_location.set(debug_location);
+    let builder = builder.unwrap_or_else(|| cx.raw_builder());
+    unsafe {
+        llvm::LLVMSetCurrentDebugLocation(builder, metadata_node);
+    }
 }
diff --git a/src/librustc_trans/debuginfo/utils.rs b/src/librustc_trans/debuginfo/utils.rs
index bef7af3aec6..3fd97937184 100644
--- a/src/librustc_trans/debuginfo/utils.rs
+++ b/src/librustc_trans/debuginfo/utils.rs
@@ -11,7 +11,7 @@
 // Utility Functions.
 
 use super::{FunctionDebugContext, CrateDebugContext};
-use super::namespace::namespace_for_item;
+use super::namespace::item_namespace;
 
 use rustc::hir::def_id::DefId;
 
@@ -44,16 +44,6 @@ pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray {
     };
 }
 
-pub fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool {
-    attributes.iter().any(|attr| {
-        let meta_item: &ast::MetaItem = &attr.node.value;
-        match meta_item.node {
-            ast::MetaItemKind::Word(ref value) => &value[..] == "no_debug",
-            _ => false
-        }
-    })
-}
-
 /// Return codemap::Loc corresponding to the beginning of the span
 pub fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc {
     cx.sess().codemap().lookup_char_pos(span.lo)
@@ -87,21 +77,19 @@ pub fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
     }
 }
 
-pub fn assert_type_for_node_id(cx: &CrateContext,
-                           node_id: ast::NodeId,
-                           error_reporting_span: Span) {
-    if !cx.tcx().node_types().contains_key(&node_id) {
-        span_bug!(error_reporting_span,
-                  "debuginfo: Could not find type for node id!");
-    }
-}
-
 pub fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: DefId)
                                    -> (DIScope, Span) {
-    let containing_scope = namespace_for_item(cx, def_id).scope;
-    let definition_span = cx.tcx().map.def_id_span(def_id, codemap::DUMMY_SP /* (1) */ );
-
-    // (1) For external items there is no span information
+    let containing_scope = item_namespace(cx, DefId {
+        krate: def_id.krate,
+        index: cx.tcx().def_key(def_id).parent
+                 .expect("get_namespace_and_span_for_item: missing parent?")
+    });
+
+    // Try to get some span information, if we have an inlined item.
+    let definition_span = match cx.external().borrow().get(&def_id) {
+        Some(&Some(node_id)) => cx.tcx().map.span(node_id),
+        _ => cx.tcx().map.def_id_span(def_id, codemap::DUMMY_SP)
+    };
 
     (containing_scope, definition_span)
 }
diff --git a/src/librustc_trans/expr.rs b/src/librustc_trans/expr.rs
index beca81da05f..6955d51cecc 100644
--- a/src/librustc_trans/expr.rs
+++ b/src/librustc_trans/expr.rs
@@ -115,7 +115,7 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                               -> Block<'blk, 'tcx> {
     let mut bcx = bcx;
 
-    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
+    expr.debug_loc().apply(bcx.fcx);
 
     if adjustment_required(bcx, expr) {
         // use trans, which may be less efficient but
@@ -587,7 +587,7 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     debug!("trans_unadjusted(expr={:?})", expr);
     let _indenter = indenter();
 
-    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
+    expr.debug_loc().apply(bcx.fcx);
 
     return match expr_kind(bcx.tcx(), expr) {
         ExprKind::Lvalue | ExprKind::RvalueDatum => {
@@ -923,7 +923,7 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         return bcx;
     }
 
-    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
+    expr.debug_loc().apply(bcx.fcx);
 
     match expr.node {
         hir::ExprBreak(label_opt) => {
@@ -987,7 +987,7 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 //
                 // We could avoid this intermediary with some analysis
                 // to determine whether `dst` may possibly own `src`.
-                debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
+                expr.debug_loc().apply(bcx.fcx);
                 let src_datum = unpack_datum!(
                     bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign"));
                 let opt_hint_datum = dst_datum.kind.drop_flag_info.hint_datum(bcx);
@@ -1062,7 +1062,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _icx = push_ctxt("trans_rvalue_dps_unadjusted");
     let mut bcx = bcx;
 
-    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
+    expr.debug_loc().apply(bcx.fcx);
 
     // Entry into the method table if this is an overloaded call/op.
     let method_call = MethodCall::expr(expr.id);
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index 5676024ea95..39ea25619ee 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -272,10 +272,9 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     let _s = StatRecorder::new(ccx, format!("drop {:?}", t));
 
-    let empty_substs = ccx.tcx().mk_substs(Substs::empty());
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &arena);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &arena);
 
     let bcx = fcx.init(false, None);
 
diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs
index 5924ae1ad84..d7932046243 100644
--- a/src/librustc_trans/intrinsic.rs
+++ b/src/librustc_trans/intrinsic.rs
@@ -195,8 +195,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let name = tcx.item_name(def_id).as_str();
 
     let span = match call_debug_location {
-        DebugLoc::At(_, span) => span,
-        DebugLoc::None => fcx.span.unwrap_or(DUMMY_SP)
+        DebugLoc::At(_, span) | DebugLoc::ScopeAt(_, span) => span,
+        DebugLoc::None => {
+            span_bug!(fcx.span.unwrap_or(DUMMY_SP),
+                      "intrinsic `{}` called with missing span", name);
+        }
     };
 
     let cleanup_scope = fcx.push_custom_cleanup_scope();
@@ -1319,10 +1322,9 @@ fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
         sig: ty::Binder(sig)
     });
     let llfn = declare::define_internal_fn(ccx, name, rust_fn_ty);
-    let empty_substs = ccx.tcx().mk_substs(Substs::empty());
     let (fcx, block_arena);
     block_arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena);
     let bcx = fcx.init(true, None);
     trans(bcx);
     fcx.cleanup();
diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs
index 478094c2b87..9e5476ae80d 100644
--- a/src/librustc_trans/meth.rs
+++ b/src/librustc_trans/meth.rs
@@ -93,10 +93,9 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
         symbol_names::internal_name_from_type_and_suffix(ccx, method_ty, "object_shim");
     let llfn = declare::define_internal_fn(ccx, &function_name, method_ty);
 
-    let empty_substs = tcx.mk_substs(Substs::empty());
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena);
+    fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena);
     let mut bcx = fcx.init(false, None);
     assert!(!fcx.needs_ret_allocas);
 
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index 303cf61ad33..f70dc0183fd 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -54,9 +54,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             bcx = self.trans_statement(bcx, statement);
         }
 
-        debug!("trans_block: terminator: {:?}", data.terminator());
-
-        match data.terminator().kind {
+        let terminator = data.terminator();
+        debug!("trans_block: terminator: {:?}", terminator);
+
+        let debug_loc = DebugLoc::ScopeAt(self.scopes[terminator.scope.index()],
+                                          terminator.span);
+        debug_loc.apply_to_bcx(&bcx);
+        debug_loc.apply(bcx.fcx());
+        match terminator.kind {
             mir::TerminatorKind::Resume => {
                 if let Some(cleanup_pad) = cleanup_pad {
                     bcx.cleanup_ret(cleanup_pad, None);
@@ -117,7 +122,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
             mir::TerminatorKind::Return => {
                 bcx.with_block(|bcx| {
-                    self.fcx.build_return_block(bcx, DebugLoc::None);
+                    self.fcx.build_return_block(bcx, debug_loc);
                 })
             }
 
@@ -144,7 +149,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                self.llblock(target),
                                unwind.llbb(),
                                cleanup_bundle.as_ref());
-                    self.bcx(target).at_start(|bcx| drop::drop_fill(bcx, lvalue.llval, ty));
+                    self.bcx(target).at_start(|bcx| {
+                        debug_loc.apply_to_bcx(bcx);
+                        drop::drop_fill(bcx, lvalue.llval, ty)
+                    });
                 } else {
                     bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref());
                     drop::drop_fill(&bcx, lvalue.llval, ty);
@@ -267,7 +275,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         bcx.with_block(|bcx| {
                             trans_intrinsic_call(bcx, callee.ty, &fn_ty,
                                                            ArgVals(llargs), dest,
-                                                           DebugLoc::None);
+                                                           debug_loc);
                         });
 
                         if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
@@ -311,13 +319,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                cleanup_bundle.as_ref());
                     fn_ty.apply_attrs_callsite(invokeret);
 
-                    landingpad.at_start(|bcx| for op in args {
-                        self.set_operand_dropped(bcx, op);
+                    landingpad.at_start(|bcx| {
+                        debug_loc.apply_to_bcx(bcx);
+                        for op in args {
+                            self.set_operand_dropped(bcx, op);
+                        }
                     });
 
                     if destination.is_some() {
                         let ret_bcx = ret_bcx.build();
                         ret_bcx.at_start(|ret_bcx| {
+                            debug_loc.apply_to_bcx(ret_bcx);
                             let op = OperandRef {
                                 val: OperandValue::Immediate(invokeret),
                                 ty: sig.output.unwrap()
@@ -514,7 +526,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let use_funclets = base::wants_msvc_seh(bcx.sess()) && data.is_cleanup;
         let cleanup_pad = if use_funclets {
             bcx.set_personality_fn(self.fcx.eh_personality());
-            bcx.at_start(|bcx| Some(bcx.cleanup_pad(None, &[])))
+            bcx.at_start(|bcx| {
+                DebugLoc::None.apply_to_bcx(bcx);
+                Some(bcx.cleanup_pad(None, &[]))
+            })
         } else {
             None
         };
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index 7e44b72db7f..1869845ccb1 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -10,11 +10,19 @@
 
 use libc::c_uint;
 use llvm::{self, ValueRef};
+use llvm::debuginfo::DIScope;
 use rustc::ty;
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
+use session::config::FullDebugInfo;
 use base;
 use common::{self, Block, BlockAndBuilder, FunctionContext};
+use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind};
+use machine;
+use type_of;
+
+use syntax::codemap::DUMMY_SP;
+use syntax::parse::token;
 
 use std::ops::Deref;
 use std::rc::Rc;
@@ -44,8 +52,6 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> {
     }
 }
 
-// FIXME DebugLoc is always None right now
-
 /// Master context for translating MIR.
 pub struct MirContext<'bcx, 'tcx:'bcx> {
     mir: CachedMir<'bcx, 'tcx>,
@@ -92,6 +98,9 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     /// always indirect, though we try to avoid creating an alloca
     /// when we can (and just reuse the pointer the caller provided).
     args: Vec<LvalueRef<'tcx>>,
+
+    /// Debug information for MIR scopes.
+    scopes: Vec<DIScope>
 }
 
 enum TempRef<'tcx> {
@@ -113,11 +122,26 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
       analyze::lvalue_temps(bcx, &mir)
     });
 
+    // Compute debuginfo scopes from MIR scopes.
+    let scopes = debuginfo::create_mir_scopes(fcx);
+
     // Allocate variable and temp allocas
     let vars = mir.var_decls.iter()
-                            .map(|decl| (bcx.monomorphize(&decl.ty), decl.name))
-                            .map(|(mty, name)| LvalueRef::alloca(&bcx, mty, &name.as_str()))
-                            .collect();
+                            .map(|decl| (bcx.monomorphize(&decl.ty), decl))
+                            .map(|(mty, decl)| {
+        let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str());
+
+        let scope = scopes[decl.scope.index()];
+        if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
+            bcx.with_block(|bcx| {
+                declare_local(bcx, decl.name, mty, scope,
+                              VariableAccess::DirectVariable { alloca: lvalue.llval },
+                              VariableKind::LocalVariable, decl.span);
+            });
+        }
+
+        lvalue
+    }).collect();
     let temps = mir.temp_decls.iter()
                               .map(|decl| bcx.monomorphize(&decl.ty))
                               .enumerate()
@@ -132,7 +156,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
                                   TempRef::Operand(None)
                               })
                               .collect();
-    let args = arg_value_refs(&bcx, &mir);
+    let args = arg_value_refs(&bcx, &mir, &scopes);
 
     // Allocate a `Block` for every basic block
     let block_bcxs: Vec<Block<'blk,'tcx>> =
@@ -152,6 +176,11 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
     let start_bcx = block_bcxs[mir::START_BLOCK.index()];
     bcx.br(start_bcx.llbb);
 
+    // Up until here, IR instructions for this function have explicitly not been annotated with
+    // source code location, so we don't step into call setup code. From here on, source location
+    // emitting should be enabled.
+    debuginfo::start_emitting_source_locations(fcx);
+
     let mut mircx = MirContext {
         mir: mir.clone(),
         fcx: fcx,
@@ -161,6 +190,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
         vars: vars,
         temps: temps,
         args: args,
+        scopes: scopes
     };
 
     let mut visited = BitVector::new(mir_blocks.len());
@@ -185,6 +215,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
         }
     }
 
+    DebugLoc::None.apply(fcx);
     fcx.cleanup();
 }
 
@@ -192,12 +223,25 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
 /// argument's value. As arguments are lvalues, these are always
 /// indirect.
 fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                              mir: &mir::Mir<'tcx>)
+                              mir: &mir::Mir<'tcx>,
+                              scopes: &[DIScope])
                               -> Vec<LvalueRef<'tcx>> {
     let fcx = bcx.fcx();
     let tcx = bcx.tcx();
     let mut idx = 0;
     let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
+
+    // Get the argument scope assuming ScopeId(0) has no parent.
+    let arg_scope = mir.scopes.get(0).and_then(|data| {
+        let scope = scopes[0];
+        if data.parent_scope.is_none() && !scope.is_null() &&
+           bcx.sess().opts.debuginfo == FullDebugInfo {
+            Some(scope)
+        } else {
+            None
+        }
+    });
+
     mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| {
         let arg_ty = bcx.monomorphize(&arg_decl.ty);
         if arg_decl.spread {
@@ -211,13 +255,14 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                 _ => bug!("spread argument isn't a tuple?!")
             };
 
+            let lltuplety = type_of::type_of(bcx.ccx(), arg_ty);
             let lltemp = bcx.with_block(|bcx| {
                 base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
             });
             for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
                 let dst = bcx.struct_gep(lltemp, i);
                 let arg = &fcx.fn_ty.args[idx];
-                    idx += 1;
+                idx += 1;
                 if common::type_is_fat_ptr(tcx, tupled_arg_ty) {
                         // We pass fat pointers as two words, but inside the tuple
                         // they are the two sub-fields of a single aggregate field.
@@ -228,17 +273,37 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                 } else {
                     arg.store_fn_arg(bcx, &mut llarg_idx, dst);
                 }
+
+                bcx.with_block(|bcx| arg_scope.map(|scope| {
+                    let byte_offset_of_var_in_tuple =
+                        machine::llelement_offset(bcx.ccx(), lltuplety, i);
+
+                    let address_operations = unsafe {
+                        [llvm::LLVMDIBuilderCreateOpDeref(),
+                         llvm::LLVMDIBuilderCreateOpPlus(),
+                         byte_offset_of_var_in_tuple as i64]
+                    };
+
+                    let variable_access = VariableAccess::IndirectVariable {
+                        alloca: lltemp,
+                        address_operations: &address_operations
+                    };
+                    declare_local(bcx, token::special_idents::invalid.name,
+                                  tupled_arg_ty, scope, variable_access,
+                                  VariableKind::ArgumentVariable(arg_index + i + 1),
+                                  bcx.fcx().span.unwrap_or(DUMMY_SP));
+                }));
             }
             return LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty));
         }
 
         let arg = &fcx.fn_ty.args[idx];
         idx += 1;
-        let llval = if arg.is_indirect() {
+        let llval = if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo {
             // Don't copy an indirect argument to an alloca, the caller
             // already put it in a temporary alloca and gave it up, unless
             // we emit extra-debug-info, which requires local allocas :(.
-            // FIXME: lifetimes, debug info
+            // FIXME: lifetimes
             let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
             llarg_idx += 1;
             llarg
@@ -261,6 +326,12 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
             }
             lltemp
         };
+        bcx.with_block(|bcx| arg_scope.map(|scope| {
+            declare_local(bcx, token::special_idents::invalid.name, arg_ty, scope,
+                          VariableAccess::DirectVariable { alloca: llval },
+                          VariableKind::ArgumentVariable(arg_index + 1),
+                          bcx.fcx().span.unwrap_or(DUMMY_SP));
+        }));
         LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
     }).collect()
 }
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index 8e5d220b4f4..641603f5aaa 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -38,7 +38,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_rvalue(&mut self,
                         bcx: BlockAndBuilder<'bcx, 'tcx>,
                         dest: LvalueRef<'tcx>,
-                        rvalue: &mir::Rvalue<'tcx>)
+                        rvalue: &mir::Rvalue<'tcx>,
+                        debug_loc: DebugLoc)
                         -> BlockAndBuilder<'bcx, 'tcx>
     {
         debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})",
@@ -58,7 +59,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
                     // into-coerce of a thin pointer to a fat pointer - just
                     // use the operand path.
-                    let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
+                    let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc);
                     self.store_operand(&bcx, dest.llval, temp);
                     return bcx;
                 }
@@ -217,7 +218,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
             _ => {
                 assert!(rvalue_creates_operand(rvalue));
-                let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
+                let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc);
                 self.store_operand(&bcx, dest.llval, temp);
                 bcx
             }
@@ -226,7 +227,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
     pub fn trans_rvalue_operand(&mut self,
                                 bcx: BlockAndBuilder<'bcx, 'tcx>,
-                                rvalue: &mir::Rvalue<'tcx>)
+                                rvalue: &mir::Rvalue<'tcx>,
+                                debug_loc: DebugLoc)
                                 -> (BlockAndBuilder<'bcx, 'tcx>, OperandRef<'tcx>)
     {
         assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
@@ -419,7 +421,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                        lhs_addr, lhs_extra,
                                                        rhs_addr, rhs_extra,
                                                        lhs.ty, op.to_hir_binop(),
-                                                       DebugLoc::None)
+                                                       debug_loc)
                             })
                         }
                         _ => bug!()
@@ -470,7 +472,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                                    box_ty,
                                                                    llsize,
                                                                    llalign,
-                                                                   DebugLoc::None);
+                                                                   debug_loc);
                     llval = Some(val);
                     bcx
                 });
diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs
index 1d85ac6fb79..e4967cead07 100644
--- a/src/librustc_trans/mir/statement.rs
+++ b/src/librustc_trans/mir/statement.rs
@@ -10,6 +10,7 @@
 
 use rustc::mir::repr as mir;
 use common::BlockAndBuilder;
+use debuginfo::DebugLoc;
 
 use super::MirContext;
 use super::TempRef;
@@ -21,6 +22,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                            -> BlockAndBuilder<'bcx, 'tcx> {
         debug!("trans_statement(statement={:?})", statement);
 
+        let debug_loc = DebugLoc::ScopeAt(self.scopes[statement.scope.index()],
+                                          statement.span);
+        debug_loc.apply_to_bcx(&bcx);
+        debug_loc.apply(bcx.fcx());
         match statement.kind {
             mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
                 match *lvalue {
@@ -28,10 +33,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let index = index as usize;
                         match self.temps[index as usize] {
                             TempRef::Lvalue(tr_dest) => {
-                                self.trans_rvalue(bcx, tr_dest, rvalue)
+                                self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
                             }
                             TempRef::Operand(None) => {
-                                let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue);
+                                let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue,
+                                                                               debug_loc);
                                 self.temps[index] = TempRef::Operand(Some(operand));
                                 bcx
                             }
@@ -44,7 +50,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     }
                     _ => {
                         let tr_dest = self.trans_lvalue(&bcx, lvalue);
-                        self.trans_rvalue(bcx, tr_dest, rvalue)
+                        self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
                     }
                 }
             }
diff --git a/src/test/compile-fail/bad-intrinsic-monomorphization.rs b/src/test/compile-fail/bad-intrinsic-monomorphization.rs
index 049552aa2d7..cfb64f80767 100644
--- a/src/test/compile-fail/bad-intrinsic-monomorphization.rs
+++ b/src/test/compile-fail/bad-intrinsic-monomorphization.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics, rustc_attrs, core_intrinsics)]
+#![feature(repr_simd, platform_intrinsics, core_intrinsics)]
 #![allow(warnings)]
 
 // Bad monomorphizations could previously cause LLVM asserts even though the
@@ -23,19 +23,16 @@ use std::intrinsics;
 #[derive(Copy, Clone)]
 struct Foo(i64);
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_cttz(v: Foo) -> Foo {
     intrinsics::cttz(v)
     //~^ ERROR `cttz` intrinsic: expected basic integer type, found `Foo`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo {
     intrinsics::fadd_fast(a, b)
     //~^ ERROR `fadd_fast` intrinsic: expected basic float type, found `Foo`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo {
     simd_add(a, b)
     //~^ ERROR `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
diff --git a/src/test/compile-fail/non-interger-atomic.rs b/src/test/compile-fail/non-interger-atomic.rs
index d2376eecd9b..0b7b33de421 100644
--- a/src/test/compile-fail/non-interger-atomic.rs
+++ b/src/test/compile-fail/non-interger-atomic.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(core_intrinsics, rustc_attrs)]
+#![feature(core_intrinsics)]
 #![allow(warnings)]
 
 use std::intrinsics;
@@ -18,97 +18,81 @@ struct Foo(i64);
 type Bar = &'static Fn();
 type Quux = [u8; 100];
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_bool_load(p: &mut bool, v: bool) {
     intrinsics::atomic_load(p);
     //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `bool`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_bool_store(p: &mut bool, v: bool) {
     intrinsics::atomic_store(p, v);
     //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `bool`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_bool_xchg(p: &mut bool, v: bool) {
     intrinsics::atomic_xchg(p, v);
     //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `bool`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_bool_cxchg(p: &mut bool, v: bool) {
     intrinsics::atomic_cxchg(p, v, v);
     //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `bool`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Foo_load(p: &mut Foo, v: Foo) {
     intrinsics::atomic_load(p);
     //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `Foo`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Foo_store(p: &mut Foo, v: Foo) {
     intrinsics::atomic_store(p, v);
     //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `Foo`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Foo_xchg(p: &mut Foo, v: Foo) {
     intrinsics::atomic_xchg(p, v);
     //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `Foo`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) {
     intrinsics::atomic_cxchg(p, v, v);
     //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `Foo`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Bar_load(p: &mut Bar, v: Bar) {
     intrinsics::atomic_load(p);
     //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Bar_store(p: &mut Bar, v: Bar) {
     intrinsics::atomic_store(p, v);
     //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Bar_xchg(p: &mut Bar, v: Bar) {
     intrinsics::atomic_xchg(p, v);
     //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) {
     intrinsics::atomic_cxchg(p, v, v);
     //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Quux_load(p: &mut Quux, v: Quux) {
     intrinsics::atomic_load(p);
     //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Quux_store(p: &mut Quux, v: Quux) {
     intrinsics::atomic_store(p, v);
     //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `[u8; 100]`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Quux_xchg(p: &mut Quux, v: Quux) {
     intrinsics::atomic_xchg(p, v);
     //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `[u8; 100]`
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 unsafe fn test_Quux_cxchg(p: &mut Quux, v: Quux) {
     intrinsics::atomic_cxchg(p, v, v);
     //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `[u8; 100]`
diff --git a/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs b/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs
index 33954d23b19..35c368f4cbe 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
+#![feature(repr_simd, platform_intrinsics)]
 #![allow(non_camel_case_types)]
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -34,7 +34,6 @@ extern "platform-intrinsic" {
     fn simd_xor<T>(x: T, y: T) -> T;
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
     let y = u32x4(0, 0, 0, 0);
diff --git a/src/test/compile-fail/simd-intrinsic-generic-cast.rs b/src/test/compile-fail/simd-intrinsic-generic-cast.rs
index cb3bed7209e..4999b790b13 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-cast.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-cast.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
+#![feature(repr_simd, platform_intrinsics)]
 
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -35,7 +35,6 @@ extern "platform-intrinsic" {
     fn simd_cast<T, U>(x: T) -> U;
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
 
diff --git a/src/test/compile-fail/simd-intrinsic-generic-comparison.rs b/src/test/compile-fail/simd-intrinsic-generic-comparison.rs
index 0e7b2bd4904..617b03a8711 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-comparison.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-comparison.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, platform_intrinsics, rustc_attrs)]
+#![feature(repr_simd, platform_intrinsics)]
 
 #[repr(simd)]
 #[derive(Copy, Clone)]
@@ -29,7 +29,6 @@ extern "platform-intrinsic" {
     fn simd_ge<T, U>(x: T, y: T) -> U;
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
 
diff --git a/src/test/compile-fail/simd-intrinsic-generic-elements.rs b/src/test/compile-fail/simd-intrinsic-generic-elements.rs
index 1f4cc72ffe7..78f7d42e25a 100644
--- a/src/test/compile-fail/simd-intrinsic-generic-elements.rs
+++ b/src/test/compile-fail/simd-intrinsic-generic-elements.rs
@@ -56,7 +56,6 @@ extern "platform-intrinsic" {
     fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
 }
 
-#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls.
 fn main() {
     let x = i32x4(0, 0, 0, 0);
 
diff --git a/src/test/debuginfo/associated-types.rs b/src/test/debuginfo/associated-types.rs
index ebaad663bb4..8615c8a7ef6 100644
--- a/src/test/debuginfo/associated-types.rs
+++ b/src/test/debuginfo/associated-types.rs
@@ -80,7 +80,7 @@
 
 #![allow(unused_variables)]
 #![allow(dead_code)]
-#![feature(omit_gdb_pretty_printer_section)]
+#![feature(omit_gdb_pretty_printer_section, rustc_attrs)]
 #![omit_gdb_pretty_printer_section]
 
 trait TraitWithAssocType {
@@ -127,6 +127,7 @@ fn assoc_tuple<T: TraitWithAssocType>(arg: (T, T::Type)) {
     zzz(); // #break
 }
 
+#[rustc_no_mir] // FIXME(#32790) MIR reuses scopes for match arms.
 fn assoc_enum<T: TraitWithAssocType>(arg: Enum<T>) {
 
     match arg {
diff --git a/src/test/debuginfo/c-style-enum.rs b/src/test/debuginfo/c-style-enum.rs
index 4eec26d335c..dbd336d2dc6 100644
--- a/src/test/debuginfo/c-style-enum.rs
+++ b/src/test/debuginfo/c-style-enum.rs
@@ -157,7 +157,8 @@ fn main() {
 
     zzz(); // #break
 
-    let a = SINGLE_VARIANT;
+    // Borrow to avoid an eager load of the constant value in the static.
+    let a = &SINGLE_VARIANT;
     let a = unsafe { AUTO_ONE };
     let a = unsafe { MANUAL_ONE };
 }
diff --git a/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs b/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
index b5b6ca75727..e90f7d649f5 100644
--- a/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
+++ b/src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
@@ -247,10 +247,11 @@
 // lldb-command:continue
 
 #![allow(dead_code, unused_assignments, unused_variables)]
-#![feature(omit_gdb_pretty_printer_section)]
+#![feature(omit_gdb_pretty_printer_section, rustc_attrs)]
 #![omit_gdb_pretty_printer_section]
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn immediate_args(a: isize, b: bool, c: f64) {
     println!("");
 }
@@ -267,43 +268,51 @@ struct BigStruct {
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn non_immediate_args(a: BigStruct, b: BigStruct) {
     println!("");
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn binding(a: i64, b: u64, c: f64) {
     let x = 0;
     println!("");
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn assignment(mut a: u64, b: u64, c: f64) {
     a = b;
     println!("");
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn function_call(x: u64, y: u64, z: f64) {
     println!("Hi!")
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn identifier(x: u64, y: u64, z: f64) -> u64 {
     x
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn return_expr(x: u64, y: u64, z: f64) -> u64 {
     return x;
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 {
     x + y
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn if_expr(x: u64, y: u64, z: f64) -> u64 {
     if x + y < 1000 {
         x
@@ -313,6 +322,7 @@ fn if_expr(x: u64, y: u64, z: f64) -> u64 {
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
     while x + y < 1000 {
         x += z
@@ -321,6 +331,7 @@ fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
 }
 
 #[no_stack_check]
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
 fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 {
     loop {
         x += z;
diff --git a/src/test/debuginfo/no-debug-attribute.rs b/src/test/debuginfo/no-debug-attribute.rs
index 6bdd68d5e26..ea237e5970c 100644
--- a/src/test/debuginfo/no-debug-attribute.rs
+++ b/src/test/debuginfo/no-debug-attribute.rs
@@ -23,10 +23,11 @@
 // gdb-command:continue
 
 #![allow(unused_variables)]
-#![feature(no_debug)]
+#![feature(no_debug, rustc_attrs)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
 
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is inaccurate for returns.
 fn function_with_debuginfo() {
     let abc = 10_usize;
     return (); // #break
diff --git a/src/test/debuginfo/var-captured-in-nested-closure.rs b/src/test/debuginfo/var-captured-in-nested-closure.rs
index 7090377e5db..d2af828a890 100644
--- a/src/test/debuginfo/var-captured-in-nested-closure.rs
+++ b/src/test/debuginfo/var-captured-in-nested-closure.rs
@@ -78,7 +78,7 @@
 // lldb-command:continue
 
 #![allow(unused_variables)]
-#![feature(box_syntax)]
+#![feature(box_syntax, rustc_attrs, stmt_expr_attributes)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
 
@@ -88,6 +88,7 @@ struct Struct {
     c: usize
 }
 
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
 fn main() {
     let mut variable = 1;
     let constant = 2;
@@ -101,10 +102,14 @@ fn main() {
     let struct_ref = &a_struct;
     let owned: Box<_> = box 6;
 
-    let mut closure = || {
+    let mut closure =
+    #[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
+    || {
         let closure_local = 8;
 
-        let mut nested_closure = || {
+        let mut nested_closure =
+        #[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
+        || {
             zzz(); // #break
             variable = constant + a_struct.a + struct_ref.a + *owned + closure_local;
         };
diff --git a/src/test/debuginfo/var-captured-in-sendable-closure.rs b/src/test/debuginfo/var-captured-in-sendable-closure.rs
index aa269edadd8..26d46040c20 100644
--- a/src/test/debuginfo/var-captured-in-sendable-closure.rs
+++ b/src/test/debuginfo/var-captured-in-sendable-closure.rs
@@ -40,7 +40,7 @@
 // lldb-check:[...]$2 = 5
 
 #![allow(unused_variables)]
-#![feature(unboxed_closures, box_syntax)]
+#![feature(unboxed_closures, box_syntax, rustc_attrs, stmt_expr_attributes)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
 
@@ -50,6 +50,7 @@ struct Struct {
     c: usize
 }
 
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
 fn main() {
     let constant = 1;
 
@@ -61,7 +62,9 @@ fn main() {
 
     let owned: Box<_> = box 5;
 
-    let closure = move || {
+    let closure =
+    #[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
+    move || {
         zzz(); // #break
         do_something(&constant, &a_struct.a, &*owned);
     };
@@ -73,7 +76,9 @@ fn main() {
     // The `self` argument of the following closure should be passed by value
     // to FnOnce::call_once(self, args), which gets translated a bit differently
     // than the regular case. Let's make sure this is supported too.
-    let immedate_env = move || {
+    let immedate_env =
+    #[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
+    move || {
         zzz(); // #break
         return constant2;
     };
diff --git a/src/test/debuginfo/var-captured-in-stack-closure.rs b/src/test/debuginfo/var-captured-in-stack-closure.rs
index 6def5cf2859..a22fbebfd1a 100644
--- a/src/test/debuginfo/var-captured-in-stack-closure.rs
+++ b/src/test/debuginfo/var-captured-in-stack-closure.rs
@@ -69,7 +69,7 @@
 // lldb-command:print *owned
 // lldb-check:[...]$9 = 6
 
-#![feature(unboxed_closures, box_syntax)]
+#![feature(unboxed_closures, box_syntax, rustc_attrs, stmt_expr_attributes)]
 #![allow(unused_variables)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
@@ -80,6 +80,7 @@ struct Struct {
     c: usize
 }
 
+#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
 fn main() {
     let mut variable = 1;
     let constant = 2;
@@ -94,7 +95,9 @@ fn main() {
     let owned: Box<_> = box 6;
 
     {
-        let mut first_closure = || {
+        let mut first_closure =
+        #[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
+        || {
             zzz(); // #break
             variable = constant + a_struct.a + struct_ref.a + *owned;
         };
@@ -103,7 +106,9 @@ fn main() {
     }
 
     {
-        let mut second_closure = || {
+        let mut second_closure =
+        #[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
+        || {
             zzz(); // #break
             variable = constant + a_struct.a + struct_ref.a + *owned;
         };
diff --git a/src/test/run-pass/backtrace-debuginfo-aux.rs b/src/test/run-pass/backtrace-debuginfo-aux.rs
index b80c938fed5..48df600214a 100644
--- a/src/test/run-pass/backtrace-debuginfo-aux.rs
+++ b/src/test/run-pass/backtrace-debuginfo-aux.rs
@@ -11,7 +11,6 @@
 // ignore-test: not a test, used by backtrace-debuginfo.rs to test file!()
 
 #[inline(never)]
-#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) {
     f((file!(), line!()))
 }
@@ -21,7 +20,6 @@ pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) {
 // this case.
 #[cfg_attr(not(target_env = "msvc"), inline(always))]
 #[cfg_attr(target_env = "msvc", inline(never))]
-#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 pub fn callback_inlined<F>(f: F) where F: FnOnce((&'static str, u32)) {
     f((file!(), line!()))
 }
diff --git a/src/test/run-pass/backtrace-debuginfo.rs b/src/test/run-pass/backtrace-debuginfo.rs
index fd1c0172339..8b2b2694882 100644
--- a/src/test/run-pass/backtrace-debuginfo.rs
+++ b/src/test/run-pass/backtrace-debuginfo.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
-
 // We disable tail merging here because it can't preserve debuginfo and thus
 // potentially breaks the backtraces. Also, subtle changes can decide whether
 // tail merging suceeds, so the test might work today but fail tomorrow due to a
@@ -74,7 +72,6 @@ fn dump_filelines(filelines: &[Pos]) {
 }
 
 #[inline(never)]
-#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
     check!(counter; main_pos, outer_pos);
     check!(counter; main_pos, outer_pos);
@@ -91,7 +88,6 @@ fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
 // this case.
 #[cfg_attr(not(target_env = "msvc"), inline(always))]
 #[cfg_attr(target_env = "msvc", inline(never))]
-#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
     check!(counter; main_pos, outer_pos);
     check!(counter; main_pos, outer_pos);
@@ -117,7 +113,6 @@ fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
 }
 
 #[inline(never)]
-#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn outer(mut counter: i32, main_pos: Pos) {
     inner(&mut counter, main_pos, pos!());
     inner_inlined(&mut counter, main_pos, pos!());
@@ -162,7 +157,6 @@ fn run_test(me: &str) {
 }
 
 #[inline(never)]
-#[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
 fn main() {
     let args: Vec<String> = env::args().collect();
     if args.len() >= 2 {
diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs
index a2108ff041d..5b364358a59 100644
--- a/src/test/run-pass/backtrace.rs
+++ b/src/test/run-pass/backtrace.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
-
 // no-pretty-expanded FIXME #15189
 // ignore-android FIXME #17520
 // compile-flags:-g
@@ -18,8 +16,6 @@ use std::env;
 use std::process::{Command, Stdio};
 use std::str;
 
-// FIXME #31005 MIR missing debuginfo currently.
-#[cfg_attr(target_env = "msvc", rustc_no_mir)]
 #[inline(never)]
 fn foo() {
     let _v = vec![1, 2, 3];
@@ -28,8 +24,6 @@ fn foo() {
     }
 }
 
-// FIXME #31005 MIR missing debuginfo currently.
-#[cfg_attr(target_env = "msvc", rustc_no_mir)]
 #[inline(never)]
 fn double() {
     struct Double;