Auto merge of #94261 - michaelwoerister:debuginfo-types-refactor, r=wesleywiser
debuginfo: Refactor debuginfo generation for types This PR implements the refactoring of the `rustc_codegen_llvm::debuginfo::metadata` module as described in MCP https://github.com/rust-lang/compiler-team/issues/482. In particular it - changes names to use `di_node` instead of `metadata` - uniformly names all functions that build new debuginfo nodes `build_xyz_di_node` - renames `CrateDebugContext` to `CodegenUnitDebugContext` (which is more accurate) - removes outdated parts from `compiler/rustc_codegen_llvm/src/debuginfo/doc.md` - moves `TypeMap` and functions that work directly work with it to a new `type_map` module - moves enum related builder functions to a new `enums` module - splits enum debuginfo building for the native and cpp-like cases, since they are mostly separate - uses `SmallVec` instead of `Vec` in many places - removes the old infrastructure for dealing with recursion cycles (`create_and_register_recursive_type_forward_declaration()`, `RecursiveTypeDescription`, `set_members_of_composite_type()`, `MemberDescription`, `MemberDescriptionFactory`, `prepare_xyz_metadata()`, etc) - adds `type_map::build_type_with_children()` as a replacement for dealing with recursion cycles - adds many (doc-)comments explaining what's going on - changes cpp-like naming for C-Style enums so they don't get a `enum$<...>` name (because the NatVis visualizer does not apply to them) - fixes detection of what is a C-style enum because some enums where classified as C-style even though they have fields - changes cpp-like naming for generator enums so that NatVis works for them - changes the position of discriminant debuginfo node so it is consistently nested inside the top-level union instead of, sometimes, next to it The following could be done in subsequent PRs: - add caching for `closure_saved_names_of_captured_variables` - add caching for `generator_layout_and_saved_local_names` - fix inconsistent handling of what is considered a C-style enum wrt to debuginfo - rename `metadata` module to `types` - move common generator fields to front instead of appending them This PR is based on https://github.com/rust-lang/rust/pull/93644 which is not merged yet. Right now, the changes are all done in one big commit. They could be split into smaller commits but hopefully the list of changes above makes it tractable to review them as a single commit too. For now: r? `@ghost` (let's see if this affects compile times)
This commit is contained in:
commit
040703018c
24 changed files with 2474 additions and 1900 deletions
|
@ -1,2 +1,34 @@
|
|||
use rustc_middle::ty::{self, layout::TyAndLayout};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
// FIXME(eddyb) find a place for this (or a way to replace it).
|
||||
pub mod type_names;
|
||||
|
||||
/// Returns true if we want to generate a DW_TAG_enumeration_type description for
|
||||
/// this instead of a DW_TAG_struct_type with DW_TAG_variant_part.
|
||||
///
|
||||
/// NOTE: This is somewhat inconsistent right now: For empty enums and enums with a single
|
||||
/// fieldless variant, we generate DW_TAG_struct_type, although a
|
||||
/// DW_TAG_enumeration_type would be a better fit.
|
||||
pub fn wants_c_like_enum_debuginfo<'tcx>(enum_type_and_layout: TyAndLayout<'tcx>) -> bool {
|
||||
match enum_type_and_layout.ty.kind() {
|
||||
ty::Adt(adt_def, _) => {
|
||||
if !adt_def.is_enum() {
|
||||
return false;
|
||||
}
|
||||
|
||||
match adt_def.variants().len() {
|
||||
0 => false,
|
||||
1 => {
|
||||
// Univariant enums unless they are zero-sized
|
||||
enum_type_and_layout.size != Size::ZERO && adt_def.all_fields().count() == 0
|
||||
}
|
||||
_ => {
|
||||
// Enums with more than one variant if they have no fields
|
||||
adt_def.all_fields().count() == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,18 @@ 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, 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;
|
||||
|
||||
// Compute the name of the type as it should be stored in debuginfo. Does not do
|
||||
// any caching, i.e., calling the function twice with the same type will also do
|
||||
// the work twice. The `qualified` parameter only affects the first level of the
|
||||
|
@ -71,8 +74,19 @@ fn push_debuginfo_type_name<'tcx>(
|
|||
ty::Float(float_ty) => output.push_str(float_ty.name_str()),
|
||||
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
|
||||
ty::Adt(def, substs) => {
|
||||
if def.is_enum() && cpp_like_debuginfo {
|
||||
msvc_enum_fallback(tcx, t, def, substs, output, visited);
|
||||
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,
|
||||
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);
|
||||
|
@ -348,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}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
|
||||
// "{async_fn_env#0}<T1, T2, ...>", 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 && t.is_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(_)
|
||||
|
@ -404,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<Ty<'tcx>>),
|
||||
output: &mut String,
|
||||
visited: &mut FxHashSet<Ty<'tcx>>,
|
||||
) {
|
||||
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];
|
||||
|
||||
|
@ -435,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);
|
||||
|
@ -696,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<Ty<'tcx>>,
|
||||
) {
|
||||
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
|
||||
// "{async_fn_env#0}<T1, T2, ...>", 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.
|
||||
|
|
|
@ -78,7 +78,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
|
|||
let align = cx.data_layout().pointer_align.abi;
|
||||
let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
|
||||
|
||||
cx.create_vtable_metadata(ty, trait_ref, vtable);
|
||||
cx.create_vtable_debuginfo(ty, trait_ref, vtable);
|
||||
cx.vtables().borrow_mut().insert((ty, trait_ref), vtable);
|
||||
vtable
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_target::abi::call::FnAbi;
|
|||
use rustc_target::abi::Size;
|
||||
|
||||
pub trait DebugInfoMethods<'tcx>: BackendTypes {
|
||||
fn create_vtable_metadata(
|
||||
fn create_vtable_debuginfo(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue