Skip to content

Commit 3ad299a

Browse files
debuginfo: change cpp-like naming for generator environments so that NatVis works for them
1 parent 07ebc13 commit 3ad299a

File tree

4 files changed

+125
-50
lines changed

4 files changed

+125
-50
lines changed

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

Lines changed: 92 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
1616
use rustc_hir::def_id::DefId;
1717
use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
1818
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
19-
use rustc_middle::ty::layout::IntegerExt;
19+
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
2020
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
21-
use rustc_middle::ty::{self, AdtDef, ExistentialProjection, ParamEnv, Ty, TyCtxt};
21+
use rustc_middle::ty::{self, ExistentialProjection, GeneratorSubsts, ParamEnv, Ty, TyCtxt};
2222
use rustc_query_system::ich::NodeIdHashingMode;
2323
use rustc_target::abi::{Integer, TagEncoding, Variants};
2424
use smallvec::SmallVec;
2525

26+
use std::borrow::Cow;
2627
use std::fmt::Write;
2728

2829
use crate::debuginfo::wants_c_like_enum_debuginfo;
@@ -76,7 +77,16 @@ fn push_debuginfo_type_name<'tcx>(
7677
let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).expect("layout error");
7778

7879
if def.is_enum() && cpp_like_debuginfo && !wants_c_like_enum_debuginfo(ty_and_layout) {
79-
msvc_enum_fallback(tcx, t, def, substs, output, visited);
80+
msvc_enum_fallback(
81+
tcx,
82+
ty_and_layout,
83+
&|output, visited| {
84+
push_item_name(tcx, def.did(), true, output);
85+
push_generic_params_internal(tcx, substs, output, visited);
86+
},
87+
output,
88+
visited,
89+
);
8090
} else {
8191
push_item_name(tcx, def.did(), qualified, output);
8292
push_generic_params_internal(tcx, substs, output, visited);
@@ -352,40 +362,26 @@ fn push_debuginfo_type_name<'tcx>(
352362
ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
353363
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
354364
// "{async_fn_env#0}<T1, T2, ...>", etc.
355-
let def_key = tcx.def_key(def_id);
356-
357-
if qualified {
358-
let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
359-
push_item_name(tcx, parent_def_id, true, output);
360-
output.push_str("::");
365+
// In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
366+
// an artificial `enum$<>` type, as defined in msvc_enum_fallback().
367+
if cpp_like_debuginfo && matches!(t.kind(), ty::Generator(..)) {
368+
let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
369+
msvc_enum_fallback(
370+
tcx,
371+
ty_and_layout,
372+
&|output, visited| {
373+
push_closure_or_generator_name(tcx, def_id, substs, true, output, visited);
374+
},
375+
output,
376+
visited,
377+
);
378+
} else {
379+
push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited);
361380
}
362-
363-
let mut label = String::with_capacity(20);
364-
write!(&mut label, "{}_env", generator_kind_label(tcx.generator_kind(def_id))).unwrap();
365-
366-
push_disambiguated_special_name(
367-
&label,
368-
def_key.disambiguated_data.disambiguator,
369-
cpp_like_debuginfo,
370-
output,
371-
);
372-
373-
// We also need to add the generic arguments of the async fn/generator or
374-
// the enclosing function (for closures or async blocks), so that we end
375-
// up with a unique name for every instantiation.
376-
377-
// Find the generics of the enclosing function, as defined in the source code.
378-
let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
379-
let generics = tcx.generics_of(enclosing_fn_def_id);
380-
381-
// Truncate the substs to the length of the above generics. This will cut off
382-
// anything closure- or generator-specific.
383-
let substs = substs.truncate_to(tcx, generics);
384-
push_generic_params_internal(tcx, substs, output, visited);
385381
}
386382
// Type parameters from polymorphized functions.
387383
ty::Param(_) => {
388-
output.push_str(&format!("{:?}", t));
384+
write!(output, "{:?}", t).unwrap();
389385
}
390386
ty::Error(_)
391387
| ty::Infer(_)
@@ -408,24 +404,32 @@ fn push_debuginfo_type_name<'tcx>(
408404
// `EnumMemberDescriptionFactor::create_member_descriptions`.
409405
fn msvc_enum_fallback<'tcx>(
410406
tcx: TyCtxt<'tcx>,
411-
ty: Ty<'tcx>,
412-
def: AdtDef<'tcx>,
413-
substs: SubstsRef<'tcx>,
407+
ty_and_layout: TyAndLayout<'tcx>,
408+
push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
414409
output: &mut String,
415410
visited: &mut FxHashSet<Ty<'tcx>>,
416411
) {
417-
let layout = tcx.layout_of(tcx.param_env(def.did()).and(ty)).expect("layout error");
412+
debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
413+
let ty = ty_and_layout.ty;
418414

419415
output.push_str("enum$<");
420-
push_item_name(tcx, def.did(), true, output);
421-
push_generic_params_internal(tcx, substs, output, visited);
416+
push_inner(output, visited);
417+
418+
let variant_name = |variant_index| match ty.kind() {
419+
ty::Adt(adt_def, _) => {
420+
debug_assert!(adt_def.is_enum());
421+
Cow::from(adt_def.variant(variant_index).name.as_str())
422+
}
423+
ty::Generator(..) => GeneratorSubsts::variant_name(variant_index),
424+
_ => unreachable!(),
425+
};
422426

423427
if let Variants::Multiple {
424428
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
425429
tag,
426430
variants,
427431
..
428-
} = &layout.variants
432+
} = &ty_and_layout.variants
429433
{
430434
let dataful_variant_layout = &variants[*dataful_variant];
431435

@@ -439,16 +443,13 @@ fn push_debuginfo_type_name<'tcx>(
439443
let max = dataful_discriminant_range.end;
440444
let max = tag.value.size(&tcx).truncate(max);
441445

442-
let dataful_variant_name = def.variant(*dataful_variant).name.as_str();
443-
444-
output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name));
445-
} else if let Variants::Single { index: variant_idx } = &layout.variants {
446+
let dataful_variant_name = variant_name(*dataful_variant);
447+
write!(output, ", {}, {}, {}", min, max, dataful_variant_name).unwrap();
448+
} else if let Variants::Single { index: variant_idx } = &ty_and_layout.variants {
446449
// Uninhabited enums can't be constructed and should never need to be visualized so
447450
// skip this step for them.
448-
if def.variants().len() != 0 {
449-
let variant = def.variant(*variant_idx).name.as_str();
450-
451-
output.push_str(&format!(", {}", variant));
451+
if !ty_and_layout.abi.is_uninhabited() {
452+
write!(output, ", {}", variant_name(*variant_idx)).unwrap();
452453
}
453454
}
454455
push_close_angle_bracket(true, output);
@@ -700,6 +701,49 @@ pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, out
700701
push_generic_params_internal(tcx, substs, output, &mut visited);
701702
}
702703

704+
fn push_closure_or_generator_name<'tcx>(
705+
tcx: TyCtxt<'tcx>,
706+
def_id: DefId,
707+
substs: SubstsRef<'tcx>,
708+
qualified: bool,
709+
output: &mut String,
710+
visited: &mut FxHashSet<Ty<'tcx>>,
711+
) {
712+
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
713+
// "{async_fn_env#0}<T1, T2, ...>", etc.
714+
let def_key = tcx.def_key(def_id);
715+
let generator_kind = tcx.generator_kind(def_id);
716+
717+
if qualified {
718+
let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
719+
push_item_name(tcx, parent_def_id, true, output);
720+
output.push_str("::");
721+
}
722+
723+
let mut label = String::with_capacity(20);
724+
write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap();
725+
726+
push_disambiguated_special_name(
727+
&label,
728+
def_key.disambiguated_data.disambiguator,
729+
cpp_like_debuginfo(tcx),
730+
output,
731+
);
732+
733+
// We also need to add the generic arguments of the async fn/generator or
734+
// the enclosing function (for closures or async blocks), so that we end
735+
// up with a unique name for every instantiation.
736+
737+
// Find the generics of the enclosing function, as defined in the source code.
738+
let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
739+
let generics = tcx.generics_of(enclosing_fn_def_id);
740+
741+
// Truncate the substs to the length of the above generics. This will cut off
742+
// anything closure- or generator-specific.
743+
let substs = substs.truncate_to(tcx, generics);
744+
push_generic_params_internal(tcx, substs, output, visited);
745+
}
746+
703747
fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
704748
// MSVC debugger always treats `>>` as a shift, even when parsing templates,
705749
// so add a space to avoid confusion.

src/test/codegen/async-fn-debug-msvc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async fn async_fn_test() {
1616

1717
// FIXME: No way to reliably check the filename.
1818

19-
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0", {{.*}}, align: {{32|64}},
19+
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$<async_fn_debug_msvc::async_fn_test::async_fn_env$0>",
2020
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
2121
// For brevity, we only check the struct name and members of the last variant.
2222
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,

src/test/codegen/generator-debug-msvc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
2020

2121
// FIXME: No way to reliably check the filename.
2222

23-
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0"
23+
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$<generator_debug_msvc::generator_test::generator_env$0>"
2424
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
2525
// For brevity, we only check the struct name and members of the last variant.
2626
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 14,

src/test/debuginfo/generator-objects.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,37 @@
3737
// lldb-command:print b
3838
// lldbg-check:(generator_objects::main::{generator_env#0}) $3 =
3939

40+
// === CDB TESTS ===================================================================================
41+
42+
// cdb-command: g
43+
// cdb-command: dx b
44+
// cdb-check: b : Unresumed [Type: enum$<generator_objects::main::generator_env$0>]
45+
// cdb-check: [variant] : Unresumed
46+
// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *]
47+
48+
// cdb-command: g
49+
// cdb-command: dx b
50+
// cdb-check: b : Suspend0 [Type: enum$<generator_objects::main::generator_env$0>]
51+
// cdb-check: [variant] : Suspend0
52+
// cdb-check: [+0x008] c : 6 [Type: int]
53+
// cdb-check: [+0x00c] d : 7 [Type: int]
54+
// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *]
55+
56+
// cdb-command: g
57+
// cdb-command: dx b
58+
// cdb-check: b : Suspend1 [Type: enum$<generator_objects::main::generator_env$0>]
59+
// cdb-check: [variant] : Suspend1
60+
// cdb-check: [+0x008] c : 7 [Type: int]
61+
// cdb-check: [+0x00c] d : 8 [Type: int]
62+
// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *]
63+
64+
// cdb-command: g
65+
// cdb-command: dx b
66+
// cdb-check: b : Returned [Type: enum$<generator_objects::main::generator_env$0>]
67+
// cdb-check: [<Raw View>] [Type: enum$<generator_objects::main::generator_env$0>]
68+
// cdb-check: [variant] : Returned
69+
// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *]
70+
4071
#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
4172
#![omit_gdb_pretty_printer_section]
4273

0 commit comments

Comments
 (0)