Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Name the captured upvars for closures/generators in debuginfo #85020

Merged
merged 4 commits into from
Aug 14, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,31 @@ fn prepare_struct_metadata(
// Tuples
//=-----------------------------------------------------------------------------

/// Returns names of captured upvars for closures and generators.
///
/// Here are some examples:
/// - `name__field1__field2` when the upvar is captured by value.
/// - `_ref__name__field` when the upvar is captured by reference.
fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<String> {
let body = tcx.optimized_mir(def_id);

body.var_debug_info
.iter()
.filter_map(|var| {
let is_ref = match var.value {
mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => {
// The projection is either `[.., Field, Deref]` or `[.., Field]`. It
// implies whether the variable is captured by value or by reference.
matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
}
_ => return None,
};
let prefix = if is_ref { "_ref__" } else { "" };
Some(prefix.to_owned() + &var.name.as_str())
tmandry marked this conversation as resolved.
Show resolved Hide resolved
})
.collect::<Vec<_>>()
}

/// Creates `MemberDescription`s for the fields of a tuple.
struct TupleMemberDescriptionFactory<'tcx> {
ty: Ty<'tcx>,
Expand All @@ -1289,14 +1314,25 @@ struct TupleMemberDescriptionFactory<'tcx> {

impl<'tcx> TupleMemberDescriptionFactory<'tcx> {
fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
let capture_names = match *self.ty.kind() {
ty::Generator(def_id, ..) | ty::Closure(def_id, ..) => {
Some(closure_saved_names_of_captured_variables(cx.tcx, def_id))
}
_ => None,
};
let layout = cx.layout_of(self.ty);
self.component_types
.iter()
.enumerate()
.map(|(i, &component_type)| {
let (size, align) = cx.size_and_align_of(component_type);
let name = if let Some(names) = capture_names.as_ref() {
names[i].clone()
lrh2000 marked this conversation as resolved.
Show resolved Hide resolved
} else {
format!("__{}", i)
};
MemberDescription {
name: format!("__{}", i),
name,
type_metadata: type_metadata(cx, component_type, self.span),
offset: layout.fields.offset(i),
size,
Expand Down
41 changes: 40 additions & 1 deletion compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use crate::hir::place::{
};
use crate::{mir, ty};

use std::fmt::Write;

use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_span::Span;
use rustc_span::{Span, Symbol};

use super::{Ty, TyCtxt};

Expand Down Expand Up @@ -159,6 +161,43 @@ impl CapturedPlace<'tcx> {
place_to_string_for_capture(tcx, &self.place)
}

/// Returns a symbol of the captured upvar, which looks like `name__field1__field2`.
pub fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol {
let hir_id = match self.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
base => bug!("Expected an upvar, found {:?}", base),
};
let mut symbol = tcx.hir().name(hir_id).as_str().to_string();

let mut ty = self.place.base_ty;
for proj in self.place.projections.iter() {
lrh2000 marked this conversation as resolved.
Show resolved Hide resolved
match proj.kind {
HirProjectionKind::Field(idx, variant) => match ty.kind() {
ty::Tuple(_) => write!(&mut symbol, "__{}", idx).unwrap(),
ty::Adt(def, ..) => {
write!(
&mut symbol,
"__{}",
def.variants[variant].fields[idx as usize].ident.name.as_str(),
)
.unwrap();
}
ty => {
bug!("Unexpected type {:?} for `Field` projection", ty)
}
},

// Ignore derefs for now, as they are likely caused by
// autoderefs that don't appear in the original code.
HirProjectionKind::Deref => {}
proj => bug!("Unexpected projection {:?} in captured place", proj),
}
ty = proj.ty;
}

Symbol::intern(&symbol)
}

/// Returns the hir-id of the root variable for the captured place.
/// e.g., if `a.b.c` was captured, would return the hir-id for `a`.
pub fn get_root_variable(&self) -> hir::HirId {
Expand Down
10 changes: 2 additions & 8 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::mir::*;
use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, PatKind, Thir};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
use rustc_span::symbol::{kw, sym};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::PanicStrategy;
Expand Down Expand Up @@ -974,13 +974,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

let mutability = captured_place.mutability;

// FIXME(project-rfc-2229#8): Store more precise information
let mut name = kw::Empty;
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
name = ident.name;
}
}
let name = captured_place.to_symbol(tcx);

let mut projs = closure_env_projs.clone();
projs.push(ProjectionElem::Field(Field::new(i), ty));
Expand Down
99 changes: 99 additions & 0 deletions src/test/debuginfo/captured-fields-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run
// gdb-command:print test
// gdbr-check:$1 = captured_fields_1::main::{closure#0} {_ref__my_ref__my_field1: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$2 = captured_fields_1::main::{closure#1} {_ref__my_ref__my_field2: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$3 = captured_fields_1::main::{closure#2} {_ref__my_ref: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$4 = captured_fields_1::main::{closure#3} {my_ref: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$5 = captured_fields_1::main::{closure#4} {my_var__my_field2: 22}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$6 = captured_fields_1::main::{closure#5} {my_var: captured_fields_1::MyStruct {my_field1: 11, my_field2: 22}}
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#0}) $0 = { _ref__my_ref__my_field1 = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#1}) $1 = { _ref__my_ref__my_field2 = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#2}) $2 = { _ref__my_ref = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#3}) $3 = { my_ref = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#4}) $4 = { my_var__my_field2 = 22 }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#5}) $5 = { my_var = { my_field1 = 11 my_field2 = 22 } }
// lldb-command:continue

#![feature(capture_disjoint_fields)]
#![allow(unused)]

struct MyStruct {
my_field1: u32,
my_field2: u32,
}

fn main() {
let mut my_var = MyStruct {
my_field1: 11,
my_field2: 22,
};
let my_ref = &mut my_var;

let test = || {
let a = &mut my_ref.my_field1;
};

_zzz(); // #break

let test = || {
let a = &my_ref.my_field2;
};

_zzz(); // #break

let test = || {
let a = &my_ref;
};

_zzz(); // #break

let test = || {
let a = my_ref;
};

_zzz(); // #break

let test = move || {
let a = my_var.my_field2;
};

_zzz(); // #break

let test = || {
let a = my_var;
};

_zzz(); // #break
}

fn _zzz() {}
55 changes: 55 additions & 0 deletions src/test/debuginfo/captured-fields-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run
// gdb-command:print my_ref__my_field1
// gdbr-check:$1 = 11
// gdb-command:continue
// gdb-command:print my_var__my_field2
// gdbr-check:$2 = 22
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run
// lldb-command:print my_ref__my_field1
// lldbg-check:(unsigned int) $0 = 11
// lldb-command:continue
// lldb-command:print my_var__my_field2
// lldbg-check:(unsigned int) $1 = 22
// lldb-command:continue

#![feature(capture_disjoint_fields)]
#![allow(unused)]

struct MyStruct {
my_field1: u32,
my_field2: u32,
}

fn main() {
let mut my_var = MyStruct {
my_field1: 11,
my_field2: 22,
};
let my_ref = &mut my_var;

let test = || {
let a = my_ref.my_field1;

_zzz(); // #break
};

test();

let test = move || {
let a = my_var.my_field2;

_zzz(); // #break
};

test();
}

fn _zzz() {}
8 changes: 4 additions & 4 deletions src/test/debuginfo/generator-objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@

// gdb-command:run
// gdb-command:print b
// gdb-check:$1 = generator_objects::main::{generator#0}::Unresumed(0x[...])
// gdb-check:$1 = generator_objects::main::{generator#0}::Unresumed{_ref__a: 0x[...]}
// gdb-command:continue
// gdb-command:print b
// gdb-check:$2 = generator_objects::main::{generator#0}::Suspend0{c: 6, d: 7, __0: 0x[...]}
// gdb-check:$2 = generator_objects::main::{generator#0}::Suspend0{c: 6, d: 7, _ref__a: 0x[...]}
// gdb-command:continue
// gdb-command:print b
// gdb-check:$3 = generator_objects::main::{generator#0}::Suspend1{c: 7, d: 8, __0: 0x[...]}
// gdb-check:$3 = generator_objects::main::{generator#0}::Suspend1{c: 7, d: 8, _ref__a: 0x[...]}
// gdb-command:continue
// gdb-command:print b
// gdb-check:$4 = generator_objects::main::{generator#0}::Returned(0x[...])
// gdb-check:$4 = generator_objects::main::{generator#0}::Returned{_ref__a: 0x[...]}

// === LLDB TESTS ==================================================================================

Expand Down
6 changes: 3 additions & 3 deletions src/test/debuginfo/issue-57822.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
// gdb-command:run

// gdb-command:print g
// gdb-check:$1 = issue_57822::main::{closure#1} (issue_57822::main::{closure#0} (1))
// gdb-check:$1 = issue_57822::main::{closure#1} {f: issue_57822::main::{closure#0} {x: 1}}

// gdb-command:print b
// gdb-check:$2 = issue_57822::main::{generator#3}::Unresumed(issue_57822::main::{generator#2}::Unresumed(2))
// gdb-check:$2 = issue_57822::main::{generator#3}::Unresumed{a: issue_57822::main::{generator#2}::Unresumed{y: 2}}

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print g
// lldbg-check:(issue_57822::main::{closure#1}) $0 = { 0 = { 0 = 1 } }
// lldbg-check:(issue_57822::main::{closure#1}) $0 = { f = { x = 1 } }

// lldb-command:print b
// lldbg-check:(issue_57822::main::{generator#3}) $1 =
Expand Down