about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2018-05-16 18:58:54 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2019-11-27 19:22:03 +0200
commit563ed27c01c204d734355709c905f9a14246d4ff (patch)
tree8609414c35f238977d85651ca531d6599eec7298
parent876a72a251e0d533f776fa9149b3e4daaeea3a61 (diff)
downloadrust-563ed27c01c204d734355709c905f9a14246d4ff.tar.gz
rust-563ed27c01c204d734355709c905f9a14246d4ff.zip
rustc: move debug info from LocalDecl and UpvarDecl into a dedicated VarDebugInfo.
-rw-r--r--src/librustc/mir/mod.rs73
-rw-r--r--src/librustc/mir/visit.rs31
-rw-r--r--src/librustc_codegen_llvm/debuginfo/create_scope_map.rs6
-rw-r--r--src/librustc_codegen_llvm/debuginfo/metadata.rs100
-rw-r--r--src/librustc_codegen_ssa/mir/analyze.rs113
-rw-r--r--src/librustc_codegen_ssa/mir/debuginfo.rs139
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs4
-rw-r--r--src/librustc_mir/borrow_check/conflict_errors.rs66
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs8
-rw-r--r--src/librustc_mir/borrow_check/mod.rs26
-rw-r--r--src/librustc_mir/borrow_check/move_errors.rs4
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs12
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs26
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/outlives_suggestion.rs23
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs14
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs11
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs16
-rw-r--r--src/librustc_mir/build/expr/into.rs2
-rw-r--r--src/librustc_mir/build/matches/mod.rs18
-rw-r--r--src/librustc_mir/build/mod.rs186
-rw-r--r--src/librustc_mir/shim.rs6
-rw-r--r--src/librustc_mir/transform/const_prop.rs1
-rw-r--r--src/librustc_mir/transform/generator.rs18
-rw-r--r--src/librustc_mir/transform/inline.rs12
-rw-r--r--src/librustc_mir/transform/promote_consts.rs1
-rw-r--r--src/librustc_mir/transform/simplify.rs8
-rw-r--r--src/librustc_mir/transform/uniform_array_move_out.rs1
-rw-r--r--src/librustc_mir/util/def_use.rs41
-rw-r--r--src/librustc_mir/util/graphviz.rs14
-rw-r--r--src/librustc_mir/util/liveness.rs3
-rw-r--r--src/librustc_mir/util/pretty.rs26
-rw-r--r--src/test/incremental/hashes/for_loops.rs2
-rw-r--r--src/test/incremental/hashes/let_expressions.rs8
-rw-r--r--src/test/incremental/hashes/loop_expressions.rs2
-rw-r--r--src/test/incremental/hashes/while_loops.rs4
-rw-r--r--src/test/mir-opt/box_expr.rs1
-rw-r--r--src/test/mir-opt/generator-storage-dead-unwind.rs2
-rw-r--r--src/test/mir-opt/inline-closure-borrows-arg.rs56
-rw-r--r--src/test/mir-opt/inline-closure-captures.rs60
-rw-r--r--src/test/mir-opt/inline-closure.rs52
-rw-r--r--src/test/mir-opt/issue-41110.rs5
-rw-r--r--src/test/mir-opt/issue-41888.rs4
-rw-r--r--src/test/mir-opt/issue-49232.rs1
-rw-r--r--src/test/mir-opt/match-arm-scopes.rs6
-rw-r--r--src/test/mir-opt/nll/region-subtyping-basic.rs4
-rw-r--r--src/test/mir-opt/packed-struct-drop-aligned.rs1
-rw-r--r--src/test/mir-opt/simplify_try.rs30
49 files changed, 780 insertions, 480 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index bd793fd07bf..4103d25fbd1 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -141,14 +141,8 @@ pub struct Body<'tcx> {
     /// This is used for the "rust-call" ABI.
     pub spread_arg: Option<Local>,
 
-    /// Names and capture modes of all the closure upvars, assuming
-    /// the first argument is either the closure or a reference to it.
-    //
-    // NOTE(eddyb) This is *strictly* a temporary hack for codegen
-    // debuginfo generation, and will be removed at some point.
-    // Do **NOT** use it for anything else; upvar information should not be
-    // in the MIR, so please rely on local crate HIR or other side-channels.
-    pub __upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
+    /// Debug information pertaining to user variables, including captures.
+    pub var_debug_info: Vec<VarDebugInfo<'tcx>>,
 
     /// Mark this MIR of a const context other than const functions as having converted a `&&` or
     /// `||` expression into `&` or `|` respectively. This is problematic because if we ever stop
@@ -170,11 +164,10 @@ impl<'tcx> Body<'tcx> {
         basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
         source_scopes: IndexVec<SourceScope, SourceScopeData>,
         source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
-        yield_ty: Option<Ty<'tcx>>,
         local_decls: LocalDecls<'tcx>,
         user_type_annotations: CanonicalUserTypeAnnotations<'tcx>,
         arg_count: usize,
-        __upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
+        var_debug_info: Vec<VarDebugInfo<'tcx>>,
         span: Span,
         control_flow_destroyed: Vec<(Span, String)>,
     ) -> Self {
@@ -191,14 +184,14 @@ impl<'tcx> Body<'tcx> {
             basic_blocks,
             source_scopes,
             source_scope_local_data,
-            yield_ty,
+            yield_ty: None,
             generator_drop: None,
             generator_layout: None,
             local_decls,
             user_type_annotations,
             arg_count,
-            __upvar_debuginfo_codegen_only_do_not_use,
             spread_arg: None,
+            var_debug_info,
             span,
             cache: cache::Cache::new(),
             control_flow_destroyed,
@@ -280,7 +273,7 @@ impl<'tcx> Body<'tcx> {
             LocalKind::ReturnPointer
         } else if index < self.arg_count + 1 {
             LocalKind::Arg
-        } else if self.local_decls[local].name.is_some() {
+        } else if self.local_decls[local].is_user_variable() {
             LocalKind::Var
         } else {
             LocalKind::Temp
@@ -728,12 +721,6 @@ pub struct LocalDecl<'tcx> {
     // FIXME(matthewjasper) Don't store in this in `Body`
     pub user_ty: UserTypeProjections,
 
-    /// The name of the local, used in debuginfo and pretty-printing.
-    ///
-    /// Note that function arguments can also have this set to `Some(_)`
-    /// to generate better debuginfo.
-    pub name: Option<Name>,
-
     /// The *syntactic* (i.e., not visibility) source scope the local is defined
     /// in. If the local was defined in a let-statement, this
     /// is *within* the let-statement, rather than outside
@@ -785,9 +772,9 @@ pub struct LocalDecl<'tcx> {
     /// `drop(x)`, we want it to refer to `x: u32`.
     ///
     /// To allow both uses to work, we need to have more than a single scope
-    /// for a local. We have the `source_info.scope` represent the
-    /// "syntactic" lint scope (with a variable being under its let
-    /// block) while the `visibility_scope` represents the "local variable"
+    /// for a local. We have the `source_info.scope` represent the "syntactic"
+    /// lint scope (with a variable being under its let block) while the
+    /// `var_debug_info.source_info.scope` represents the "local variable"
     /// scope (where the "rest" of a block is under all prior let-statements).
     ///
     /// The end result looks like this:
@@ -806,18 +793,14 @@ pub struct LocalDecl<'tcx> {
     ///  │ │
     ///  │ │ │{ let y: u32 }
     ///  │ │ │
-    ///  │ │ │← y.visibility_scope
+    ///  │ │ │← y.var_debug_info.source_info.scope
     ///  │ │ │← `y + 2`
     ///  │
     ///  │ │{ let x: u32 }
-    ///  │ │← x.visibility_scope
+    ///  │ │← x.var_debug_info.source_info.scope
     ///  │ │← `drop(x)` // This accesses `x: u32`.
     /// ```
     pub source_info: SourceInfo,
-
-    /// Source scope within which the local is visible (for debuginfo)
-    /// (see `source_info` for more details).
-    pub visibility_scope: SourceScope,
 }
 
 /// Extra information about a local that's used for diagnostics.
@@ -955,9 +938,7 @@ impl<'tcx> LocalDecl<'tcx> {
             mutability,
             ty,
             user_ty: UserTypeProjections::none(),
-            name: None,
             source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE },
-            visibility_scope: OUTERMOST_SOURCE_SCOPE,
             internal,
             local_info: LocalInfo::Other,
             is_block_tail: None,
@@ -974,22 +955,27 @@ impl<'tcx> LocalDecl<'tcx> {
             ty: return_ty,
             user_ty: UserTypeProjections::none(),
             source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE },
-            visibility_scope: OUTERMOST_SOURCE_SCOPE,
             internal: false,
             is_block_tail: None,
-            name: None, // FIXME maybe we do want some name here?
             local_info: LocalInfo::Other,
         }
     }
 }
 
-/// A closure capture, with its name and mode.
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
-pub struct UpvarDebuginfo {
-    pub debug_name: Name,
+/// Debug information pertaining to a user variable.
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
+pub struct VarDebugInfo<'tcx> {
+    pub name: Name,
 
-    /// If true, the capture is behind a reference.
-    pub by_ref: bool,
+    /// Source info of the user variable, including the scope
+    /// within which the variable is visible (to debuginfo)
+    /// (see `LocalDecl`'s `source_info` field for more details).
+    pub source_info: SourceInfo,
+
+    /// Where the data for this user variable is to be found.
+    /// NOTE(eddyb) There's an unenforced invariant that this `Place` is
+    /// based on a `Local`, not a `Static`, and contains no indexing.
+    pub place: Place<'tcx>,
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -2758,16 +2744,6 @@ pub struct GeneratorLayout<'tcx> {
     /// have conflicts with each other are allowed to overlap in the computed
     /// layout.
     pub storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
-
-    /// The names and scopes of all the stored generator locals.
-    ///
-    /// N.B., this is *strictly* a temporary hack for codegen
-    /// debuginfo generation, and will be removed at some point.
-    /// Do **NOT** use it for anything else, local information should not be
-    /// in the MIR, please rely on local crate HIR or other side-channels.
-    //
-    // FIXME(tmandry): see above.
-    pub __local_debuginfo_codegen_only_do_not_use: IndexVec<GeneratorSavedLocal, LocalDecl<'tcx>>,
 }
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
@@ -2946,7 +2922,6 @@ CloneTypeFoldableAndLiftImpls! {
     MirPhase,
     Mutability,
     SourceInfo,
-    UpvarDebuginfo,
     FakeReadCause,
     RetagKind,
     SourceScope,
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index fc0e77aab43..464d4c74366 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -221,6 +221,11 @@ macro_rules! make_mir_visitor {
                 self.super_local_decl(local, local_decl);
             }
 
+            fn visit_var_debug_info(&mut self,
+                                    var_debug_info: & $($mutability)* VarDebugInfo<'tcx>) {
+                self.super_var_debug_info(var_debug_info);
+            }
+
             fn visit_local(&mut self,
                             _local: & $($mutability)? Local,
                             _context: PlaceContext,
@@ -279,6 +284,10 @@ macro_rules! make_mir_visitor {
                     );
                 }
 
+                for var_debug_info in &$($mutability)? body.var_debug_info {
+                    self.visit_var_debug_info(var_debug_info);
+                }
+
                 self.visit_span(&$($mutability)? body.span);
             }
 
@@ -687,9 +696,7 @@ macro_rules! make_mir_visitor {
                     mutability: _,
                     ty,
                     user_ty,
-                    name: _,
                     source_info,
-                    visibility_scope,
                     internal: _,
                     local_info: _,
                     is_block_tail: _,
@@ -703,7 +710,23 @@ macro_rules! make_mir_visitor {
                     self.visit_user_type_projection(user_ty);
                 }
                 self.visit_source_info(source_info);
-                self.visit_source_scope(visibility_scope);
+            }
+
+            fn super_var_debug_info(&mut self,
+                                    var_debug_info: & $($mutability)? VarDebugInfo<'tcx>) {
+                let VarDebugInfo {
+                    name: _,
+                    source_info,
+                    place,
+                } = var_debug_info;
+
+                self.visit_source_info(source_info);
+                let location = START_BLOCK.start_location();
+                self.visit_place(
+                    place,
+                    PlaceContext::NonUse(NonUseContext::VarDebugInfo),
+                    location,
+                );
             }
 
             fn super_source_scope(&mut self,
@@ -1029,6 +1052,8 @@ pub enum NonUseContext {
     StorageDead,
     /// User type annotation assertions for NLL.
     AscribeUserTy,
+    /// The data of an user variable, for debug info.
+    VarDebugInfo,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
index 6ee76b71fce..91d5a22b022 100644
--- a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
+++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
@@ -23,12 +23,10 @@ pub fn compute_mir_scopes(
 ) {
     // Find all the scopes with variables defined in them.
     let mut has_variables = BitSet::new_empty(mir.source_scopes.len());
-    // FIXME(eddyb) base this on `decl.name`, or even better, on debuginfo.
     // FIXME(eddyb) take into account that arguments always have debuginfo,
     // irrespective of their name (assuming full debuginfo is enabled).
-    for var in mir.vars_iter() {
-        let decl = &mir.local_decls[var];
-        has_variables.insert(decl.visibility_scope);
+    for var_debug_info in &mir.var_debug_info {
+        has_variables.insert(var_debug_info.source_info.scope);
     }
 
     // Instantiate all scopes.
diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs
index 2e16ae4c73f..1847e4e9fa9 100644
--- a/src/librustc_codegen_llvm/debuginfo/metadata.rs
+++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -17,13 +17,13 @@ use crate::llvm_util;
 use crate::value::Value;
 
 use rustc_codegen_ssa::traits::*;
+use rustc_index::vec::{Idx, IndexVec};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc::hir::CodegenFnAttrFlags;
 use rustc::hir::def::CtorKind;
 use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
 use rustc::ich::NodeIdHashingMode;
-use rustc::mir::Field;
-use rustc::mir::GeneratorLayout;
+use rustc::mir::{self, Field, GeneratorLayout};
 use rustc::mir::interpret::truncate;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc::ty::Instance;
@@ -1316,6 +1316,45 @@ fn use_enum_fallback(cx: &CodegenCx<'_, '_>) -> bool {
         || llvm_util::get_major_version() < 8;
 }
 
+// FIXME(eddyb) maybe precompute this? Right now it's computed once
+// per generator monomorphization, but it doesn't depend on substs.
+fn generator_layout_and_saved_local_names(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+) -> (&'tcx GeneratorLayout<'tcx>, IndexVec<mir::GeneratorSavedLocal, Option<ast::Name>>) {
+    let body = tcx.optimized_mir(def_id);
+    let generator_layout = body.generator_layout.as_ref().unwrap();
+    let mut generator_saved_local_names =
+        IndexVec::from_elem(None, &generator_layout.field_tys);
+
+    let state_arg = mir::PlaceBase::Local(mir::Local::new(1));
+    for var in &body.var_debug_info {
+        if var.place.base != state_arg {
+            continue;
+        }
+        match var.place.projection[..] {
+            [
+                // Deref of the `Pin<&mut Self>` state argument.
+                mir::ProjectionElem::Field(..),
+                mir::ProjectionElem::Deref,
+
+                // Field of a variant of the state.
+                mir::ProjectionElem::Downcast(_, variant),
+                mir::ProjectionElem::Field(field, _),
+            ] => {
+                let name = &mut generator_saved_local_names[
+                    generator_layout.variant_fields[variant][field]
+                ];
+                if name.is_none() {
+                    name.replace(var.name);
+                }
+            }
+            _ => {}
+        }
+    }
+    (generator_layout, generator_saved_local_names)
+}
+
 /// Describes the members of an enum value; an enum is described as a union of
 /// structs in DWARF. This `MemberDescriptionFactory` provides the description for
 /// the members of this union; so for every variant of the given enum, this
@@ -1332,12 +1371,25 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> {
 impl EnumMemberDescriptionFactory<'ll, 'tcx> {
     fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>)
                                   -> Vec<MemberDescription<'ll>> {
+        let generator_variant_info_data = match self.enum_type.kind {
+            ty::Generator(def_id, ..) => {
+                Some(generator_layout_and_saved_local_names(cx.tcx, def_id))
+            }
+            _ => None,
+        };
+
         let variant_info_for = |index: VariantIdx| {
-            match &self.enum_type.kind {
+            match self.enum_type.kind {
                 ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]),
-                ty::Generator(def_id, substs, _) => {
-                    let generator_layout = cx.tcx.generator_layout(*def_id);
-                    VariantInfo::Generator(substs, generator_layout, index)
+                ty::Generator(_, substs, _) => {
+                    let (generator_layout, generator_saved_local_names) =
+                        generator_variant_info_data.as_ref().unwrap();
+                    VariantInfo::Generator {
+                        substs,
+                        generator_layout: *generator_layout,
+                        generator_saved_local_names,
+                        variant_index: index,
+                    }
                 }
                 _ => bug!(),
             }
@@ -1608,16 +1660,21 @@ enum EnumDiscriminantInfo<'ll> {
 }
 
 #[derive(Copy, Clone)]
-enum VariantInfo<'tcx> {
+enum VariantInfo<'a, 'tcx> {
     Adt(&'tcx ty::VariantDef),
-    Generator(SubstsRef<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx),
+    Generator {
+        substs: SubstsRef<'tcx>,
+        generator_layout: &'tcx GeneratorLayout<'tcx>,
+        generator_saved_local_names: &'a IndexVec<mir::GeneratorSavedLocal, Option<ast::Name>>,
+        variant_index: VariantIdx,
+    },
 }
 
-impl<'tcx> VariantInfo<'tcx> {
+impl<'tcx> VariantInfo<'_, 'tcx> {
     fn map_struct_name<R>(&self, f: impl FnOnce(&str) -> R) -> R {
         match self {
             VariantInfo::Adt(variant) => f(&variant.ident.as_str()),
-            VariantInfo::Generator(substs, _, variant_index) =>
+            VariantInfo::Generator { substs, variant_index, .. } =>
                 f(&substs.as_generator().variant_name(*variant_index)),
         }
     }
@@ -1625,7 +1682,7 @@ impl<'tcx> VariantInfo<'tcx> {
     fn variant_name(&self) -> String {
         match self {
             VariantInfo::Adt(variant) => variant.ident.to_string(),
-            VariantInfo::Generator(_, _, variant_index) => {
+            VariantInfo::Generator { variant_index, .. } => {
                 // Since GDB currently prints out the raw discriminant along
                 // with every variant, make each variant name be just the value
                 // of the discriminant. The struct name for the variant includes
@@ -1636,17 +1693,20 @@ impl<'tcx> VariantInfo<'tcx> {
     }
 
     fn field_name(&self, i: usize) -> String {
-        let field_name = match self {
+        let field_name = match *self {
             VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn =>
-                Some(variant.fields[i].ident.to_string()),
-            VariantInfo::Generator(_, generator_layout, variant_index) => {
-                let field = generator_layout.variant_fields[*variant_index][i.into()];
-                let decl = &generator_layout.__local_debuginfo_codegen_only_do_not_use[field];
-                decl.name.map(|name| name.to_string())
-            }
+                Some(variant.fields[i].ident.name),
+            VariantInfo::Generator {
+                generator_layout,
+                generator_saved_local_names,
+                variant_index,
+                ..
+            } => generator_saved_local_names[
+                generator_layout.variant_fields[variant_index][i.into()]
+            ],
             _ => None,
         };
-        field_name.unwrap_or_else(|| format!("__{}", i))
+        field_name.map(|name| name.to_string()).unwrap_or_else(|| format!("__{}", i))
     }
 }
 
@@ -1657,7 +1717,7 @@ impl<'tcx> VariantInfo<'tcx> {
 fn describe_enum_variant(
     cx: &CodegenCx<'ll, 'tcx>,
     layout: layout::TyLayout<'tcx>,
-    variant: VariantInfo<'tcx>,
+    variant: VariantInfo<'_, 'tcx>,
     discriminant_info: EnumDiscriminantInfo<'ll>,
     containing_scope: &'ll DIScope,
     span: Span,
diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs
index 2e5dc3db31a..e44551fcbcd 100644
--- a/src/librustc_codegen_ssa/mir/analyze.rs
+++ b/src/librustc_codegen_ssa/mir/analyze.rs
@@ -5,7 +5,9 @@ use rustc_index::bit_set::BitSet;
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc::mir::{self, Location, TerminatorKind};
-use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
+use rustc::mir::visit::{
+    Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext, NonUseContext,
+};
 use rustc::mir::traversal;
 use rustc::session::config::DebugInfo;
 use rustc::ty;
@@ -27,7 +29,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
         // of putting everything in allocas just so we can use llvm.dbg.declare.
         if fx.cx.sess().opts.debuginfo == DebugInfo::Full {
-            if mir.local_kind(local) == mir::LocalKind::Arg || decl.name.is_some() {
+            if mir.local_kind(local) == mir::LocalKind::Arg {
                 analyzer.not_ssa(local);
                 continue;
             }
@@ -114,6 +116,12 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
         let cx = self.fx.cx;
 
         if let [proj_base @ .., elem] = place_ref.projection {
+            let mut base_context = if context.is_mutating_use() {
+                PlaceContext::MutatingUse(MutatingUseContext::Projection)
+            } else {
+                PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
+            };
+
             // Allow uses of projections that are ZSTs or from scalar fields.
             let is_consume = match context {
                 PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
@@ -145,47 +153,81 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
                         // Recurse with the same context, instead of `Projection`,
                         // potentially stopping at non-operand projections,
                         // which would trigger `not_ssa` on locals.
-                        self.process_place(
-                            &mir::PlaceRef {
-                                base: place_ref.base,
-                                projection: proj_base,
-                            },
-                            context,
-                            location,
-                        );
-                        return;
+                        base_context = context;
                     }
                 }
             }
 
-            // A deref projection only reads the pointer, never needs the place.
             if let mir::ProjectionElem::Deref = elem {
-                self.process_place(
-                    &mir::PlaceRef {
-                        base: place_ref.base,
-                        projection: proj_base,
-                    },
+                // Deref projections typically only read the pointer.
+                // (the exception being `VarDebugInfo` contexts, handled below)
+                base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
+
+                // Indirect debuginfo requires going through memory, that only
+                // the debugger accesses, following our emitted DWARF pointer ops.
+                //
+                // FIXME(eddyb) Investigate the possibility of relaxing this, but
+                // note that `llvm.dbg.declare` *must* be used for indirect places,
+                // even if we start using `llvm.dbg.value` for all other cases,
+                // as we don't necessarily know when the value changes, but only
+                // where it lives in memory.
+                //
+                // It's possible `llvm.dbg.declare` could support starting from
+                // a pointer that doesn't point to an `alloca`, but this would
+                // only be useful if we know the pointer being `Deref`'d comes
+                // from an immutable place, and if `llvm.dbg.declare` calls
+                // must be at the very start of the function, then only function
+                // arguments could contain such pointers.
+                if context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
+                    // We use `NonUseContext::VarDebugInfo` for the base,
+                    // which might not force the base local to memory,
+                    // so we have to do it manually.
+                    if let mir::PlaceBase::Local(local) = place_ref.base {
+                        self.visit_local(&local, context, location);
+                    }
+                }
+            }
+
+            // `NonUseContext::VarDebugInfo` needs to flow all the
+            // way down to the base local (see `visit_local`).
+            if context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
+                base_context = context;
+            }
+
+            self.process_place(
+                &mir::PlaceRef {
+                    base: place_ref.base,
+                    projection: proj_base,
+                },
+                base_context,
+                location
+            );
+            // HACK(eddyb) this emulates the old `visit_projection_elem`, this
+            // entire `visit_place`-like `process_place` method should be rewritten,
+            // now that we have moved to the "slice of projections" representation.
+            if let mir::ProjectionElem::Index(local) = elem {
+                self.visit_local(
+                    local,
                     PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                     location
                 );
-                return;
             }
-        }
+        } else {
+            // FIXME this is super_place code, is repeated here to avoid cloning place or changing
+            // visit_place API
+            let mut context = context;
 
-        // FIXME this is super_place code, is repeated here to avoid cloning place or changing
-        // visit_place API
-        let mut context = context;
+            if !place_ref.projection.is_empty() {
+                context = if context.is_mutating_use() {
+                    PlaceContext::MutatingUse(MutatingUseContext::Projection)
+                } else {
+                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
+                };
+            }
 
-        if !place_ref.projection.is_empty() {
-            context = if context.is_mutating_use() {
-                PlaceContext::MutatingUse(MutatingUseContext::Projection)
-            } else {
-                PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
-            };
+            self.visit_place_base(place_ref.base, context, location);
+            self.visit_projection(place_ref.base, place_ref.projection, context, location);
         }
-
-        self.visit_place_base(place_ref.base, context, location);
-        self.visit_projection(place_ref.base, place_ref.projection, context, location);
     }
 
 }
@@ -264,6 +306,15 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
                 self.assign(local, location);
             }
 
+            PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
+                // We need to keep locals in `alloca`s for debuginfo.
+                // FIXME(eddyb): We should figure out how to use `llvm.dbg.value` instead
+                // of putting everything in allocas just so we can use `llvm.dbg.declare`.
+                if self.fx.cx.sess().opts.debuginfo == DebugInfo::Full {
+                    self.not_ssa(local);
+                }
+            }
+
             PlaceContext::NonUse(_) |
             PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
 
diff --git a/src/librustc_codegen_ssa/mir/debuginfo.rs b/src/librustc_codegen_ssa/mir/debuginfo.rs
index c215db34ccb..bb2679e214d 100644
--- a/src/librustc_codegen_ssa/mir/debuginfo.rs
+++ b/src/librustc_codegen_ssa/mir/debuginfo.rs
@@ -1,12 +1,12 @@
-use rustc_index::vec::{Idx, IndexVec};
+use rustc_index::vec::IndexVec;
 use rustc::hir::def_id::CrateNum;
 use rustc::mir;
 use rustc::session::config::DebugInfo;
-use rustc::ty::{self, TyCtxt};
-use rustc::ty::layout::{LayoutOf, Size, VariantIdx};
+use rustc::ty::TyCtxt;
+use rustc::ty::layout::{LayoutOf, Size};
 use crate::traits::*;
 
-use syntax_pos::{BytePos, Span, Symbol};
+use syntax_pos::{BytePos, Span};
 use syntax::symbol::kw;
 
 use super::{FunctionCx, LocalRef};
@@ -113,7 +113,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             Some(per_local) => &per_local[local],
             None => return,
         };
-        let whole_local_var = vars.iter().find(|var| {
+        let whole_local_var = vars.iter().copied().find(|var| {
             var.place.projection.is_empty()
         });
         let has_proj = || vars.iter().any(|var| {
@@ -131,7 +131,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 // be offset to account for the hidden environment?
                 None
             } else {
-                Some(VarDebugInfo {
+                Some(mir::VarDebugInfo {
                     name: kw::Invalid,
                     source_info: self.mir.local_decls[local].source_info,
                     place: local.into(),
@@ -185,7 +185,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             _ => return,
         };
 
-        let vars = vars.iter().chain(if whole_local_var.is_none() {
+        let vars = vars.iter().copied().chain(if whole_local_var.is_none() {
             fallback_var.as_ref()
         } else {
             None
@@ -253,133 +253,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     }
 }
 
+/// Partition all `VarDebuginfo` in `body`, by their base `Local`.
 pub fn per_local_var_debug_info(
     tcx: TyCtxt<'tcx>,
-    body: &mir::Body<'tcx>,
-) -> Option<IndexVec<mir::Local, Vec<VarDebugInfo<'tcx>>>> {
+    body: &'a mir::Body<'tcx>,
+) -> Option<IndexVec<mir::Local, Vec<&'a mir::VarDebugInfo<'tcx>>>> {
     if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() {
         let mut per_local = IndexVec::from_elem(vec![], &body.local_decls);
-        for (local, decl) in body.local_decls.iter_enumerated() {
-            if let Some(name) = decl.name {
-                per_local[local].push(VarDebugInfo {
-                    name,
-                    source_info: mir::SourceInfo {
-                        span: decl.source_info.span,
-                        scope: decl.visibility_scope,
-                    },
-                    place: local.into(),
-                });
-            }
-        }
-
-        let upvar_debuginfo = &body.__upvar_debuginfo_codegen_only_do_not_use;
-        if !upvar_debuginfo.is_empty() {
-
-            let env_arg = mir::Local::new(1);
-            let mut env_projs = vec![];
-
-            let pin_did = tcx.lang_items().pin_type();
-            match body.local_decls[env_arg].ty.kind {
-                ty::RawPtr(_) |
-                ty::Ref(..)  => {
-                    env_projs.push(mir::ProjectionElem::Deref);
-                }
-                ty::Adt(def, substs) if Some(def.did) == pin_did => {
-                    if let ty::Ref(..) = substs.type_at(0).kind {
-                        env_projs.push(mir::ProjectionElem::Field(
-                            mir::Field::new(0),
-                            // HACK(eddyb) field types aren't used or needed here.
-                            tcx.types.err,
-                        ));
-                        env_projs.push(mir::ProjectionElem::Deref);
-                    }
-                }
-                _ => {}
-            }
-
-            let extra_locals = {
-                let upvars = upvar_debuginfo
-                    .iter()
-                    .enumerate()
-                    .map(|(i, upvar)| {
-                        let source_info = mir::SourceInfo {
-                            span: body.span,
-                            scope: mir::OUTERMOST_SOURCE_SCOPE,
-                        };
-                        (None, i, upvar.debug_name, upvar.by_ref, source_info)
-                    });
-
-                let generator_fields = body.generator_layout.as_ref().map(|generator_layout| {
-                    generator_layout.variant_fields.iter()
-                        .enumerate()
-                        .flat_map(move |(variant_idx, fields)| {
-                            let variant_idx = Some(VariantIdx::from(variant_idx));
-                            fields.iter()
-                                .enumerate()
-                                .filter_map(move |(i, field)| {
-                                    let decl = &generator_layout.
-                                        __local_debuginfo_codegen_only_do_not_use[*field];
-                                    if let Some(name) = decl.name {
-                                        let source_info = mir::SourceInfo {
-                                            span: decl.source_info.span,
-                                            scope: decl.visibility_scope,
-                                        };
-                                        Some((variant_idx, i, name, false, source_info))
-                                    } else {
-                                        None
-                                    }
-                            })
-                        })
-                }).into_iter().flatten();
-
-                upvars.chain(generator_fields)
-            };
-
-            for (variant_idx, field, name, by_ref, source_info) in extra_locals {
-                let mut projs = env_projs.clone();
-
-                if let Some(variant_idx) = variant_idx {
-                    projs.push(mir::ProjectionElem::Downcast(None, variant_idx));
-                }
-
-                projs.push(mir::ProjectionElem::Field(
-                    mir::Field::new(field),
-                    // HACK(eddyb) field types aren't used or needed here.
-                    tcx.types.err,
-                ));
-
-                if by_ref {
-                    projs.push(mir::ProjectionElem::Deref);
-                }
-
-                per_local[env_arg].push(VarDebugInfo {
-                    name,
-                    source_info,
-                    place: mir::Place {
-                        base: mir::PlaceBase::Local(env_arg),
-                        projection: tcx.intern_place_elems(&projs),
-                    },
-                });
+        for var in &body.var_debug_info {
+            if let mir::PlaceBase::Local(local) = var.place.base {
+                per_local[local].push(var);
             }
         }
-
         Some(per_local)
     } else {
         None
     }
 }
-
-/// Debug information relatating to an user variable.
-// FIXME(eddyb) move this to the MIR bodies themselves.
-#[derive(Clone)]
-pub struct VarDebugInfo<'tcx> {
-    pub name: Symbol,
-
-    /// Source info of the user variable, including the scope
-    /// within which the variable is visible (to debuginfo)
-    /// (see `LocalDecl`'s `source_info` field for more details).
-    pub source_info: mir::SourceInfo,
-
-    /// Where the data for this user variable is to be found.
-    pub place: mir::Place<'tcx>,
-}
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index fec31f07a34..6041232489d 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -74,7 +74,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     /// notably `expect`.
     locals: IndexVec<mir::Local, LocalRef<'tcx, Bx::Value>>,
 
-    per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<debuginfo::VarDebugInfo<'tcx>>>>,
+    /// All `VarDebuginfo` from the MIR body, partitioned by `Local`.
+    /// This is `None` if no variable debuginfo/names are needed.
+    per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<&'a mir::VarDebugInfo<'tcx>>>>,
 }
 
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
diff --git a/src/librustc_mir/borrow_check/conflict_errors.rs b/src/librustc_mir/borrow_check/conflict_errors.rs
index 9364bbedb0c..8508bf62d8f 100644
--- a/src/librustc_mir/borrow_check/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/conflict_errors.rs
@@ -308,7 +308,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             location,
             borrow,
             None,
-        ).add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", Some(borrow_span));
+        ).add_explanation_to_diagnostic(
+            self.infcx.tcx,
+            self.body,
+            &self.local_names,
+            &mut err,
+            "",
+            Some(borrow_span),
+        );
         err.buffer(&mut self.errors_buffer);
     }
 
@@ -343,7 +350,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         });
 
         self.explain_why_borrow_contains_point(location, borrow, None)
-            .add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+            .add_explanation_to_diagnostic(
+                self.infcx.tcx,
+                self.body,
+                &self.local_names,
+                &mut err,
+                "",
+                None,
+            );
         err
     }
 
@@ -561,6 +575,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         explanation.add_explanation_to_diagnostic(
             self.infcx.tcx,
             self.body,
+            &self.local_names,
             &mut err,
             first_borrow_desc,
             None,
@@ -947,6 +962,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 explanation.add_explanation_to_diagnostic(
                     self.infcx.tcx,
                     self.body,
+                    &self.local_names,
                     &mut err,
                     "",
                     None,
@@ -971,7 +987,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             );
 
             explanation.add_explanation_to_diagnostic(
-                self.infcx.tcx, self.body, &mut err, "", None);
+                self.infcx.tcx, self.body, &self.local_names, &mut err, "", None);
         }
 
         err
@@ -1029,7 +1045,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             _ => {}
         }
 
-        explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+        explanation.add_explanation_to_diagnostic(
+            self.infcx.tcx,
+            self.body,
+            &self.local_names,
+            &mut err,
+            "",
+            None,
+        );
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -1109,7 +1132,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
             _ => {}
         }
-        explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+        explanation.add_explanation_to_diagnostic(
+            self.infcx.tcx,
+            self.body,
+            &self.local_names,
+            &mut err,
+            "",
+            None,
+        );
 
         let within = if borrow_spans.for_generator() {
             " by generator"
@@ -1478,7 +1508,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         );
 
         self.explain_why_borrow_contains_point(location, loan, None)
-            .add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
+            .add_explanation_to_diagnostic(
+                self.infcx.tcx,
+                self.body,
+                &self.local_names,
+                &mut err,
+                "",
+                None,
+            );
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -1496,14 +1533,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         assigned_span: Span,
         err_place: &Place<'tcx>,
     ) {
-        let (from_arg, local_decl) = if let Some(local) = err_place.as_local() {
-            if let LocalKind::Arg = self.body.local_kind(local) {
-                (true, Some(&self.body.local_decls[local]))
-            } else {
-                (false, Some(&self.body.local_decls[local]))
-            }
-        } else {
-            (false, None)
+        let (from_arg, local_decl, local_name) = match err_place.as_local() {
+            Some(local) => (
+                self.body.local_kind(local) == LocalKind::Arg,
+                Some(&self.body.local_decls[local]),
+                self.local_names[local],
+            ),
+            None => (false, None, None),
         };
 
         // If root local is initialized immediately (everything apart from let
@@ -1553,7 +1589,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
         if let Some(decl) = local_decl {
-            if let Some(name) = decl.name {
+            if let Some(name) = local_name {
                 if decl.can_be_made_mutable() {
                     err.span_suggestion(
                         decl.source_info.span,
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 3835503b0ef..a555e0b74c2 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -331,10 +331,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
     /// a name, or its name was generated by the compiler, then `Err` is returned
-    fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
-        let local = &self.body.local_decls[local_index];
-        match local.name {
-            Some(name) if !local.from_compiler_desugaring() => {
+    fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
+        let decl = &self.body.local_decls[local];
+        match self.local_names[local] {
+            Some(name) if !decl.from_compiler_desugaring() => {
                 buf.push_str(&name.as_str());
                 Ok(())
             }
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 90e39286ec8..8a74e3a74ad 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -115,6 +115,20 @@ fn do_mir_borrowck<'a, 'tcx>(
         .as_local_hir_id(def_id)
         .expect("do_mir_borrowck: non-local DefId");
 
+    let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
+    for var_debug_info in &input_body.var_debug_info {
+        if let Some(local) = var_debug_info.place.as_local() {
+            if let Some(prev_name) = local_names[local] {
+                if var_debug_info.name != prev_name {
+                    span_bug!(var_debug_info.source_info.span,
+                        "local {:?} has many names (`{}` vs `{}`)",
+                        local, prev_name, var_debug_info.name);
+                }
+            }
+            local_names[local] = Some(var_debug_info.name);
+        }
+    }
+
     // Gather the upvars of a closure, if any.
     let tables = tcx.typeck_tables_of(def_id);
     let upvars: Vec<_> = tables
@@ -189,6 +203,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         free_regions,
         body,
         &promoted,
+        &local_names,
         &upvars,
         location_table,
         param_env,
@@ -264,6 +279,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         borrow_set,
         dominators,
         upvars,
+        local_names,
     };
 
     let mut state = Flows::new(
@@ -325,13 +341,8 @@ fn do_mir_borrowck<'a, 'tcx>(
         if let ClearCrossCrate::Set(ref vsi) = mbcx.body.source_scope_local_data {
             let local_decl = &mbcx.body.local_decls[local];
 
-            // Skip implicit `self` argument for closures
-            if local.index() == 1 && tcx.is_closure(mbcx.mir_def_id) {
-                continue;
-            }
-
             // Skip over locals that begin with an underscore or have no name
-            match local_decl.name {
+            match mbcx.local_names[local] {
                 Some(name) => if name.as_str().starts_with("_") {
                     continue;
                 },
@@ -463,6 +474,9 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
 
     /// Information about upvars not necessarily preserved in types or MIR
     upvars: Vec<Upvar>,
+
+    /// Names of local (user) variables (extracted from `var_debug_info`).
+    local_names: IndexVec<Local, Option<Name>>,
 }
 
 // Check that:
diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs
index c7cfda79b93..bf61eb9f0c5 100644
--- a/src/librustc_mir/borrow_check/move_errors.rs
+++ b/src/librustc_mir/borrow_check/move_errors.rs
@@ -322,7 +322,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             if decl.is_ref_for_guard() {
                 let mut err = self.cannot_move_out_of(
                     span,
-                    &format!("`{}` in pattern guard", decl.name.unwrap()),
+                    &format!("`{}` in pattern guard", self.local_names[*local].unwrap()),
                 );
                 err.note(
                     "variables bound in patterns cannot be moved from \
@@ -571,7 +571,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             if binds_to.len() == 1 {
                 self.note_type_does_not_implement_copy(
                     err,
-                    &format!("`{}`", bind_to.name.unwrap()),
+                    &format!("`{}`", self.local_names[*local].unwrap()),
                     bind_to.ty,
                     Some(binding_span)
                 );
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index 404684c07a0..bf070c3f07d 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -50,8 +50,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 if access_place.as_local().is_some() {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
-                    let name = self.body.local_decls[*local]
-                        .name
+                    let name = self.local_names[*local]
                         .expect("immutable unnamed local");
                     reason = format!(", as `{}` is not declared as mutable", name);
                 }
@@ -253,7 +252,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         // Deliberately fall into this case for all implicit self types,
                         // so that we don't fall in to the next case with them.
                         kind == mir::ImplicitSelfKind::MutRef
-                    } else if Some(kw::SelfLower) == local_decl.name {
+                    } else if Some(kw::SelfLower) == self.local_names[*local] {
                         // Otherwise, check if the name is the self kewyord - in which case
                         // we have an explicit self. Do the same thing in this case and check
                         // for a `self: &mut Self` to suggest removing the `&mut`.
@@ -290,7 +289,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 err.span_suggestion(
                     local_decl.source_info.span,
                     "consider changing this to be mutable",
-                    format!("mut {}", local_decl.name.unwrap()),
+                    format!("mut {}", self.local_names[*local].unwrap()),
                     Applicability::MachineApplicable,
                 );
             }
@@ -415,7 +414,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     );
                 }
 
-                match local_decl.name {
+                match self.local_names[*local] {
                     Some(name) if !local_decl.from_compiler_desugaring() => {
                         err.span_label(
                             span,
diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
index 26bead3047d..c7058531958 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
@@ -11,9 +11,11 @@ use rustc::mir::{
 };
 use rustc::ty::{self, TyCtxt};
 use rustc::ty::adjustment::{PointerCast};
+use rustc_index::vec::IndexVec;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::DiagnosticBuilder;
 use syntax_pos::Span;
+use syntax_pos::symbol::Symbol;
 
 mod find_use;
 
@@ -56,6 +58,7 @@ impl BorrowExplanation {
         &self,
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         err: &mut DiagnosticBuilder<'_>,
         borrow_desc: &str,
         borrow_span: Option<Span>,
@@ -112,7 +115,7 @@ impl BorrowExplanation {
                     _ => ("destructor", format!("type `{}`", local_decl.ty)),
                 };
 
-                match local_decl.name {
+                match local_names[dropped_local] {
                     Some(local_name) if !local_decl.from_compiler_desugaring() => {
                         let message = format!(
                             "{B}borrow might be used here, when `{LOC}` is dropped \
@@ -271,10 +274,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
             Some(Cause::DropVar(local, location)) => {
                 let mut should_note_order = false;
-                if body.local_decls[local].name.is_some() {
+                if self.local_names[local].is_some() {
                     if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
                         if let Some(borrowed_local) = place.as_local() {
-                             if body.local_decls[borrowed_local].name.is_some()
+                            if self.local_names[borrowed_local].is_some()
                                 && local != borrowed_local
                             {
                                 should_note_order = true;
@@ -295,6 +298,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     let (category, from_closure, span, region_name) =
                         self.nonlexical_regioncx.free_region_constraint_info(
                             self.body,
+                        &self.local_names,
                         &self.upvars,
                             self.mir_def_id,
                             self.infcx,
@@ -495,7 +499,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             Operand::Move(place) => {
                                 if let Some(l) = place.as_local() {
                                     let local_decl = &self.body.local_decls[l];
-                                    if local_decl.name.is_none() {
+                                    if self.local_names[l].is_none() {
                                         local_decl.source_info.span
                                     } else {
                                         span
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index b2e5751b902..4d67b72c98c 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -16,6 +16,7 @@ use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements,
 use rustc::ty::{self, RegionKind, RegionVid};
 use rustc_index::vec::IndexVec;
 use rustc_errors::Diagnostic;
+use syntax_pos::symbol::Symbol;
 use std::fmt::Debug;
 use std::env;
 use std::io;
@@ -158,6 +159,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
     universal_regions: UniversalRegions<'tcx>,
     body: &Body<'tcx>,
     promoted: &IndexVec<Promoted, Body<'tcx>>,
+    local_names: &IndexVec<Local, Option<Symbol>>,
     upvars: &[Upvar],
     location_table: &LocationTable,
     param_env: ty::ParamEnv<'tcx>,
@@ -281,7 +283,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
 
     // Solve the region constraints.
     let closure_region_requirements =
-        regioncx.solve(infcx, &body, upvars, def_id, errors_buffer);
+        regioncx.solve(infcx, body, local_names, upvars, def_id, errors_buffer);
 
     // Dump MIR results into a file, if that is enabled. This let us
     // write unit-tests, as well as helping with debugging.
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
index e6795dbfd44..5e79a2fea9b 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
@@ -9,7 +9,7 @@ use rustc::hir::def_id::DefId;
 use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc::infer::InferCtxt;
 use rustc::infer::NLLRegionVariableOrigin;
-use rustc::mir::{ConstraintCategory, Location, Body};
+use rustc::mir::{ConstraintCategory, Local, Location, Body};
 use rustc::ty::{self, RegionVid};
 use rustc_index::vec::IndexVec;
 use rustc_errors::DiagnosticBuilder;
@@ -17,6 +17,7 @@ use std::collections::VecDeque;
 use syntax::errors::Applicability;
 use syntax::symbol::kw;
 use syntax_pos::Span;
+use syntax_pos::symbol::Symbol;
 
 use self::outlives_suggestion::OutlivesSuggestionBuilder;
 
@@ -71,6 +72,9 @@ pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
     /// The MIR body we are reporting errors on (for convenience).
     body: &'b Body<'tcx>,
 
+    /// User variable names for MIR locals (where applicable).
+    local_names: &'b IndexVec<Local, Option<Symbol>>,
+
     /// Any upvars for the MIR body we have kept track of during borrow checking.
     upvars: &'b [Upvar],
 }
@@ -367,13 +371,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     pub(super) fn report_error<'a>(
         &'a self,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         infcx: &'a InferCtxt<'a, 'tcx>,
         mir_def_id: DefId,
         fr: RegionVid,
         fr_origin: NLLRegionVariableOrigin,
         outlived_fr: RegionVid,
-        outlives_suggestion: &mut OutlivesSuggestionBuilder,
+        outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
         renctx: &mut RegionErrorNamingCtx,
     ) -> DiagnosticBuilder<'a> {
         debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
@@ -407,6 +412,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             infcx,
             mir_def_id,
             body,
+            local_names,
             upvars,
         };
 
@@ -551,7 +557,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         renctx: &mut RegionErrorNamingCtx,
     ) -> DiagnosticBuilder<'_> {
         let ErrorReportingCtx {
-            infcx, body, upvars, ..
+            infcx, body, upvars, local_names, ..
         } = errctx;
 
         let ErrorConstraintInfo {
@@ -559,9 +565,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         } = errci;
 
         let fr_name_and_span =
-            self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, errci.fr);
-        let outlived_fr_name_and_span =
-            self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, errci.outlived_fr);
+            self.get_var_name_and_span_for_region(infcx.tcx, body, local_names, upvars, errci.fr);
+        let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
+            infcx.tcx,
+            body,
+            local_names,
+            upvars,
+            errci.outlived_fr,
+        );
 
         let escapes_from = match self.universal_regions.defining_ty {
             DefiningTy::Closure(..) => "closure",
@@ -789,6 +800,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn free_region_constraint_info(
         &self,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         mir_def_id: DefId,
         infcx: &InferCtxt<'_, 'tcx>,
@@ -804,7 +816,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         let mut renctx = RegionErrorNamingCtx::new();
         let errctx = ErrorReportingCtx {
-            infcx, body, upvars, mir_def_id,
+            infcx, body, local_names, upvars, mir_def_id,
             region_infcx: self,
         };
         let outlived_fr_name = self.give_region_a_name(&errctx, &mut renctx, outlived_region);
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/outlives_suggestion.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/outlives_suggestion.rs
index 62142efe505..c0cf4eb5285 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/outlives_suggestion.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/outlives_suggestion.rs
@@ -4,9 +4,12 @@
 use std::collections::BTreeMap;
 
 use log::debug;
-use rustc::{hir::def_id::DefId, infer::InferCtxt, mir::Body, ty::RegionVid};
+use rustc::{hir::def_id::DefId, infer::InferCtxt, ty::RegionVid};
+use rustc::mir::{Body, Local};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Diagnostic, DiagnosticBuilder};
+use rustc_index::vec::IndexVec;
+use syntax_pos::symbol::Symbol;
 
 use smallvec::SmallVec;
 
@@ -34,10 +37,12 @@ enum SuggestedConstraint {
 /// corresponding to a function definition.
 ///
 /// Adds a help note suggesting adding a where clause with the needed constraints.
-pub struct OutlivesSuggestionBuilder {
+pub struct OutlivesSuggestionBuilder<'a> {
     /// The MIR DefId of the fn with the lifetime error.
     mir_def_id: DefId,
 
+    local_names: &'a IndexVec<Local, Option<Symbol>>,
+
     /// The list of outlives constraints that need to be added. Specifically, we map each free
     /// region to all other regions that it must outlive. I will use the shorthand `fr:
     /// outlived_frs`. Not all of these regions will already have names necessarily. Some could be
@@ -46,10 +51,17 @@ pub struct OutlivesSuggestionBuilder {
     constraints_to_add: BTreeMap<RegionVid, Vec<RegionVid>>,
 }
 
-impl OutlivesSuggestionBuilder {
+impl OutlivesSuggestionBuilder<'a> {
     /// Create a new builder for the given MIR node representing a fn definition.
-    crate fn new(mir_def_id: DefId) -> Self {
-        OutlivesSuggestionBuilder { mir_def_id, constraints_to_add: BTreeMap::default() }
+    crate fn new(
+        mir_def_id: DefId,
+        local_names: &'a IndexVec<Local, Option<Symbol>>,
+    ) -> Self {
+        OutlivesSuggestionBuilder {
+            mir_def_id,
+            local_names,
+            constraints_to_add: BTreeMap::default(),
+        }
     }
 
     /// Returns `true` iff the `RegionNameSource` is a valid source for an outlives
@@ -125,6 +137,7 @@ impl OutlivesSuggestionBuilder {
             infcx,
             body,
             mir_def_id: self.mir_def_id,
+            local_names: self.local_names,
 
             // We should not be suggesting naming upvars, so we pass in a dummy set of upvars that
             // should never be used.
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
index a8cc0cc044a..e59928987a0 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
@@ -11,10 +11,11 @@ use rustc::hir;
 use rustc::hir::def::{Res, DefKind};
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
-use rustc::mir::Body;
+use rustc::mir::{Local, Body};
 use rustc::ty::subst::{SubstsRef, GenericArgKind};
 use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt};
 use rustc::ty::print::RegionHighlightMode;
+use rustc_index::vec::IndexVec;
 use rustc_errors::DiagnosticBuilder;
 use syntax::symbol::kw;
 use rustc_data_structures::fx::FxHashMap;
@@ -210,7 +211,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         fr: RegionVid,
     ) -> Option<RegionName> {
         let ErrorReportingCtx {
-            infcx, body, mir_def_id, upvars, ..
+            infcx, body, mir_def_id, local_names, upvars, ..
         } = errctx;
 
         debug!("give_region_a_name(fr={:?}, counter={:?})", fr, renctx.counter);
@@ -225,7 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .give_name_from_error_region(infcx.tcx, *mir_def_id, fr, renctx)
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_arguments(
-                    infcx, body, *mir_def_id, fr, renctx,
+                    infcx, body, local_names, *mir_def_id, fr, renctx,
                 )
             })
             .or_else(|| {
@@ -395,6 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         mir_def_id: DefId,
         fr: RegionVid,
         renctx: &mut RegionErrorNamingCtx,
@@ -415,7 +417,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             return Some(region_name);
         }
 
-        self.give_name_if_we_cannot_match_hir_ty(infcx, body, fr, arg_ty, renctx)
+        self.give_name_if_we_cannot_match_hir_ty(infcx, body, local_names, fr, arg_ty, renctx)
     }
 
     fn give_name_if_we_can_match_hir_ty_from_argument(
@@ -463,6 +465,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         needle_fr: RegionVid,
         argument_ty: Ty<'tcx>,
         renctx: &mut RegionErrorNamingCtx,
@@ -479,7 +482,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() {
             // Only add a label if we can confirm that a region was labelled.
             let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?;
-            let (_, span) = self.get_argument_name_and_span_for_region(body, argument_index);
+            let (_, span) =
+                self.get_argument_name_and_span_for_region(body, local_names, argument_index);
 
             Some(RegionName {
                 // This counter value will already have been used, so this function will increment
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
index 7f0e97c9ae4..1ac44c4fdb1 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
@@ -3,7 +3,7 @@ use crate::borrow_check::nll::ToRegionVid;
 use crate::borrow_check::Upvar;
 use rustc::mir::{Local, Body};
 use rustc::ty::{RegionVid, TyCtxt};
-use rustc_index::vec::Idx;
+use rustc_index::vec::{Idx, IndexVec};
 use syntax::source_map::Span;
 use syntax_pos::symbol::Symbol;
 
@@ -12,6 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         fr: RegionVid,
     ) -> Option<(Option<Symbol>, Span)> {
@@ -27,8 +28,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             })
             .or_else(|| {
                 debug!("get_var_name_and_span_for_region: attempting argument");
-                self.get_argument_index_for_region(tcx, fr)
-                    .map(|index| self.get_argument_name_and_span_for_region(body, index))
+                self.get_argument_index_for_region(tcx, fr).map(|index| {
+                    self.get_argument_name_and_span_for_region(body, local_names, index)
+                })
             })
     }
 
@@ -117,13 +119,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn get_argument_name_and_span_for_region(
         &self,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         argument_index: usize,
     ) -> (Option<Symbol>, Span) {
         let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
         let argument_local = Local::new(implicit_inputs + argument_index + 1);
         debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
 
-        let argument_name = body.local_decls[argument_local].name;
+        let argument_name = local_names[argument_local];
         let argument_span = body.local_decls[argument_local].source_info.span;
         debug!("get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}",
                argument_name, argument_span);
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
index 4de8200e6a0..d44e85fa790 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -36,6 +36,7 @@ use rustc_data_structures::graph::vec_graph::VecGraph;
 use rustc_index::vec::IndexVec;
 use rustc_errors::{Diagnostic, DiagnosticBuilder};
 use syntax_pos::Span;
+use syntax_pos::symbol::Symbol;
 
 crate use self::error_reporting::{RegionName, RegionNameSource, RegionErrorNamingCtx};
 use self::values::{LivenessValues, RegionValueElements, RegionValues};
@@ -471,6 +472,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &mut self,
         infcx: &InferCtxt<'_, 'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         mir_def_id: DefId,
         errors_buffer: &mut Vec<Diagnostic>,
@@ -502,6 +504,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.check_universal_regions(
             infcx,
             body,
+            local_names,
             upvars,
             mir_def_id,
             outlives_requirements.as_mut(),
@@ -1321,13 +1324,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         mir_def_id: DefId,
         mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
         errors_buffer: &mut Vec<Diagnostic>,
         region_naming: &mut RegionErrorNamingCtx,
     ) {
-        let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id);
+        let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names);
 
         for (fr, fr_definition) in self.definitions.iter_enumerated() {
             match fr_definition.origin {
@@ -1338,6 +1342,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     self.check_universal_region(
                         infcx,
                         body,
+                        local_names,
                         upvars,
                         mir_def_id,
                         fr,
@@ -1374,11 +1379,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         mir_def_id: DefId,
         longer_fr: RegionVid,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
-        outlives_suggestion: &mut OutlivesSuggestionBuilder,
+        outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
         errors_buffer: &mut Vec<Diagnostic>,
         region_naming: &mut RegionErrorNamingCtx,
     ) {
@@ -1404,6 +1410,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 representative,
                 infcx,
                 body,
+                local_names,
                 upvars,
                 mir_def_id,
                 propagated_outlives_requirements,
@@ -1422,6 +1429,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 shorter_fr,
                 infcx,
                 body,
+                local_names,
                 upvars,
                 mir_def_id,
                 propagated_outlives_requirements,
@@ -1445,10 +1453,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         shorter_fr: RegionVid,
         infcx: &InferCtxt<'_, 'tcx>,
         body: &Body<'tcx>,
+        local_names: &IndexVec<Local, Option<Symbol>>,
         upvars: &[Upvar],
         mir_def_id: DefId,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
-        outlives_suggestion: &mut OutlivesSuggestionBuilder,
+        outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
         errors_buffer: &mut Vec<Diagnostic>,
         region_naming: &mut RegionErrorNamingCtx,
     ) -> Option<ErrorReported> {
@@ -1502,6 +1511,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // error. This gives better error messages in some cases.
         let db = self.report_error(
             body,
+            local_names,
             upvars,
             infcx,
             mir_def_id,
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index e991181189f..f5dc09ccebc 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -227,9 +227,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         mutability: Mutability::Mut,
                         ty: ptr_ty,
                         user_ty: UserTypeProjections::none(),
-                        name: None,
                         source_info,
-                        visibility_scope: source_info.scope,
                         internal: true,
                         local_info: LocalInfo::Other,
                         is_block_tail: None,
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 514dd74dc0b..032ea7d8161 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -1721,6 +1721,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         );
 
         let tcx = self.hir.tcx();
+        let debug_source_info = SourceInfo {
+            span: source_info.span,
+            scope: visibility_scope,
+        };
         let binding_mode = match mode {
             BindingMode::ByValue => ty::BindingMode::BindByValue(mutability.into()),
             BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability.into()),
@@ -1730,9 +1734,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             mutability,
             ty: var_ty,
             user_ty,
-            name: Some(name),
             source_info,
-            visibility_scope,
             internal: false,
             is_block_tail: None,
             local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
@@ -1749,6 +1751,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             ))),
         };
         let for_arm_body = self.local_decls.push(local);
+        self.var_debug_info.push(VarDebugInfo {
+            name,
+            source_info: debug_source_info,
+            place: for_arm_body.into(),
+        });
         let locals = if has_guard.0 {
             let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
                 // This variable isn't mutated but has a name, so has to be
@@ -1756,13 +1763,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 mutability: Mutability::Not,
                 ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
                 user_ty: UserTypeProjections::none(),
-                name: Some(name),
                 source_info,
-                visibility_scope,
                 internal: false,
                 is_block_tail: None,
                 local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)),
             });
+            self.var_debug_info.push(VarDebugInfo {
+                name,
+                source_info: debug_source_info,
+                place: ref_for_guard.into(),
+            });
             LocalsForNode::ForGuard {
                 ref_for_guard,
                 for_arm_body,
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 6b458cc244c..180f2cc089f 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -161,8 +161,18 @@ pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
                 (None, fn_sig.output())
             };
 
-            build::construct_fn(cx, id, arguments, safety, abi,
-                                return_ty, yield_ty, return_ty_span, body)
+            let mut mir = build::construct_fn(
+                cx,
+                id,
+                arguments,
+                safety,
+                abi,
+                return_ty,
+                return_ty_span,
+                body,
+            );
+            mir.yield_ty = yield_ty;
+            mir
         } else {
             // Get the revealed type of this const. This is *not* the adjusted
             // type of its body, which may be a subtype of this type. For
@@ -312,10 +322,11 @@ struct Builder<'a, 'tcx> {
     var_indices: HirIdMap<LocalsForNode>,
     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
     canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
-    __upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
     upvar_mutbls: Vec<Mutability>,
     unit_temp: Option<Place<'tcx>>,
 
+    var_debug_info: Vec<VarDebugInfo<'tcx>>,
+
     /// Cached block with the `RESUME` terminator; this is created
     /// when first set of cleanups are built.
     cached_resume_block: Option<BasicBlock>,
@@ -539,7 +550,6 @@ fn construct_fn<'a, 'tcx, A>(
     safety: Safety,
     abi: Abi,
     return_ty: Ty<'tcx>,
-    yield_ty: Option<Ty<'tcx>>,
     return_ty_span: Span,
     body: &'tcx hir::Body,
 ) -> Body<'tcx>
@@ -552,58 +562,14 @@ where
     let tcx_hir = tcx.hir();
     let span = tcx_hir.span(fn_id);
 
-    let hir_tables = hir.tables();
     let fn_def_id = tcx_hir.local_def_id(fn_id);
 
-    // Gather the upvars of a closure, if any.
-    let mut upvar_mutbls = vec![];
-    // In analyze_closure() in upvar.rs we gathered a list of upvars used by a
-    // closure and we stored in a map called upvar_list in TypeckTables indexed
-    // with the closure's DefId. Here, we run through that vec of UpvarIds for
-    // the given closure and use the necessary information to create UpvarDecl.
-    let upvar_debuginfo: Vec<_> = hir_tables
-        .upvar_list
-        .get(&fn_def_id)
-        .into_iter()
-        .flatten()
-        .map(|(&var_hir_id, &upvar_id)| {
-            let capture = hir_tables.upvar_capture(upvar_id);
-            let by_ref = match capture {
-                ty::UpvarCapture::ByValue => false,
-                ty::UpvarCapture::ByRef(..) => true,
-            };
-            let mut debuginfo = UpvarDebuginfo {
-                debug_name: kw::Invalid,
-                by_ref,
-            };
-            let mut mutability = Mutability::Not;
-            if let Some(Node::Binding(pat)) = tcx_hir.find(var_hir_id) {
-                if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
-                    debuginfo.debug_name = ident.name;
-                    if let Some(&bm) = hir.tables.pat_binding_modes().get(pat.hir_id) {
-                        if bm == ty::BindByValue(hir::Mutability::Mutable) {
-                            mutability = Mutability::Mut;
-                        } else {
-                            mutability = Mutability::Not;
-                        }
-                    } else {
-                        tcx.sess.delay_span_bug(pat.span, "missing binding mode");
-                    }
-                }
-            }
-            upvar_mutbls.push(mutability);
-            debuginfo
-        })
-        .collect();
-
     let mut builder = Builder::new(hir,
         span,
         arguments.len(),
         safety,
         return_ty,
         return_ty_span,
-        upvar_debuginfo,
-        upvar_mutbls,
         body.generator_kind.is_some());
 
     let call_site_scope = region::Scope {
@@ -631,7 +597,7 @@ where
             Place::return_place(),
             |builder| {
                 builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
-                    builder.args_and_body(block, &arguments, arg_scope, &body.value)
+                    builder.args_and_body(block, fn_def_id, &arguments, arg_scope, &body.value)
                 })
             },
         ));
@@ -660,7 +626,7 @@ where
     info!("fn_id {:?} has attrs {:?}", fn_def_id,
           tcx.get_attrs(fn_def_id));
 
-    let mut body = builder.finish(yield_ty);
+    let mut body = builder.finish();
     body.spread_arg = spread_arg;
     body
 }
@@ -681,8 +647,6 @@ fn construct_const<'a, 'tcx>(
         Safety::Safe,
         const_ty,
         const_ty_span,
-        vec![],
-        vec![],
         false,
     );
 
@@ -704,7 +668,7 @@ fn construct_const<'a, 'tcx>(
                               TerminatorKind::Unreachable);
     }
 
-    builder.finish(None)
+    builder.finish()
 }
 
 fn construct_error<'a, 'tcx>(
@@ -714,10 +678,10 @@ fn construct_error<'a, 'tcx>(
     let owner_id = hir.tcx().hir().body_owner(body_id);
     let span = hir.tcx().hir().span(owner_id);
     let ty = hir.tcx().types.err;
-    let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, vec![], vec![], false);
+    let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, false);
     let source_info = builder.source_info(span);
     builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
-    builder.finish(None)
+    builder.finish()
 }
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -727,8 +691,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
            safety: Safety,
            return_ty: Ty<'tcx>,
            return_span: Span,
-           __upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
-           upvar_mutbls: Vec<Mutability>,
            is_generator: bool)
            -> Builder<'a, 'tcx> {
         let lint_level = LintLevel::Explicit(hir.root_lint_level);
@@ -751,10 +713,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 1,
             ),
             canonical_user_type_annotations: IndexVec::new(),
-            __upvar_debuginfo_codegen_only_do_not_use,
-            upvar_mutbls,
+            upvar_mutbls: vec![],
             var_indices: Default::default(),
             unit_temp: None,
+            var_debug_info: vec![],
             cached_resume_block: None,
             cached_return_block: None,
             cached_unreachable_block: None,
@@ -769,9 +731,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         builder
     }
 
-    fn finish(self,
-              yield_ty: Option<Ty<'tcx>>)
-              -> Body<'tcx> {
+    fn finish(self) -> Body<'tcx> {
         for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
             if block.terminator.is_none() {
                 span_bug!(self.fn_span, "no terminator on block {:?}", index);
@@ -782,11 +742,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             self.cfg.basic_blocks,
             self.source_scopes,
             ClearCrossCrate::Set(self.source_scope_local_data),
-            yield_ty,
             self.local_decls,
             self.canonical_user_type_annotations,
             self.arg_count,
-            self.__upvar_debuginfo_codegen_only_do_not_use,
+            self.var_debug_info,
             self.fn_span,
             self.hir.control_flow_destroyed(),
         )
@@ -794,6 +753,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
     fn args_and_body(&mut self,
                      mut block: BasicBlock,
+                     fn_def_id: DefId,
                      arguments: &[ArgInfo<'tcx>],
                      argument_scope: region::Scope,
                      ast_body: &'tcx hir::Expr)
@@ -801,28 +761,100 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     {
         // Allocate locals for the function arguments
         for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() {
-            // If this is a simple binding pattern, give the local a name for
-            // debuginfo and so that error reporting knows that this is a user
-            // variable. For any other pattern the pattern introduces new
-            // variables which will be named instead.
-            let (name, span) = if let Some(arg) = arg_opt {
-                (arg.pat.simple_ident().map(|ident| ident.name), arg.pat.span)
-            } else {
-                (None, self.fn_span)
+            let source_info = SourceInfo {
+                scope: OUTERMOST_SOURCE_SCOPE,
+                span: arg_opt.map_or(self.fn_span, |arg| arg.pat.span)
             };
-
-            let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span, };
-            self.local_decls.push(LocalDecl {
+            let arg_local = self.local_decls.push(LocalDecl {
                 mutability: Mutability::Mut,
                 ty,
                 user_ty: UserTypeProjections::none(),
                 source_info,
-                visibility_scope: source_info.scope,
-                name,
                 internal: false,
                 local_info: LocalInfo::Other,
                 is_block_tail: None,
             });
+
+            // If this is a simple binding pattern, give debuginfo a nice name.
+            if let Some(arg) = arg_opt {
+                if let Some(ident) = arg.pat.simple_ident() {
+                    self.var_debug_info.push(VarDebugInfo {
+                        name: ident.name,
+                        source_info,
+                        place: arg_local.into(),
+                    });
+                }
+            }
+        }
+
+        let tcx = self.hir.tcx();
+        let tcx_hir = tcx.hir();
+        let hir_tables = self.hir.tables();
+
+        // In analyze_closure() in upvar.rs we gathered a list of upvars used by a
+        // closure and we stored in a map called upvar_list in TypeckTables indexed
+        // with the closure's DefId. Here, we run through that vec of UpvarIds for
+        // the given closure and use the necessary information to create upvar
+        // debuginfo and to fill `self.upvar_mutbls`.
+        if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
+            let closure_env_arg = Local::new(1);
+            let mut closure_env_projs = vec![];
+            let mut closure_ty = self.local_decls[closure_env_arg].ty;
+            if let ty::Ref(_, ty, _) = closure_ty.kind {
+                closure_env_projs.push(ProjectionElem::Deref);
+                closure_ty = ty;
+            }
+            let (def_id, upvar_substs) = match closure_ty.kind {
+                ty::Closure(def_id, substs) => (def_id, ty::UpvarSubsts::Closure(substs)),
+                ty::Generator(def_id, substs, _) => (def_id, ty::UpvarSubsts::Generator(substs)),
+                _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty)
+            };
+            let upvar_tys = upvar_substs.upvar_tys(def_id, tcx);
+            let upvars_with_tys = upvars.iter().zip(upvar_tys);
+            self.upvar_mutbls = upvars_with_tys.enumerate().map(|(i, ((&var_id, &upvar_id), ty))| {
+                let capture = hir_tables.upvar_capture(upvar_id);
+
+                let mut mutability = Mutability::Not;
+                let mut name = kw::Invalid;
+                if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
+                    if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
+                        name = ident.name;
+
+                        if let Some(&bm) = hir_tables.pat_binding_modes().get(pat.hir_id) {
+                            if bm == ty::BindByValue(hir::Mutability::Mutable) {
+                                mutability = Mutability::Mut;
+                            } else {
+                                mutability = Mutability::Not;
+                            }
+                        } else {
+                            tcx.sess.delay_span_bug(pat.span, "missing binding mode");
+                        }
+                    }
+                }
+
+                let mut projs = closure_env_projs.clone();
+                projs.push(ProjectionElem::Field(Field::new(i), ty));
+                match capture {
+                    ty::UpvarCapture::ByValue => {}
+                    ty::UpvarCapture::ByRef(..) => {
+                        projs.push(ProjectionElem::Deref);
+                    }
+                };
+
+                self.var_debug_info.push(VarDebugInfo {
+                    name,
+                    source_info: SourceInfo {
+                        scope: OUTERMOST_SOURCE_SCOPE,
+                        span: tcx_hir.span(var_id),
+                    },
+                    place: Place {
+                        base: closure_env_arg.into(),
+                        projection: tcx.intern_place_elems(&projs),
+                    },
+                });
+
+                mutability
+            }).collect();
         }
 
         let mut scope = None;
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 17f5e3d4e47..b5cb6a92816 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -148,9 +148,7 @@ fn temp_decl(mutability: Mutability, ty: Ty<'_>, span: Span) -> LocalDecl<'_> {
         mutability,
         ty,
         user_ty: UserTypeProjections::none(),
-        name: None,
         source_info,
-        visibility_scope: source_info.scope,
         internal: false,
         local_info: LocalInfo::Other,
         is_block_tail: None,
@@ -204,7 +202,6 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
             SourceScopeData { span: span, parent_scope: None }, 1
         ),
         ClearCrossCrate::Clear,
-        None,
         local_decls_for_sig(&sig, span),
         IndexVec::new(),
         sig.inputs().len(),
@@ -371,7 +368,6 @@ impl CloneShimBuilder<'tcx> {
                 SourceScopeData { span: self.span, parent_scope: None }, 1
             ),
             ClearCrossCrate::Clear,
-            None,
             self.local_decls,
             IndexVec::new(),
             self.sig.inputs().len(),
@@ -832,7 +828,6 @@ fn build_call_shim<'tcx>(
             SourceScopeData { span: span, parent_scope: None }, 1
         ),
         ClearCrossCrate::Clear,
-        None,
         local_decls,
         IndexVec::new(),
         sig.inputs().len(),
@@ -919,7 +914,6 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &Body<'_> {
             SourceScopeData { span: span, parent_scope: None }, 1
         ),
         ClearCrossCrate::Clear,
-        None,
         local_decls,
         IndexVec::new(),
         sig.inputs().len(),
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 6f0b960cab1..8de16308e83 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -85,7 +85,6 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
                 body.basic_blocks().clone(),
                 Default::default(),
                 ClearCrossCrate::Clear,
-                None,
                 body.local_decls.clone(),
                 Default::default(),
                 body.arg_count,
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 524b6b08790..a904c6a3ada 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -62,7 +62,6 @@ use rustc_index::vec::{Idx, IndexVec};
 use rustc_index::bit_set::{BitSet, BitMatrix};
 use std::borrow::Cow;
 use std::iter;
-use std::mem;
 use crate::transform::{MirPass, MirSource};
 use crate::transform::simplify;
 use crate::transform::no_landing_pads::no_landing_pads;
@@ -427,9 +426,7 @@ fn replace_result_variable<'tcx>(
         mutability: Mutability::Mut,
         ty: ret_ty,
         user_ty: UserTypeProjections::none(),
-        name: None,
         source_info,
-        visibility_scope: source_info.scope,
         internal: false,
         is_block_tail: None,
         local_info: LocalInfo::Other
@@ -788,18 +785,12 @@ fn compute_layout<'tcx>(
         }
     }
 
-    let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), body.span);
-
-    // Gather live locals and their indices replacing values in body.local_decls
-    // with a dummy to avoid changing local indices.
+    // Gather live local types and their indices.
     let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
     let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
-    let mut decls = IndexVec::<GeneratorSavedLocal, _>::new();
     for (idx, local) in live_locals.iter().enumerate() {
-        let var = mem::replace(&mut body.local_decls[local], dummy_local.clone());
         locals.push(local);
-        tys.push(var.ty);
-        decls.push(var);
+        tys.push(body.local_decls[local].ty);
         debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local);
     }
 
@@ -831,7 +822,6 @@ fn compute_layout<'tcx>(
         field_tys: tys,
         variant_fields,
         storage_conflicts,
-        __local_debuginfo_codegen_only_do_not_use: decls,
     };
 
     (remap, layout, storage_liveness)
@@ -962,9 +952,7 @@ fn create_generator_drop_shim<'tcx>(
         mutability: Mutability::Mut,
         ty: tcx.mk_unit(),
         user_ty: UserTypeProjections::none(),
-        name: None,
         source_info,
-        visibility_scope: source_info.scope,
         internal: false,
         is_block_tail: None,
         local_info: LocalInfo::Other
@@ -980,9 +968,7 @@ fn create_generator_drop_shim<'tcx>(
             mutbl: hir::Mutability::Mutable,
         }),
         user_ty: UserTypeProjections::none(),
-        name: None,
         source_info,
-        visibility_scope: source_info.scope,
         internal: false,
         is_block_tail: None,
         local_info: LocalInfo::Other
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index 5a34e3f471f..867673beb35 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -219,13 +219,6 @@ impl Inliner<'tcx> {
         debug!("should_inline({:?})", callsite);
         let tcx = self.tcx;
 
-        // Don't inline closures that have capture debuginfo
-        // FIXME: Handle closures better
-        if callee_body.__upvar_debuginfo_codegen_only_do_not_use.len() > 0 {
-            debug!("    upvar debuginfo present - not inlining");
-            return false;
-        }
-
         // Cannot inline generators which haven't been transformed yet
         if callee_body.yield_ty.is_some() {
             debug!("    yield ty present - not inlining");
@@ -413,7 +406,6 @@ impl Inliner<'tcx> {
                     local.source_info.scope =
                         scope_map[local.source_info.scope];
                     local.source_info.span = callsite.location.span;
-                    local.visibility_scope = scope_map[local.visibility_scope];
 
                     let idx = caller_body.local_decls.push(local);
                     local_map.push(idx);
@@ -484,6 +476,10 @@ impl Inliner<'tcx> {
                     tcx: self.tcx,
                 };
 
+                for mut var_debug_info in callee_body.var_debug_info.drain(..) {
+                    integrator.visit_var_debug_info(&mut var_debug_info);
+                    caller_body.var_debug_info.push(var_debug_info);
+                }
 
                 for (bb, mut block) in callee_body.basic_blocks_mut().drain_enumerated(..) {
                     integrator.visit_basic_block_data(bb, &mut block);
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index 86ecfbb4fbe..c92c59dd2f7 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -1098,7 +1098,6 @@ pub fn promote_candidates<'tcx>(
                 // memory usage?
                 body.source_scopes.clone(),
                 body.source_scope_local_data.clone(),
-                None,
                 initial_locals,
                 IndexVec::new(),
                 0,
diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs
index f6b09f20bab..c9185d14148 100644
--- a/src/librustc_mir/transform/simplify.rs
+++ b/src/librustc_mir/transform/simplify.rs
@@ -32,7 +32,6 @@ use rustc_index::vec::{Idx, IndexVec};
 use rustc::ty::TyCtxt;
 use rustc::mir::*;
 use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext};
-use rustc::session::config::DebugInfo;
 use std::borrow::Cow;
 use crate::transform::{MirPass, MirSource};
 
@@ -307,13 +306,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
                 marker.locals.insert(arg);
             }
 
-            // We may need to keep dead user variables live for debuginfo.
-            if tcx.sess.opts.debuginfo == DebugInfo::Full {
-                for local in body.vars_iter() {
-                    marker.locals.insert(local);
-                }
-            }
-
             marker.locals
         };
 
diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs
index e4c2f7d389b..28f68ac2de0 100644
--- a/src/librustc_mir/transform/uniform_array_move_out.rs
+++ b/src/librustc_mir/transform/uniform_array_move_out.rs
@@ -362,6 +362,7 @@ impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
         match context {
             PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location),
             PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location),
+            PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {}
             _ => {
                 local_use.use_count += 1;
                 if local_use.first_use.is_none() {
diff --git a/src/librustc_mir/util/def_use.rs b/src/librustc_mir/util/def_use.rs
index 725ec84ca62..1611caddad1 100644
--- a/src/librustc_mir/util/def_use.rs
+++ b/src/librustc_mir/util/def_use.rs
@@ -1,6 +1,6 @@
 //! Def-use analysis.
 
-use rustc::mir::{Body, Local, Location, PlaceElem};
+use rustc::mir::{Body, Local, Location, PlaceElem, VarDebugInfo};
 use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
 use rustc::ty::TyCtxt;
 use rustc_index::vec::IndexVec;
@@ -12,7 +12,9 @@ pub struct DefUseAnalysis {
 
 #[derive(Clone)]
 pub struct Info {
+    // FIXME(eddyb) use smallvec where possible.
     pub defs_and_uses: Vec<Use>,
+    var_debug_info_indices: Vec<usize>,
 }
 
 #[derive(Clone)]
@@ -33,6 +35,8 @@ impl DefUseAnalysis {
 
         let mut finder = DefUseFinder {
             info: mem::take(&mut self.info),
+            var_debug_info_index: 0,
+            in_var_debug_info: false,
         };
         finder.visit_body(body);
         self.info = finder.info
@@ -55,9 +59,14 @@ impl DefUseAnalysis {
         new_local: Local,
         tcx: TyCtxt<'tcx>,
     ) {
-        for place_use in &self.info[local].defs_and_uses {
-            MutateUseVisitor::new(local, new_local, body, tcx)
-                .visit_location(body, place_use.location)
+        let mut visitor = MutateUseVisitor::new(local, new_local, body, tcx);
+        let info = &self.info[local];
+        for place_use in &info.defs_and_uses {
+            visitor.visit_location(body, place_use.location)
+        }
+        // Update debuginfo as well, alongside defs/uses.
+        for &i in &info.var_debug_info_indices {
+            visitor.visit_var_debug_info(&mut body.var_debug_info[i]);
         }
     }
 
@@ -73,6 +82,8 @@ impl DefUseAnalysis {
 
 struct DefUseFinder {
     info: IndexVec<Local, Info>,
+    var_debug_info_index: usize,
+    in_var_debug_info: bool,
 }
 
 impl Visitor<'_> for DefUseFinder {
@@ -80,10 +91,22 @@ impl Visitor<'_> for DefUseFinder {
                    &local: &Local,
                    context: PlaceContext,
                    location: Location) {
-        self.info[local].defs_and_uses.push(Use {
-            context,
-            location,
-        });
+        let info = &mut self.info[local];
+        if self.in_var_debug_info {
+            info.var_debug_info_indices.push(self.var_debug_info_index);
+        } else {
+            info.defs_and_uses.push(Use {
+                context,
+                location,
+            });
+        }
+    }
+    fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) {
+        assert!(!self.in_var_debug_info);
+        self.in_var_debug_info = true;
+        self.super_var_debug_info(var_debug_info);
+        self.in_var_debug_info = false;
+        self.var_debug_info_index += 1;
     }
 }
 
@@ -91,11 +114,13 @@ impl Info {
     fn new() -> Info {
         Info {
             defs_and_uses: vec![],
+            var_debug_info_indices: vec![],
         }
     }
 
     fn clear(&mut self) {
         self.defs_and_uses.clear();
+        self.var_debug_info_indices.clear();
     }
 
     pub fn def_count(&self) -> usize {
diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs
index ff2946d3a69..3a7ec9f038a 100644
--- a/src/librustc_mir/util/graphviz.rs
+++ b/src/librustc_mir/util/graphviz.rs
@@ -197,13 +197,13 @@ fn write_graph_label<'tcx, W: Write>(
             write!(w, "mut ")?;
         }
 
-        if let Some(name) = decl.name {
-            write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
-                   Place::from(local), escape(&decl.ty), name)?;
-        } else {
-            write!(w, r#"{:?}: {};<br align="left"/>"#,
-                   Place::from(local), escape(&decl.ty))?;
-        }
+        write!(w, r#"{:?}: {};<br align="left"/>"#,
+               Place::from(local), escape(&decl.ty))?;
+    }
+
+    for var_debug_info in &body.var_debug_info {
+        write!(w, r#"debug {} => {};<br align="left"/>"#,
+               var_debug_info.name, escape(&var_debug_info.place))?;
     }
 
     writeln!(w, ">;")
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
index 9757f4ac392..63e4af0a56a 100644
--- a/src/librustc_mir/util/liveness.rs
+++ b/src/librustc_mir/util/liveness.rs
@@ -183,6 +183,9 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
 
         PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
             Some(DefUse::Drop),
+
+        // Debug info is neither def nor use.
+        PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
     }
 }
 
diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs
index 89f298846d2..36194335a55 100644
--- a/src/librustc_mir/util/pretty.rs
+++ b/src/librustc_mir/util/pretty.rs
@@ -463,6 +463,30 @@ fn write_scope_tree(
 ) -> io::Result<()> {
     let indent = depth * INDENT.len();
 
+    // Local variable debuginfo.
+    for var_debug_info in &body.var_debug_info {
+        if var_debug_info.source_info.scope != parent {
+            // Not declared in this scope.
+            continue;
+        }
+
+        let indented_debug_info = format!(
+            "{0:1$}debug {2} => {3:?};",
+            INDENT,
+            indent,
+            var_debug_info.name,
+            var_debug_info.place,
+        );
+
+        writeln!(
+            w,
+            "{0:1$} // in {2}",
+            indented_debug_info,
+            ALIGN,
+            comment(tcx, var_debug_info.source_info),
+        )?;
+    }
+
     // Local variable types (including the user's name in a comment).
     for (local, local_decl) in body.local_decls.iter_enumerated() {
         if (1..body.arg_count+1).contains(&local.index()) {
@@ -496,8 +520,6 @@ fn write_scope_tree(
 
         let local_name = if local == RETURN_PLACE {
             format!(" return place")
-        } else if let Some(name) = local_decl.name {
-            format!(" \"{}\"", name)
         } else {
             String::new()
         };
diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs
index 8e134ad14fc..70820dfaea4 100644
--- a/src/test/incremental/hashes/for_loops.rs
+++ b/src/test/incremental/hashes/for_loops.rs
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
 }
 
 #[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
+#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_loop_body() {
     let mut _x = 0;
diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs
index 4e8ba5a209d..68545b7daaa 100644
--- a/src/test/incremental/hashes/let_expressions.rs
+++ b/src/test/incremental/hashes/let_expressions.rs
@@ -22,7 +22,7 @@ pub fn change_name() {
 
 #[cfg(not(cfail1))]
 #[rustc_clean(cfg="cfail2",
-    except="HirBody,mir_built")]
+    except="HirBody,mir_built,optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_name() {
     let _y = 2u64;
@@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() {
 
 #[cfg(not(cfail1))]
 #[rustc_clean(cfg="cfail2",
-    except="HirBody,typeck_tables_of,mir_built")]
+    except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_mutability_of_slot() {
     let _x: u64 = 0;
@@ -182,7 +182,7 @@ pub fn add_initializer() {
 
 #[cfg(not(cfail1))]
 #[rustc_clean(cfg="cfail2",
-    except="HirBody,typeck_tables_of,mir_built")]
+    except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn add_initializer() {
     let _x: i16 = 3i16;
@@ -198,7 +198,7 @@ pub fn change_initializer() {
 
 #[cfg(not(cfail1))]
 #[rustc_clean(cfg="cfail2",
-    except="HirBody,mir_built")]
+    except="HirBody,mir_built,optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_initializer() {
     let _x = 5u16;
diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs
index ca85ee39e36..a2222db4c59 100644
--- a/src/test/incremental/hashes/loop_expressions.rs
+++ b/src/test/incremental/hashes/loop_expressions.rs
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
 }
 
 #[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
+#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_loop_body() {
     let mut _x = 0;
diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs
index 70d0f86c3d2..a427ffb96e3 100644
--- a/src/test/incremental/hashes/while_loops.rs
+++ b/src/test/incremental/hashes/while_loops.rs
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
 }
 
 #[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
+#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_loop_body() {
     let mut _x = 0;
@@ -48,7 +48,7 @@ pub fn change_loop_condition() {
 }
 
 #[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
+#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
 #[rustc_clean(cfg="cfail3")]
 pub fn change_loop_condition() {
     let mut _x = 0;
diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs
index 8dc6b73edf6..fa1a291858b 100644
--- a/src/test/mir-opt/box_expr.rs
+++ b/src/test/mir-opt/box_expr.rs
@@ -27,6 +27,7 @@ impl Drop for S {
 //     let _3: ();
 //     let mut _4: std::boxed::Box<S>;
 //     scope 1 {
+//         debug x => _1;
 //     }
 //     bb0: {
 //         StorageLive(_1);
diff --git a/src/test/mir-opt/generator-storage-dead-unwind.rs b/src/test/mir-opt/generator-storage-dead-unwind.rs
index 109304d6d22..ecce0a08c7b 100644
--- a/src/test/mir-opt/generator-storage-dead-unwind.rs
+++ b/src/test/mir-opt/generator-storage-dead-unwind.rs
@@ -37,8 +37,10 @@ fn main() {
 // ...
 // let mut _9: Bar;
 // scope 1 {
+//     debug a => _2;
 //     let _3: Bar;
 //     scope 2 {
+//         debug b => _3;
 //     }
 // }
 // bb0: {
diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs
index 491130b7c5d..b5bfeef5e9c 100644
--- a/src/test/mir-opt/inline-closure-borrows-arg.rs
+++ b/src/test/mir-opt/inline-closure-borrows-arg.rs
@@ -17,23 +17,43 @@ fn foo<T: Copy>(_t: T, q: &i32) -> i32 {
 
 // END RUST SOURCE
 // START rustc.foo.Inline.after.mir
-// ...
-// bb0: {
-//     ...
-//     _3 = [closure@HirId { owner: DefIndex(4), local_id: 31 }];
-//     ...
-//     _4 = &_3;
-//     ...
-//     _6 = &(*_2);
-//     ...
-//     _7 = &(*_2);
-//     _5 = (move _6, move _7);
-//     _8 = move (_5.0: &i32);
-//     _9 = move (_5.1: &i32);
-//     ...
-//     _0 = (*_8);
-//     ...
-//     return;
+// fn foo(_1: T, _2: &i32) -> i32{
+//     debug _t => _1;
+//     debug q => _2;
+//     let mut _0: i32;
+//     let _3: [closure@HirId { owner: DefIndex(4), local_id: 31 }];
+//     let mut _4: &[closure@HirId { owner: DefIndex(4), local_id: 31 }];
+//     let mut _5: (&i32, &i32);
+//     let mut _6: &i32;
+//     let mut _7: &i32;
+//     let mut _8: &i32;
+//     let mut _9: &i32;
+//     scope 1 {
+//         debug x => _3;
+//         scope 2 {
+//             debug r => _8;
+//             debug _s => _9;
+//         }
+//     }
+//     scope 3 {
+//         debug variable => _8;
+//     }
+//     bb0: {
+//         ...
+//         _3 = [closure@HirId { owner: DefIndex(4), local_id: 31 }];
+//         ...
+//         _4 = &_3;
+//         ...
+//         _6 = &(*_2);
+//         ...
+//         _7 = &(*_2);
+//         _5 = (move _6, move _7);
+//         _8 = move (_5.0: &i32);
+//         _9 = move (_5.1: &i32);
+//         ...
+//         _0 = (*_8);
+//         ...
+//         return;
+//     }
 // }
-// ...
 // END rustc.foo.Inline.after.mir
diff --git a/src/test/mir-opt/inline-closure-captures.rs b/src/test/mir-opt/inline-closure-captures.rs
new file mode 100644
index 00000000000..e73dbe48bd1
--- /dev/null
+++ b/src/test/mir-opt/inline-closure-captures.rs
@@ -0,0 +1,60 @@
+// compile-flags: -Z span_free_formats
+
+// Tests that MIR inliner can handle closure captures.
+
+fn main() {
+    println!("{:?}", foo(0, 14));
+}
+
+fn foo<T: Copy>(t: T, q: i32) -> (i32, T) {
+    let x = |_q| (q, t);
+    x(q)
+}
+
+// END RUST SOURCE
+// START rustc.foo.Inline.after.mir
+// fn foo(_1: T, _2: i32) -> (i32, T){
+//     debug t => _1;
+//     debug q => _2;
+//     let mut _0: (i32, T);
+//     let _3: [closure@HirId { owner: DefIndex(4), local_id: 15 } q:&i32, t:&T];
+//     let mut _4: &i32;
+//     let mut _5: &T;
+//     let mut _6: &[closure@HirId { owner: DefIndex(4), local_id: 15 } q:&i32, t:&T];
+//     let mut _7: (i32,);
+//     let mut _8: i32;
+//     let mut _11: i32;
+//     scope 1 {
+//         debug x => _3;
+//         scope 2 {
+//             debug _q => _11;
+//             debug q => (*((*_6).0: &i32));
+//             debug t => (*((*_6).1: &T));
+//             let mut _9: i32;
+//             let mut _10: T;
+//         }
+//     }
+//     bb0: {
+//         ...
+//         _4 = &_2;
+//         ...
+//         _5 = &_1;
+//         _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }] { q: move _4, t: move _5 };
+//         ...
+//         _6 = &_3;
+//         ...
+//         ...
+//         _8 = _2;
+//         _7 = (move _8,);
+//         _11 = move (_7.0: i32);
+//         ...
+//         _9 = (*((*_6).0: &i32));
+//         ...
+//         _10 = (*((*_6).1: &T));
+//         (_0.0: i32) = move _9;
+//         (_0.1: T) = move _10;
+//         ...
+//         return;
+//     }
+// }
+// END rustc.foo.Inline.after.mir
diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs
index 7c0259b643a..ddf027f4be3 100644
--- a/src/test/mir-opt/inline-closure.rs
+++ b/src/test/mir-opt/inline-closure.rs
@@ -13,22 +13,38 @@ fn foo<T: Copy>(_t: T, q: i32) -> i32 {
 
 // END RUST SOURCE
 // START rustc.foo.Inline.after.mir
-// ...
-// bb0: {
-//     ...
-//     _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }];
-//     ...
-//     _4 = &_3;
-//     ...
-//     _6 = _2;
-//     ...
-//     _7 = _2;
-//     _5 = (move _6, move _7);
-//     _8 = move (_5.0: i32);
-//     _9 = move (_5.1: i32);
-//     _0 = _8;
-//     ...
-//     return;
-// }
-// ...
+// fn foo(_1: T, _2: i32) -> i32{
+//     debug _t => _1;
+//     debug q => _2;
+//     let mut _0: i32;
+//     let _3: [closure@HirId { owner: DefIndex(4), local_id: 15 }];
+//     let mut _4: &[closure@HirId { owner: DefIndex(4), local_id: 15 }];
+//     let mut _5: (i32, i32);
+//     let mut _6: i32;
+//     let mut _7: i32;
+//     let mut _8: i32;
+//     let mut _9: i32;
+//     scope 1 {
+//         debug x => _3;
+//         scope 2 {
+//             debug _t => _8;
+//             debug _q => _9;
+//         }
+//     }
+//     bb0: {
+//         ...
+//         _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }];
+//         ...
+//         _4 = &_3;
+//         ...
+//         _6 = _2;
+//         ...
+//         _7 = _2;
+//         _5 = (move _6, move _7);
+//         _8 = move (_5.0: i32);
+//         _9 = move (_5.1: i32);
+//         _0 = _8;
+//         ...
+//         return;
+//     }
 // END rustc.foo.Inline.after.mir
diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs
index e73390f52b5..5ba54f98d00 100644
--- a/src/test/mir-opt/issue-41110.rs
+++ b/src/test/mir-opt/issue-41110.rs
@@ -35,6 +35,7 @@ impl S {
 //    let mut _4: S;
 //    let mut _5: bool;
 //    scope 1 {
+//        debug x => _1;
 //    }
 //    ...
 //    bb0: {
@@ -47,7 +48,11 @@ impl S {
 //    let mut _5: S;
 //    let mut _6: bool;
 //    ...
+//    debug u => _1;
+//    ...
 //    let mut _2: S;
 //    ...
+//    debug v => _2;
+//    ...
 //    bb0: {
 // END rustc.test.ElaborateDrops.after.mir
diff --git a/src/test/mir-opt/issue-41888.rs b/src/test/mir-opt/issue-41888.rs
index 58f321d6df1..efe2b249d4a 100644
--- a/src/test/mir-opt/issue-41888.rs
+++ b/src/test/mir-opt/issue-41888.rs
@@ -25,9 +25,11 @@ enum E {
 // fn main() -> () {
 //     let mut _0: ();
 //     scope 1 {
-//         let _1: E; // `e`
+//         let _1: E;
+//         debug e => _1;
 //         scope 2 {
 //             let _6: K;
+//             debug _k => _6;
 //         }
 //     }
 //     let mut _2: bool;
diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs
index d0dbcbd7515..54c89b85f42 100644
--- a/src/test/mir-opt/issue-49232.rs
+++ b/src/test/mir-opt/issue-49232.rs
@@ -24,6 +24,7 @@ fn main() {
 //     let _5: ();
 //     let mut _6: &i32;
 //     scope 1 {
+//         debug beacon => _2;
 //     }
 //     bb0: {
 //         goto -> bb1;
diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs
index c898d3a6f16..4412a16e74d 100644
--- a/src/test/mir-opt/match-arm-scopes.rs
+++ b/src/test/mir-opt/match-arm-scopes.rs
@@ -53,8 +53,14 @@ fn main() {
 // let _15: bool;                       // `b`
 // let _16: std::string::String;        // `t`
 // scope 1 {
+//     debug a => _5;
+//     debug a => _6;
+//     debug s => _7;
+//     debug s => _8;
 // }
 // scope 2 {
+//     debug b => _15;
+//     debug t => _16;
 // }
 // bb0: {
 //     FakeRead(ForMatchedPlace, _2);
diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs
index 8228d9740f0..16e357fc162 100644
--- a/src/test/mir-opt/nll/region-subtyping-basic.rs
+++ b/src/test/mir-opt/nll/region-subtyping-basic.rs
@@ -29,8 +29,12 @@ fn main() {
 // START rustc.main.nll.0.mir
 // let _2: &'_#3r usize;
 // ...
+// debug p => _2;
+// ...
 // let _6: &'_#4r usize;
 // ...
+// debug q => _6;
+// ...
 // _2 = &'_#2r _1[_3];
 // ...
 // _6 = _2;
diff --git a/src/test/mir-opt/packed-struct-drop-aligned.rs b/src/test/mir-opt/packed-struct-drop-aligned.rs
index da73cc96348..113f81c441f 100644
--- a/src/test/mir-opt/packed-struct-drop-aligned.rs
+++ b/src/test/mir-opt/packed-struct-drop-aligned.rs
@@ -25,6 +25,7 @@ impl Drop for Droppy {
 //     let mut _5: Droppy;
 //     let mut _6: Aligned;
 //     scope 1 {
+//         debug x => _1;
 //     }
 //
 //     bb0: {
diff --git a/src/test/mir-opt/simplify_try.rs b/src/test/mir-opt/simplify_try.rs
index 7911fbd0a98..656b405ef34 100644
--- a/src/test/mir-opt/simplify_try.rs
+++ b/src/test/mir-opt/simplify_try.rs
@@ -10,6 +10,7 @@ fn main() {
 // END RUST SOURCE
 // START rustc.try_identity.SimplifyArmIdentity.before.mir
 // fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
+//     debug x => _1;
 //     let mut _0: std::result::Result<u32, i32>;
 //     let _2: u32;
 //     let mut _3: std::result::Result<u32, i32>;
@@ -22,21 +23,27 @@ fn main() {
 //     let _10: u32;
 //     let mut _11: u32;
 //     scope 1 {
+//         debug y => _10;
 //     }
 //     scope 2 {
+//         debug err => _6;
 //         scope 3 {
 //             scope 7 {
+//                 debug t => _6;
 //             }
 //             scope 8 {
+//                 debug v => _6;
 //                 let mut _12: i32;
 //             }
 //         }
 //     }
 //     scope 4 {
+//         debug val => _10;
 //         scope 5 {
 //         }
 //     }
 //     scope 6 {
+//         debug self => _1;
 //     }
 //     bb0: {
 //         _5 = discriminant(_1);
@@ -65,6 +72,7 @@ fn main() {
 
 // START rustc.try_identity.SimplifyArmIdentity.after.mir
 // fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
+//     debug x => _1;
 //     let mut _0: std::result::Result<u32, i32>;
 //     let _2: u32;
 //     let mut _3: std::result::Result<u32, i32>;
@@ -77,21 +85,27 @@ fn main() {
 //     let _10: u32;
 //     let mut _11: u32;
 //     scope 1 {
+//         debug y => _10;
 //     }
 //     scope 2 {
+//         debug err => _6;
 //         scope 3 {
 //             scope 7 {
+//                 debug t => _6;
 //             }
 //             scope 8 {
+//                 debug v => _6;
 //                 let mut _12: i32;
 //             }
 //         }
 //     }
 //     scope 4 {
+//         debug val => _10;
 //         scope 5 {
 //         }
 //     }
 //     scope 6 {
+//         debug self => _1;
 //     }
 //     bb0: {
 //         _5 = discriminant(_1);
@@ -120,6 +134,7 @@ fn main() {
 
 // START rustc.try_identity.SimplifyBranchSame.after.mir
 // fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
+//     debug x => _1;
 //     let mut _0: std::result::Result<u32, i32>;
 //     let _2: u32;
 //     let mut _3: std::result::Result<u32, i32>;
@@ -132,21 +147,27 @@ fn main() {
 //     let _10: u32;
 //     let mut _11: u32;
 //     scope 1 {
+//         debug y => _10;
 //     }
 //     scope 2 {
+//         debug err => _6;
 //         scope 3 {
 //             scope 7 {
+//                 debug t => _6;
 //             }
 //             scope 8 {
+//                 debug v => _6;
 //                 let mut _12: i32;
 //             }
 //         }
 //     }
 //     scope 4 {
+//         debug val => _10;
 //         scope 5 {
 //         }
 //     }
 //     scope 6 {
+//         debug self => _1;
 //     }
 //     bb0: {
 //         _5 = discriminant(_1);
@@ -166,23 +187,32 @@ fn main() {
 
 // START rustc.try_identity.SimplifyLocals.after.mir
 // fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
+//     debug x => _1;
 //     let mut _0: std::result::Result<u32, i32>;
 //     let mut _2: isize;
+//     let _3: i32;
+//     let _4: u32;
 //     scope 1 {
+//         debug y => _4;
 //     }
 //     scope 2 {
+//         debug err => _3;
 //         scope 3 {
 //             scope 7 {
+//                 debug t => _3;
 //             }
 //             scope 8 {
+//                 debug v => _3;
 //             }
 //         }
 //     }
 //     scope 4 {
+//         debug val => _4;
 //         scope 5 {
 //         }
 //     }
 //     scope 6 {
+//         debug self => _1;
 //     }
 //     bb0: {
 //         _2 = discriminant(_1);