Skip to content

Commit 99efc51

Browse files
committed
Auto merge of #85020 - lrh2000:named-upvars, r=tmandry
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 PR names the upvars, so we can expect to see something like ``` y::main::closure-0 {_captured_ref__b: 0x[...]} ``` r? `@tmandry` Discussed at #84752 (comment) .
2 parents c6094fc + cf5eda1 commit 99efc51

File tree

9 files changed

+276
-23
lines changed

9 files changed

+276
-23
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,31 @@ fn prepare_struct_metadata(
12801280
// Tuples
12811281
//=-----------------------------------------------------------------------------
12821282

1283+
/// Returns names of captured upvars for closures and generators.
1284+
///
1285+
/// Here are some examples:
1286+
/// - `name__field1__field2` when the upvar is captured by value.
1287+
/// - `_ref__name__field` when the upvar is captured by reference.
1288+
fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<String> {
1289+
let body = tcx.optimized_mir(def_id);
1290+
1291+
body.var_debug_info
1292+
.iter()
1293+
.filter_map(|var| {
1294+
let is_ref = match var.value {
1295+
mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => {
1296+
// The projection is either `[.., Field, Deref]` or `[.., Field]`. It
1297+
// implies whether the variable is captured by value or by reference.
1298+
matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
1299+
}
1300+
_ => return None,
1301+
};
1302+
let prefix = if is_ref { "_ref__" } else { "" };
1303+
Some(prefix.to_owned() + &var.name.as_str())
1304+
})
1305+
.collect::<Vec<_>>()
1306+
}
1307+
12831308
/// Creates `MemberDescription`s for the fields of a tuple.
12841309
struct TupleMemberDescriptionFactory<'tcx> {
12851310
ty: Ty<'tcx>,
@@ -1289,14 +1314,25 @@ struct TupleMemberDescriptionFactory<'tcx> {
12891314

12901315
impl<'tcx> TupleMemberDescriptionFactory<'tcx> {
12911316
fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
1317+
let mut capture_names = match *self.ty.kind() {
1318+
ty::Generator(def_id, ..) | ty::Closure(def_id, ..) => {
1319+
Some(closure_saved_names_of_captured_variables(cx.tcx, def_id).into_iter())
1320+
}
1321+
_ => None,
1322+
};
12921323
let layout = cx.layout_of(self.ty);
12931324
self.component_types
12941325
.iter()
12951326
.enumerate()
12961327
.map(|(i, &component_type)| {
12971328
let (size, align) = cx.size_and_align_of(component_type);
1329+
let name = if let Some(names) = capture_names.as_mut() {
1330+
names.next().unwrap()
1331+
} else {
1332+
format!("__{}", i)
1333+
};
12981334
MemberDescription {
1299-
name: format!("__{}", i),
1335+
name,
13001336
type_metadata: type_metadata(cx, component_type, self.span),
13011337
offset: layout.fields.offset(i),
13021338
size,

compiler/rustc_middle/src/query/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,16 @@ rustc_queries! {
342342
}
343343
}
344344

345+
query symbols_for_closure_captures(
346+
key: (LocalDefId, DefId)
347+
) -> Vec<rustc_span::Symbol> {
348+
desc {
349+
|tcx| "symbols for captures of closure `{}` in `{}`",
350+
tcx.def_path_str(key.1),
351+
tcx.def_path_str(key.0.to_def_id())
352+
}
353+
}
354+
345355
/// MIR after our optimization passes have run. This is MIR that is ready
346356
/// for codegen. This is also the only query that can fetch non-local MIR, at present.
347357
query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {

compiler/rustc_middle/src/ty/closure.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use crate::hir::place::{
33
};
44
use crate::{mir, ty};
55

6+
use std::fmt::Write;
7+
68
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
79
use rustc_hir as hir;
810
use rustc_hir::def_id::{DefId, LocalDefId};
9-
use rustc_span::Span;
11+
use rustc_span::{Span, Symbol};
1012

1113
use super::{Ty, TyCtxt};
1214

@@ -159,6 +161,43 @@ impl CapturedPlace<'tcx> {
159161
place_to_string_for_capture(tcx, &self.place)
160162
}
161163

164+
/// Returns a symbol of the captured upvar, which looks like `name__field1__field2`.
165+
fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol {
166+
let hir_id = match self.place.base {
167+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
168+
base => bug!("Expected an upvar, found {:?}", base),
169+
};
170+
let mut symbol = tcx.hir().name(hir_id).as_str().to_string();
171+
172+
let mut ty = self.place.base_ty;
173+
for proj in self.place.projections.iter() {
174+
match proj.kind {
175+
HirProjectionKind::Field(idx, variant) => match ty.kind() {
176+
ty::Tuple(_) => write!(&mut symbol, "__{}", idx).unwrap(),
177+
ty::Adt(def, ..) => {
178+
write!(
179+
&mut symbol,
180+
"__{}",
181+
def.variants[variant].fields[idx as usize].ident.name.as_str(),
182+
)
183+
.unwrap();
184+
}
185+
ty => {
186+
bug!("Unexpected type {:?} for `Field` projection", ty)
187+
}
188+
},
189+
190+
// Ignore derefs for now, as they are likely caused by
191+
// autoderefs that don't appear in the original code.
192+
HirProjectionKind::Deref => {}
193+
proj => bug!("Unexpected projection {:?} in captured place", proj),
194+
}
195+
ty = proj.ty;
196+
}
197+
198+
Symbol::intern(&symbol)
199+
}
200+
162201
/// Returns the hir-id of the root variable for the captured place.
163202
/// e.g., if `a.b.c` was captured, would return the hir-id for `a`.
164203
pub fn get_root_variable(&self) -> hir::HirId {
@@ -209,6 +248,15 @@ impl CapturedPlace<'tcx> {
209248
}
210249
}
211250

251+
fn symbols_for_closure_captures<'tcx>(
252+
tcx: TyCtxt<'tcx>,
253+
def_id: (LocalDefId, DefId),
254+
) -> Vec<Symbol> {
255+
let typeck_results = tcx.typeck(def_id.0);
256+
let captures = typeck_results.closure_min_captures_flattened(def_id.1);
257+
captures.into_iter().map(|captured_place| captured_place.to_symbol(tcx)).collect()
258+
}
259+
212260
/// Return true if the `proj_possible_ancestor` represents an ancestor path
213261
/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
214262
/// assuming they both start off of the same root variable.
@@ -392,3 +440,7 @@ impl BorrowKind {
392440
}
393441
}
394442
}
443+
444+
pub fn provide(providers: &mut ty::query::Providers) {
445+
*providers = ty::query::Providers { symbols_for_closure_captures, ..*providers }
446+
}

compiler/rustc_middle/src/ty/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ pub use self::IntVarValue::*;
1616
pub use self::Variance::*;
1717
pub use adt::*;
1818
pub use assoc::*;
19-
pub use closure::*;
2019
pub use generics::*;
2120
pub use vtable::*;
2221

@@ -55,6 +54,12 @@ pub use rustc_type_ir::*;
5554

5655
pub use self::binding::BindingMode;
5756
pub use self::binding::BindingMode::*;
57+
pub use self::closure::{
58+
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
59+
CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList,
60+
RootVariableMinCaptureList, UpvarBorrow, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap,
61+
UpvarPath, CAPTURE_STRUCT_LOCAL,
62+
};
5863
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree};
5964
pub use self::context::{
6065
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
@@ -1980,6 +1985,7 @@ pub fn ast_uint_ty(uty: UintTy) -> ast::UintTy {
19801985
}
19811986

19821987
pub fn provide(providers: &mut ty::query::Providers) {
1988+
closure::provide(providers);
19831989
context::provide(providers);
19841990
erase_regions::provide(providers);
19851991
layout::provide(providers);

compiler/rustc_mir_build/src/build/mod.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::mir::*;
1515
use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, PatKind, Thir};
1616
use rustc_middle::ty::subst::Subst;
1717
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
18-
use rustc_span::symbol::{kw, sym};
18+
use rustc_span::symbol::sym;
1919
use rustc_span::Span;
2020
use rustc_target::spec::abi::Abi;
2121

@@ -902,13 +902,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
902902
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
903903
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
904904
};
905+
let def_id = self.def_id.as_local().unwrap();
906+
let capture_syms = tcx.symbols_for_closure_captures((def_id, fn_def_id));
905907
let capture_tys = upvar_substs.upvar_tys();
906-
let captures_with_tys =
907-
hir_typeck_results.closure_min_captures_flattened(fn_def_id).zip(capture_tys);
908+
let captures_with_tys = hir_typeck_results
909+
.closure_min_captures_flattened(fn_def_id)
910+
.zip(capture_tys.zip(capture_syms));
908911

909912
self.upvar_mutbls = captures_with_tys
910913
.enumerate()
911-
.map(|(i, (captured_place, ty))| {
914+
.map(|(i, (captured_place, (ty, sym)))| {
912915
let capture = captured_place.info.capture_kind;
913916
let var_id = match captured_place.place.base {
914917
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
@@ -917,14 +920,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
917920

918921
let mutability = captured_place.mutability;
919922

920-
// FIXME(project-rfc-2229#8): Store more precise information
921-
let mut name = kw::Empty;
922-
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
923-
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
924-
name = ident.name;
925-
}
926-
}
927-
928923
let mut projs = closure_env_projs.clone();
929924
projs.push(ProjectionElem::Field(Field::new(i), ty));
930925
match capture {
@@ -935,7 +930,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
935930
};
936931

937932
self.var_debug_info.push(VarDebugInfo {
938-
name,
933+
name: sym,
939934
source_info: SourceInfo::outermost(tcx_hir.span(var_id)),
940935
value: VarDebugInfoContents::Place(Place {
941936
local: ty::CAPTURE_STRUCT_LOCAL,
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// compile-flags:-g
2+
3+
// === GDB TESTS ===================================================================================
4+
5+
// gdb-command:run
6+
// gdb-command:print test
7+
// gdbr-check:$1 = captured_fields_1::main::{closure#0} {_ref__my_ref__my_field1: 0x[...]}
8+
// gdb-command:continue
9+
// gdb-command:print test
10+
// gdbr-check:$2 = captured_fields_1::main::{closure#1} {_ref__my_ref__my_field2: 0x[...]}
11+
// gdb-command:continue
12+
// gdb-command:print test
13+
// gdbr-check:$3 = captured_fields_1::main::{closure#2} {_ref__my_ref: 0x[...]}
14+
// gdb-command:continue
15+
// gdb-command:print test
16+
// gdbr-check:$4 = captured_fields_1::main::{closure#3} {my_ref: 0x[...]}
17+
// gdb-command:continue
18+
// gdb-command:print test
19+
// gdbr-check:$5 = captured_fields_1::main::{closure#4} {my_var__my_field2: 22}
20+
// gdb-command:continue
21+
// gdb-command:print test
22+
// gdbr-check:$6 = captured_fields_1::main::{closure#5} {my_var: captured_fields_1::MyStruct {my_field1: 11, my_field2: 22}}
23+
// gdb-command:continue
24+
25+
// === LLDB TESTS ==================================================================================
26+
27+
// lldb-command:run
28+
// lldb-command:print test
29+
// lldbg-check:(captured_fields_1::main::{closure#0}) $0 = { _ref__my_ref__my_field1 = 0x[...] }
30+
// lldb-command:continue
31+
// lldb-command:print test
32+
// lldbg-check:(captured_fields_1::main::{closure#1}) $1 = { _ref__my_ref__my_field2 = 0x[...] }
33+
// lldb-command:continue
34+
// lldb-command:print test
35+
// lldbg-check:(captured_fields_1::main::{closure#2}) $2 = { _ref__my_ref = 0x[...] }
36+
// lldb-command:continue
37+
// lldb-command:print test
38+
// lldbg-check:(captured_fields_1::main::{closure#3}) $3 = { my_ref = 0x[...] }
39+
// lldb-command:continue
40+
// lldb-command:print test
41+
// lldbg-check:(captured_fields_1::main::{closure#4}) $4 = { my_var__my_field2 = 22 }
42+
// lldb-command:continue
43+
// lldb-command:print test
44+
// lldbg-check:(captured_fields_1::main::{closure#5}) $5 = { my_var = { my_field1 = 11 my_field2 = 22 } }
45+
// lldb-command:continue
46+
47+
#![feature(capture_disjoint_fields)]
48+
#![allow(unused)]
49+
50+
struct MyStruct {
51+
my_field1: u32,
52+
my_field2: u32,
53+
}
54+
55+
fn main() {
56+
let mut my_var = MyStruct {
57+
my_field1: 11,
58+
my_field2: 22,
59+
};
60+
let my_ref = &mut my_var;
61+
62+
let test = || {
63+
let a = &mut my_ref.my_field1;
64+
};
65+
66+
_zzz(); // #break
67+
68+
let test = || {
69+
let a = &my_ref.my_field2;
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_ref;
82+
};
83+
84+
_zzz(); // #break
85+
86+
let test = move || {
87+
let a = my_var.my_field2;
88+
};
89+
90+
_zzz(); // #break
91+
92+
let test = || {
93+
let a = my_var;
94+
};
95+
96+
_zzz(); // #break
97+
}
98+
99+
fn _zzz() {}
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// compile-flags:-g
2+
3+
// === GDB TESTS ===================================================================================
4+
5+
// gdb-command:run
6+
// gdb-command:print my_ref__my_field1
7+
// gdbr-check:$1 = 11
8+
// gdb-command:continue
9+
// gdb-command:print my_var__my_field2
10+
// gdbr-check:$2 = 22
11+
// gdb-command:continue
12+
13+
// === LLDB TESTS ==================================================================================
14+
15+
// lldb-command:run
16+
// lldb-command:print my_ref__my_field1
17+
// lldbg-check:(unsigned int) $0 = 11
18+
// lldb-command:continue
19+
// lldb-command:print my_var__my_field2
20+
// lldbg-check:(unsigned int) $1 = 22
21+
// lldb-command:continue
22+
23+
#![feature(capture_disjoint_fields)]
24+
#![allow(unused)]
25+
26+
struct MyStruct {
27+
my_field1: u32,
28+
my_field2: u32,
29+
}
30+
31+
fn main() {
32+
let mut my_var = MyStruct {
33+
my_field1: 11,
34+
my_field2: 22,
35+
};
36+
let my_ref = &mut my_var;
37+
38+
let test = || {
39+
let a = my_ref.my_field1;
40+
41+
_zzz(); // #break
42+
};
43+
44+
test();
45+
46+
let test = move || {
47+
let a = my_var.my_field2;
48+
49+
_zzz(); // #break
50+
};
51+
52+
test();
53+
}
54+
55+
fn _zzz() {}

0 commit comments

Comments
 (0)