Skip to content

Commit 29856ac

Browse files
committed
Name the captured upvars for closures/generators in debuginfo
Previously, debuggers print closures as something like ``` y::main::closure-0 (0x7fffffffdd34) ``` The pointer actually references to an upvar. It is not very obvious, especially for beginners. It's because upvars don't have names before, as they are packed into a tuple. This commit names the upvars, so we can expect to see something like ``` y::main::closure-0 {_captured_ref__b: 0x[...]} ```
1 parent 95fb131 commit 29856ac

File tree

5 files changed

+165
-8
lines changed

5 files changed

+165
-8
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1289,14 +1289,36 @@ struct TupleMemberDescriptionFactory<'tcx> {
12891289

12901290
impl<'tcx> TupleMemberDescriptionFactory<'tcx> {
12911291
fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
1292+
// For closures and generators, name the captured upvars
1293+
// with the help of `CapturedPlace::to_mangled_name`.
1294+
let closure_def_id = match *self.ty.kind() {
1295+
ty::Generator(def_id, ..) => def_id.as_local(),
1296+
ty::Closure(def_id, ..) => def_id.as_local(),
1297+
_ => None,
1298+
};
1299+
let captures = match closure_def_id {
1300+
Some(local_def_id) => {
1301+
let typeck_results = cx.tcx.typeck(local_def_id);
1302+
let captures = typeck_results
1303+
.closure_min_captures_flattened(local_def_id.to_def_id())
1304+
.collect::<Vec<_>>();
1305+
Some(captures)
1306+
}
1307+
_ => None,
1308+
};
1309+
12921310
let layout = cx.layout_of(self.ty);
12931311
self.component_types
12941312
.iter()
12951313
.enumerate()
12961314
.map(|(i, &component_type)| {
12971315
let (size, align) = cx.size_and_align_of(component_type);
1316+
let name = captures
1317+
.as_ref()
1318+
.map(|c| c[i].to_mangled_name(cx.tcx))
1319+
.unwrap_or_else(|| format!("__{}", i));
12981320
MemberDescription {
1299-
name: format!("__{}", i),
1321+
name,
13001322
type_metadata: type_metadata(cx, component_type, self.span),
13011323
offset: layout.fields.offset(i),
13021324
size,

compiler/rustc_middle/src/ty/closure.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,54 @@ impl CapturedPlace<'tcx> {
159159
place_to_string_for_capture(tcx, &self.place)
160160
}
161161

162+
/// Returns mangled names of captured upvars. Here are some examples:
163+
/// - `_captured_val__name__field`
164+
/// - `_captured_ref__name__field`
165+
///
166+
/// The purpose is to use those names in debuginfo. They should be human-understandable.
167+
/// Without the names, the end users may get confused when the debuggers just print some
168+
/// pointers in closures or generators.
169+
pub fn to_mangled_name(&self, tcx: TyCtxt<'tcx>) -> String {
170+
let prefix = match self.info.capture_kind {
171+
ty::UpvarCapture::ByValue(_) => "_captured_val__",
172+
ty::UpvarCapture::ByRef(_) => "_captured_ref__",
173+
};
174+
175+
let hir_id = match self.place.base {
176+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
177+
base => bug!("Expected an upvar, found {:?}", base),
178+
};
179+
let name = tcx.hir().name(hir_id);
180+
181+
let mut ty = self.place.base_ty;
182+
let mut fields = String::new();
183+
for proj in self.place.projections.iter() {
184+
match proj.kind {
185+
HirProjectionKind::Field(idx, variant) => match ty.kind() {
186+
ty::Tuple(_) => fields = format!("{}__{}", fields, idx),
187+
ty::Adt(def, ..) => {
188+
fields = format!(
189+
"{}__{}",
190+
fields,
191+
def.variants[variant].fields[idx as usize].ident.name.as_str(),
192+
);
193+
}
194+
ty => {
195+
bug!("Unexpected type {:?} for `Field` projection", ty)
196+
}
197+
},
198+
199+
// Ignore derefs for now, as they are likely caused by
200+
// autoderefs that don't appear in the original code.
201+
HirProjectionKind::Deref => {}
202+
proj => bug!("Unexpected projection {:?} in captured place", proj),
203+
}
204+
ty = proj.ty;
205+
}
206+
207+
prefix.to_owned() + &name.to_string() + &fields
208+
}
209+
162210
/// Returns the hir-id of the root variable for the captured place.
163211
/// e.g., if `a.b.c` was captured, would return the hir-id for `a`.
164212
pub fn get_root_variable(&self) -> hir::HirId {

src/test/debuginfo/captured-fields.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// compile-flags:-g
2+
3+
// === GDB TESTS ===================================================================================
4+
5+
// gdb-command:run
6+
// gdb-command:print test
7+
// gdbr-check:$1 = captured_fields::main::{closure#0} {_captured_ref__my_ref__my_field1: 0x[...]}
8+
// gdb-command:continue
9+
// gdb-command:print test
10+
// gdbr-check:$2 = captured_fields::main::{closure#1} {_captured_ref__my_ref__my_field2: 0x[...]}
11+
// gdb-command:continue
12+
// gdb-command:print test
13+
// gdbr-check:$3 = captured_fields::main::{closure#2} {_captured_ref__my_ref: 0x[...]}
14+
// gdb-command:continue
15+
// gdb-command:print test
16+
// gdbr-check:$4 = captured_fields::main::{closure#3} {_captured_val__my_ref: 0x[...]}
17+
// gdb-command:continue
18+
// gdb-command:print test
19+
// gdbr-check:$5 = captured_fields::main::{closure#4} {_captured_val__my_var: captured_fields::MyStruct {my_field1: 11, my_field2: 22}}
20+
// gdb-command:continue
21+
22+
// === LLDB TESTS ==================================================================================
23+
24+
// lldb-command:run
25+
// lldb-command:print test
26+
// lldbg-check:(captured_fields::main::{closure#0}) $0 = { _captured_ref__my_ref__my_field1 = 0x[...] }
27+
// lldb-command:continue
28+
// lldb-command:print test
29+
// lldbg-check:(captured_fields::main::{closure#1}) $1 = { _captured_ref__my_ref__my_field2 = 0x[...] }
30+
// lldb-command:continue
31+
// lldb-command:print test
32+
// lldbg-check:(captured_fields::main::{closure#2}) $2 = { _captured_ref__my_ref = 0x[...] }
33+
// lldb-command:continue
34+
// lldb-command:print test
35+
// lldbg-check:(captured_fields::main::{closure#3}) $3 = { _captured_val__my_ref = 0x[...] }
36+
// lldb-command:continue
37+
// lldb-command:print test
38+
// lldbg-check:(captured_fields::main::{closure#4}) $4 = { _captured_val__my_var = { my_field1 = 11 my_field2 = 22 } }
39+
// lldb-command:continue
40+
41+
#![feature(capture_disjoint_fields)]
42+
#![allow(unused)]
43+
44+
struct MyStruct {
45+
my_field1: u32,
46+
my_field2: u32,
47+
}
48+
49+
fn main() {
50+
let mut my_var = MyStruct {
51+
my_field1: 11,
52+
my_field2: 22,
53+
};
54+
let my_ref = &mut my_var;
55+
56+
let test = || {
57+
let a = &mut my_ref.my_field1;
58+
};
59+
60+
_zzz(); // #break
61+
62+
let test = || {
63+
let a = &my_ref.my_field2;
64+
};
65+
66+
_zzz(); // #break
67+
68+
let test = || {
69+
let a = &my_ref;
70+
};
71+
72+
_zzz(); // #break
73+
74+
let test = || {
75+
let a = my_ref;
76+
};
77+
78+
_zzz(); // #break
79+
80+
let test = || {
81+
let a = my_var;
82+
};
83+
84+
_zzz(); // #break
85+
}
86+
87+
fn _zzz() {}

src/test/debuginfo/generator-objects.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111

1212
// gdb-command:run
1313
// gdb-command:print b
14-
// gdb-check:$1 = generator_objects::main::{generator#0}::Unresumed(0x[...])
14+
// gdb-check:$1 = generator_objects::main::{generator#0}::Unresumed{_captured_ref__a: 0x[...]}
1515
// gdb-command:continue
1616
// gdb-command:print b
17-
// gdb-check:$2 = generator_objects::main::{generator#0}::Suspend0{c: 6, d: 7, __0: 0x[...]}
17+
// gdb-check:$2 = generator_objects::main::{generator#0}::Suspend0{c: 6, d: 7, _captured_ref__a: 0x[...]}
1818
// gdb-command:continue
1919
// gdb-command:print b
20-
// gdb-check:$3 = generator_objects::main::{generator#0}::Suspend1{c: 7, d: 8, __0: 0x[...]}
20+
// gdb-check:$3 = generator_objects::main::{generator#0}::Suspend1{c: 7, d: 8, _captured_ref__a: 0x[...]}
2121
// gdb-command:continue
2222
// gdb-command:print b
23-
// gdb-check:$4 = generator_objects::main::{generator#0}::Returned(0x[...])
23+
// gdb-check:$4 = generator_objects::main::{generator#0}::Returned{_captured_ref__a: 0x[...]}
2424

2525
// === LLDB TESTS ==================================================================================
2626

src/test/debuginfo/issue-57822.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@
1111
// gdb-command:run
1212

1313
// gdb-command:print g
14-
// gdb-check:$1 = issue_57822::main::{closure#1} (issue_57822::main::{closure#0} (1))
14+
// gdb-check:$1 = issue_57822::main::{closure#1} {_captured_val__f: issue_57822::main::{closure#0} {_captured_val__x: 1}}
1515

1616
// gdb-command:print b
17-
// gdb-check:$2 = issue_57822::main::{generator#3}::Unresumed(issue_57822::main::{generator#2}::Unresumed(2))
17+
// gdb-check:$2 = issue_57822::main::{generator#3}::Unresumed{_captured_val__a: issue_57822::main::{generator#2}::Unresumed{_captured_val__y: 2}}
1818

1919
// === LLDB TESTS ==================================================================================
2020

2121
// lldb-command:run
2222

2323
// lldb-command:print g
24-
// lldbg-check:(issue_57822::main::{closure#1}) $0 = { 0 = { 0 = 1 } }
24+
// lldbg-check:(issue_57822::main::{closure#1}) $0 = { _captured_val__f = { _captured_val__x = 1 } }
2525

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

0 commit comments

Comments
 (0)