From 3ad299aa670face2085d2abec6e8481fa582068a Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 12:10:02 +0100 Subject: [PATCH] debuginfo: change cpp-like naming for generator environments so that NatVis works for them --- .../src/debuginfo/type_names.rs | 140 ++++++++++++------ src/test/codegen/async-fn-debug-msvc.rs | 2 +- src/test/codegen/generator-debug-msvc.rs | 2 +- src/test/debuginfo/generator-objects.rs | 31 ++++ 4 files changed, 125 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 6a122addf22..ee0658f486a 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -16,13 +16,14 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability}; -use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, ExistentialProjection, ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{self, ExistentialProjection, GeneratorSubsts, ParamEnv, Ty, TyCtxt}; use rustc_query_system::ich::NodeIdHashingMode; use rustc_target::abi::{Integer, TagEncoding, Variants}; use smallvec::SmallVec; +use std::borrow::Cow; use std::fmt::Write; use crate::debuginfo::wants_c_like_enum_debuginfo; @@ -76,7 +77,16 @@ fn push_debuginfo_type_name<'tcx>( let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).expect("layout error"); if def.is_enum() && cpp_like_debuginfo && !wants_c_like_enum_debuginfo(ty_and_layout) { - msvc_enum_fallback(tcx, t, def, substs, output, visited); + msvc_enum_fallback( + tcx, + ty_and_layout, + &|output, visited| { + push_item_name(tcx, def.did(), true, output); + push_generic_params_internal(tcx, substs, output, visited); + }, + output, + visited, + ); } else { push_item_name(tcx, def.did(), qualified, output); push_generic_params_internal(tcx, substs, output, visited); @@ -352,40 +362,26 @@ fn push_debuginfo_type_name<'tcx>( ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { // Name will be "{closure_env#0}", "{generator_env#0}", or // "{async_fn_env#0}", etc. - let def_key = tcx.def_key(def_id); - - if qualified { - let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; - push_item_name(tcx, parent_def_id, true, output); - output.push_str("::"); + // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of + // an artificial `enum$<>` type, as defined in msvc_enum_fallback(). + if cpp_like_debuginfo && matches!(t.kind(), ty::Generator(..)) { + let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap(); + msvc_enum_fallback( + tcx, + ty_and_layout, + &|output, visited| { + push_closure_or_generator_name(tcx, def_id, substs, true, output, visited); + }, + output, + visited, + ); + } else { + push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited); } - - let mut label = String::with_capacity(20); - write!(&mut label, "{}_env", generator_kind_label(tcx.generator_kind(def_id))).unwrap(); - - push_disambiguated_special_name( - &label, - def_key.disambiguated_data.disambiguator, - cpp_like_debuginfo, - output, - ); - - // We also need to add the generic arguments of the async fn/generator or - // the enclosing function (for closures or async blocks), so that we end - // up with a unique name for every instantiation. - - // Find the generics of the enclosing function, as defined in the source code. - let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); - let generics = tcx.generics_of(enclosing_fn_def_id); - - // Truncate the substs to the length of the above generics. This will cut off - // anything closure- or generator-specific. - let substs = substs.truncate_to(tcx, generics); - push_generic_params_internal(tcx, substs, output, visited); } // Type parameters from polymorphized functions. ty::Param(_) => { - output.push_str(&format!("{:?}", t)); + write!(output, "{:?}", t).unwrap(); } ty::Error(_) | ty::Infer(_) @@ -408,24 +404,32 @@ fn push_debuginfo_type_name<'tcx>( // `EnumMemberDescriptionFactor::create_member_descriptions`. fn msvc_enum_fallback<'tcx>( tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - def: AdtDef<'tcx>, - substs: SubstsRef<'tcx>, + ty_and_layout: TyAndLayout<'tcx>, + push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet>), output: &mut String, visited: &mut FxHashSet>, ) { - let layout = tcx.layout_of(tcx.param_env(def.did()).and(ty)).expect("layout error"); + debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout)); + let ty = ty_and_layout.ty; output.push_str("enum$<"); - push_item_name(tcx, def.did(), true, output); - push_generic_params_internal(tcx, substs, output, visited); + push_inner(output, visited); + + let variant_name = |variant_index| match ty.kind() { + ty::Adt(adt_def, _) => { + debug_assert!(adt_def.is_enum()); + Cow::from(adt_def.variant(variant_index).name.as_str()) + } + ty::Generator(..) => GeneratorSubsts::variant_name(variant_index), + _ => unreachable!(), + }; if let Variants::Multiple { tag_encoding: TagEncoding::Niche { dataful_variant, .. }, tag, variants, .. - } = &layout.variants + } = &ty_and_layout.variants { let dataful_variant_layout = &variants[*dataful_variant]; @@ -439,16 +443,13 @@ fn push_debuginfo_type_name<'tcx>( let max = dataful_discriminant_range.end; let max = tag.value.size(&tcx).truncate(max); - let dataful_variant_name = def.variant(*dataful_variant).name.as_str(); - - output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name)); - } else if let Variants::Single { index: variant_idx } = &layout.variants { + let dataful_variant_name = variant_name(*dataful_variant); + write!(output, ", {}, {}, {}", min, max, dataful_variant_name).unwrap(); + } else if let Variants::Single { index: variant_idx } = &ty_and_layout.variants { // Uninhabited enums can't be constructed and should never need to be visualized so // skip this step for them. - if def.variants().len() != 0 { - let variant = def.variant(*variant_idx).name.as_str(); - - output.push_str(&format!(", {}", variant)); + if !ty_and_layout.abi.is_uninhabited() { + write!(output, ", {}", variant_name(*variant_idx)).unwrap(); } } push_close_angle_bracket(true, output); @@ -700,6 +701,49 @@ pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, out push_generic_params_internal(tcx, substs, output, &mut visited); } +fn push_closure_or_generator_name<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + qualified: bool, + output: &mut String, + visited: &mut FxHashSet>, +) { + // Name will be "{closure_env#0}", "{generator_env#0}", or + // "{async_fn_env#0}", etc. + let def_key = tcx.def_key(def_id); + let generator_kind = tcx.generator_kind(def_id); + + if qualified { + let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; + push_item_name(tcx, parent_def_id, true, output); + output.push_str("::"); + } + + let mut label = String::with_capacity(20); + write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap(); + + push_disambiguated_special_name( + &label, + def_key.disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); + + // We also need to add the generic arguments of the async fn/generator or + // the enclosing function (for closures or async blocks), so that we end + // up with a unique name for every instantiation. + + // Find the generics of the enclosing function, as defined in the source code. + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); + let generics = tcx.generics_of(enclosing_fn_def_id); + + // Truncate the substs to the length of the above generics. This will cut off + // anything closure- or generator-specific. + let substs = substs.truncate_to(tcx, generics); + push_generic_params_internal(tcx, substs, output, visited); +} + fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { // MSVC debugger always treats `>>` as a shift, even when parsing templates, // so add a space to avoid confusion. diff --git a/src/test/codegen/async-fn-debug-msvc.rs b/src/test/codegen/async-fn-debug-msvc.rs index b10e662b5bb..8995605e3dd 100644 --- a/src/test/codegen/async-fn-debug-msvc.rs +++ b/src/test/codegen/async-fn-debug-msvc.rs @@ -16,7 +16,7 @@ async fn async_fn_test() { // FIXME: No way to reliably check the filename. -// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0", {{.*}}, align: {{32|64}}, +// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$", // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. // CHECK-SAME: file: [[FILE:![0-9]*]], line: 11, diff --git a/src/test/codegen/generator-debug-msvc.rs b/src/test/codegen/generator-debug-msvc.rs index a6e56a6bd57..74b1eb948b0 100644 --- a/src/test/codegen/generator-debug-msvc.rs +++ b/src/test/codegen/generator-debug-msvc.rs @@ -20,7 +20,7 @@ fn generator_test() -> impl Generator { // FIXME: No way to reliably check the filename. -// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0" +// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$" // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. // CHECK-SAME: file: [[FILE:![0-9]*]], line: 14, diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index aee19736e7e..a972943d58e 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -37,6 +37,37 @@ // lldb-command:print b // lldbg-check:(generator_objects::main::{generator_env#0}) $3 = +// === CDB TESTS =================================================================================== + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Unresumed [Type: enum$] +// cdb-check: [variant] : Unresumed +// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *] + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Suspend0 [Type: enum$] +// cdb-check: [variant] : Suspend0 +// cdb-check: [+0x008] c : 6 [Type: int] +// cdb-check: [+0x00c] d : 7 [Type: int] +// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *] + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Suspend1 [Type: enum$] +// cdb-check: [variant] : Suspend1 +// cdb-check: [+0x008] c : 7 [Type: int] +// cdb-check: [+0x00c] d : 8 [Type: int] +// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *] + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Returned [Type: enum$] +// cdb-check: [] [Type: enum$] +// cdb-check: [variant] : Returned +// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *] + #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section]