mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-04 02:54:00 +00:00
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
@ -31,7 +31,7 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
|
||||
fn create_vtable_debuginfo(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
|
||||
// TODO(antoyo)
|
||||
}
|
||||
|
||||
|
@ -140,8 +140,8 @@ pub(crate) unsafe fn codegen(
|
||||
llvm::LLVMDisposeBuilder(llbuilder);
|
||||
|
||||
if tcx.sess.opts.debuginfo != DebugInfo::None {
|
||||
let dbg_cx = debuginfo::CrateDebugContext::new(llmod);
|
||||
debuginfo::metadata::compile_unit_metadata(tcx, module_name, &dbg_cx);
|
||||
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
|
||||
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
|
||||
dbg_cx.finalize(tcx.sess);
|
||||
}
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
|
||||
llvm::LLVMSetGlobalConstant(g, llvm::True);
|
||||
}
|
||||
|
||||
debuginfo::create_global_var_metadata(self, def_id, g);
|
||||
debuginfo::build_global_var_di_node(self, def_id, g);
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
|
@ -95,7 +95,7 @@ pub struct CodegenCx<'ll, 'tcx> {
|
||||
pub isize_ty: &'ll Type,
|
||||
|
||||
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
|
||||
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
|
||||
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
|
||||
|
||||
eh_personality: Cell<Option<&'ll Value>>,
|
||||
eh_catch_typeinfo: Cell<Option<&'ll Value>>,
|
||||
@ -396,8 +396,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
};
|
||||
|
||||
let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None {
|
||||
let dctx = debuginfo::CrateDebugContext::new(llmod);
|
||||
debuginfo::metadata::compile_unit_metadata(tcx, codegen_unit.name().as_str(), &dctx);
|
||||
let dctx = debuginfo::CodegenUnitDebugContext::new(llmod);
|
||||
debuginfo::metadata::build_compile_unit_di_node(
|
||||
tcx,
|
||||
codegen_unit.name().as_str(),
|
||||
&dctx,
|
||||
);
|
||||
Some(dctx)
|
||||
} else {
|
||||
None
|
||||
|
@ -34,7 +34,7 @@ The function will take care of probing the cache for an existing node for
|
||||
that exact file path.
|
||||
|
||||
All private state used by the module is stored within either the
|
||||
CrateDebugContext struct (owned by the CodegenCx) or the
|
||||
CodegenUnitDebugContext struct (owned by the CodegenCx) or the
|
||||
FunctionDebugContext (owned by the FunctionCx).
|
||||
|
||||
This file consists of three conceptual sections:
|
||||
@ -72,7 +72,7 @@ describe(t = List)
|
||||
...
|
||||
```
|
||||
|
||||
To break cycles like these, we use "forward declarations". That is, when
|
||||
To break cycles like these, we use "stubs". That is, when
|
||||
the algorithm encounters a possibly recursive type (any struct or enum), it
|
||||
immediately creates a type description node and inserts it into the cache
|
||||
*before* describing the members of the type. This type description is just
|
||||
@ -80,13 +80,8 @@ a stub (as type members are not described and added to it yet) but it
|
||||
allows the algorithm to already refer to the type. After the stub is
|
||||
inserted into the cache, the algorithm continues as before. If it now
|
||||
encounters a recursive reference, it will hit the cache and does not try to
|
||||
describe the type anew.
|
||||
|
||||
This behavior is encapsulated in the 'RecursiveTypeDescription' enum,
|
||||
which represents a kind of continuation, storing all state needed to
|
||||
continue traversal at the type members after the type has been registered
|
||||
with the cache. (This implementation approach might be a tad over-
|
||||
engineered and may change in the future)
|
||||
describe the type anew. This behavior is encapsulated in the
|
||||
`type_map::build_type_with_children()` function.
|
||||
|
||||
|
||||
## Source Locations and Line Information
|
||||
@ -134,47 +129,3 @@ detection. The `create_argument_metadata()` and related functions take care
|
||||
of linking the `llvm.dbg.declare` instructions to the correct source
|
||||
locations even while source location emission is still disabled, so there
|
||||
is no need to do anything special with source location handling here.
|
||||
|
||||
## Unique Type Identification
|
||||
|
||||
In order for link-time optimization to work properly, LLVM needs a unique
|
||||
type identifier that tells it across compilation units which types are the
|
||||
same as others. This type identifier is created by
|
||||
`TypeMap::get_unique_type_id_of_type()` using the following algorithm:
|
||||
|
||||
1. Primitive types have their name as ID
|
||||
|
||||
2. Structs, enums and traits have a multipart identifier
|
||||
|
||||
1. The first part is the SVH (strict version hash) of the crate they
|
||||
were originally defined in
|
||||
|
||||
2. The second part is the ast::NodeId of the definition in their
|
||||
original crate
|
||||
|
||||
3. The final part is a concatenation of the type IDs of their concrete
|
||||
type arguments if they are generic types.
|
||||
|
||||
3. Tuple-, pointer-, and function types are structurally identified, which
|
||||
means that they are equivalent if their component types are equivalent
|
||||
(i.e., `(i32, i32)` is the same regardless in which crate it is used).
|
||||
|
||||
This algorithm also provides a stable ID for types that are defined in one
|
||||
crate but instantiated from metadata within another crate. We just have to
|
||||
take care to always map crate and `NodeId`s back to the original crate
|
||||
context.
|
||||
|
||||
As a side-effect these unique type IDs also help to solve a problem arising
|
||||
from lifetime parameters. Since lifetime parameters are completely omitted
|
||||
in debuginfo, more than one `Ty` instance may map to the same debuginfo
|
||||
type metadata, that is, some struct `Struct<'a>` may have N instantiations
|
||||
with different concrete substitutions for `'a`, and thus there will be N
|
||||
`Ty` instances for the type `Struct<'a>` even though it is not generic
|
||||
otherwise. Unfortunately this means that we cannot use `ty::type_id()` as
|
||||
cheap identifier for type metadata -- we have done this in the past, but it
|
||||
led to unnecessary metadata duplication in the best case and LLVM
|
||||
assertions in the worst. However, the unique type ID as described above
|
||||
*can* be used as identifier. Since it is comparatively expensive to
|
||||
construct, though, `ty::type_id()` is still used additionally as an
|
||||
optimization for cases where the exact same type has been seen before
|
||||
(which is most of the time).
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,515 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_codegen_ssa::debuginfo::{
|
||||
type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo,
|
||||
};
|
||||
use rustc_middle::{
|
||||
bug,
|
||||
ty::{
|
||||
self,
|
||||
layout::{LayoutOf, TyAndLayout},
|
||||
util::Discr,
|
||||
AdtDef, GeneratorSubsts,
|
||||
},
|
||||
};
|
||||
use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants};
|
||||
use smallvec::smallvec;
|
||||
|
||||
use crate::{
|
||||
common::CodegenCx,
|
||||
debuginfo::{
|
||||
metadata::{
|
||||
build_field_di_node, closure_saved_names_of_captured_variables,
|
||||
enums::tag_base_type,
|
||||
file_metadata, generator_layout_and_saved_local_names, size_and_align_of,
|
||||
type_map::{self, UniqueTypeId},
|
||||
unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
},
|
||||
utils::DIB,
|
||||
},
|
||||
llvm::{
|
||||
self,
|
||||
debuginfo::{DIFile, DIFlags, DIType},
|
||||
},
|
||||
};
|
||||
|
||||
/// In CPP-like mode, we generate a union of structs for each variant and an
|
||||
/// explicit discriminant field roughly equivalent to the following C/C++ code:
|
||||
///
|
||||
/// ```c
|
||||
/// union enum$<{fully-qualified-name}> {
|
||||
/// struct {variant 0 name} {
|
||||
/// <variant 0 fields>
|
||||
/// } variant0;
|
||||
/// <other variant structs>
|
||||
/// {name} discriminant;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// As you can see, the type name is wrapped `enum$`. This way we can have a
|
||||
/// single NatVis rule for handling all enums.
|
||||
///
|
||||
/// At the LLVM IR level this looks like
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_union_type (top-level type for enum)
|
||||
/// DW_TAG_member (member for variant 1)
|
||||
/// DW_TAG_member (member for variant 2)
|
||||
/// DW_TAG_member (member for variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
/// DW_TAG_enumeration_type (type of tag)
|
||||
/// ```
|
||||
///
|
||||
/// The above encoding applies for enums with a direct tag. For niche-tag we have to do things
|
||||
/// differently in order to allow a NatVis visualizer to extract all the information needed:
|
||||
/// We generate a union of two fields, one for the dataful variant
|
||||
/// and one that just points to the discriminant (which is some field within the dataful variant).
|
||||
/// We also create a DW_TAG_enumeration_type DIE that contains tag values for the non-dataful
|
||||
/// variants and make the discriminant field that type. We then use NatVis to render the enum type
|
||||
/// correctly in Windbg/VS. This will generate debuginfo roughly equivalent to the following C:
|
||||
///
|
||||
/// ```c
|
||||
/// union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> {
|
||||
/// struct <dataful variant name> {
|
||||
/// <fields in dataful variant>
|
||||
/// } dataful_variant;
|
||||
/// enum Discriminant$ {
|
||||
/// <non-dataful variants>
|
||||
/// } discriminant;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The NatVis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>`
|
||||
/// and evaluates `this.discriminant`. If the value is between the min niche and max
|
||||
/// niche, then the enum is in the dataful variant and `this.dataful_variant` is
|
||||
/// rendered. Otherwise, the enum is in one of the non-dataful variants. In that
|
||||
/// case, we just need to render the name of the `this.discriminant` enum.
|
||||
pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
let enum_type = unique_type_id.expect_ty();
|
||||
let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
|
||||
bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
|
||||
};
|
||||
|
||||
let enum_type_and_layout = cx.layout_of(enum_type);
|
||||
let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
|
||||
|
||||
debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
type_map::stub(
|
||||
cx,
|
||||
type_map::Stub::Union,
|
||||
unique_type_id,
|
||||
&enum_type_name,
|
||||
cx.size_and_align_of(enum_type),
|
||||
NO_SCOPE_METADATA,
|
||||
DIFlags::FlagZero,
|
||||
),
|
||||
|cx, enum_type_di_node| {
|
||||
match enum_type_and_layout.variants {
|
||||
Variants::Single { index: variant_index } => {
|
||||
if enum_adt_def.variants().is_empty() {
|
||||
// Uninhabited enums have Variants::Single. We don't generate
|
||||
// any members for them.
|
||||
return smallvec![];
|
||||
}
|
||||
|
||||
build_single_variant_union_fields(
|
||||
cx,
|
||||
enum_adt_def,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
variant_index,
|
||||
)
|
||||
}
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
ref variants,
|
||||
tag_field,
|
||||
..
|
||||
} => build_union_fields_for_direct_tag_enum(
|
||||
cx,
|
||||
enum_adt_def,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
&mut variants.indices(),
|
||||
tag_field,
|
||||
),
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
|
||||
ref variants,
|
||||
tag_field,
|
||||
..
|
||||
} => build_union_fields_for_niche_tag_enum(
|
||||
cx,
|
||||
enum_adt_def,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
dataful_variant,
|
||||
&mut variants.indices(),
|
||||
tag_field,
|
||||
),
|
||||
}
|
||||
},
|
||||
NO_GENERICS,
|
||||
)
|
||||
}
|
||||
|
||||
/// A generator debuginfo node looks the same as a that of an enum type.
|
||||
///
|
||||
/// See [build_enum_type_di_node] for more information.
|
||||
pub(super) fn build_generator_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
let generator_type = unique_type_id.expect_ty();
|
||||
let generator_type_and_layout = cx.layout_of(generator_type);
|
||||
let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false);
|
||||
|
||||
debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout));
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
type_map::stub(
|
||||
cx,
|
||||
type_map::Stub::Union,
|
||||
unique_type_id,
|
||||
&generator_type_name,
|
||||
size_and_align_of(generator_type_and_layout),
|
||||
NO_SCOPE_METADATA,
|
||||
DIFlags::FlagZero,
|
||||
),
|
||||
|cx, generator_type_di_node| match generator_type_and_layout.variants {
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => {
|
||||
build_union_fields_for_direct_tag_generator(
|
||||
cx,
|
||||
generator_type_and_layout,
|
||||
generator_type_di_node,
|
||||
)
|
||||
}
|
||||
Variants::Single { .. }
|
||||
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => {
|
||||
bug!(
|
||||
"Encountered generator with non-direct-tag layout: {:?}",
|
||||
generator_type_and_layout
|
||||
)
|
||||
}
|
||||
},
|
||||
NO_GENERICS,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_single_variant_union_fields<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_adt_def: AdtDef<'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
variant_index: VariantIdx,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
let variant_layout = enum_type_and_layout.for_variant(cx, variant_index);
|
||||
let variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node(
|
||||
cx,
|
||||
enum_type_and_layout.ty,
|
||||
enum_type_di_node,
|
||||
variant_index,
|
||||
enum_adt_def.variant(variant_index),
|
||||
variant_layout,
|
||||
);
|
||||
|
||||
// NOTE: The field name of the union is the same as the variant name, not "variant0".
|
||||
let variant_name = enum_adt_def.variant(variant_index).name.as_str();
|
||||
|
||||
smallvec![build_field_di_node(
|
||||
cx,
|
||||
enum_type_di_node,
|
||||
variant_name,
|
||||
// NOTE: We use the size and align of the entire type, not from variant_layout
|
||||
// since the later is sometimes smaller (if it has fewer fields).
|
||||
size_and_align_of(enum_type_and_layout),
|
||||
Size::ZERO,
|
||||
DIFlags::FlagZero,
|
||||
variant_struct_type_di_node,
|
||||
)]
|
||||
}
|
||||
|
||||
fn build_union_fields_for_direct_tag_enum<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_adt_def: AdtDef<'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
variant_indices: &mut dyn Iterator<Item = VariantIdx>,
|
||||
tag_field: usize,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_indices
|
||||
.map(|variant_index| {
|
||||
let variant_layout = enum_type_and_layout.for_variant(cx, variant_index);
|
||||
|
||||
VariantFieldInfo {
|
||||
variant_index,
|
||||
variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
|
||||
cx,
|
||||
enum_type_and_layout.ty,
|
||||
enum_type_di_node,
|
||||
variant_index,
|
||||
enum_adt_def.variant(variant_index),
|
||||
variant_layout,
|
||||
),
|
||||
source_info: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let discr_type_name = cx.tcx.item_name(enum_adt_def.did());
|
||||
let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
|
||||
let discr_type_di_node = super::build_enumeration_type_di_node(
|
||||
cx,
|
||||
discr_type_name.as_str(),
|
||||
tag_base_type,
|
||||
&mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
|
||||
(discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
|
||||
}),
|
||||
enum_type_di_node,
|
||||
);
|
||||
|
||||
build_union_fields_for_direct_tag_enum_or_generator(
|
||||
cx,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
&variant_field_infos,
|
||||
discr_type_di_node,
|
||||
tag_field,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_union_fields_for_niche_tag_enum<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_adt_def: AdtDef<'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
dataful_variant_index: VariantIdx,
|
||||
variant_indices: &mut dyn Iterator<Item = VariantIdx>,
|
||||
tag_field: usize,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
let dataful_variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node(
|
||||
cx,
|
||||
enum_type_and_layout.ty,
|
||||
enum_type_di_node,
|
||||
dataful_variant_index,
|
||||
&enum_adt_def.variant(dataful_variant_index),
|
||||
enum_type_and_layout.for_variant(cx, dataful_variant_index),
|
||||
);
|
||||
|
||||
let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
|
||||
// Create an DW_TAG_enumerator for each variant except the dataful one.
|
||||
let discr_type_di_node = super::build_enumeration_type_di_node(
|
||||
cx,
|
||||
"Discriminant$",
|
||||
tag_base_type,
|
||||
&mut variant_indices.filter_map(|variant_index| {
|
||||
if let Some(discr_val) =
|
||||
super::compute_discriminant_value(cx, enum_type_and_layout, variant_index)
|
||||
{
|
||||
let discr = Discr { val: discr_val as u128, ty: tag_base_type };
|
||||
let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
|
||||
Some((discr, variant_name))
|
||||
} else {
|
||||
debug_assert_eq!(variant_index, dataful_variant_index);
|
||||
None
|
||||
}
|
||||
}),
|
||||
enum_type_di_node,
|
||||
);
|
||||
|
||||
smallvec![
|
||||
build_field_di_node(
|
||||
cx,
|
||||
enum_type_di_node,
|
||||
"dataful_variant",
|
||||
size_and_align_of(enum_type_and_layout),
|
||||
Size::ZERO,
|
||||
DIFlags::FlagZero,
|
||||
dataful_variant_struct_type_di_node,
|
||||
),
|
||||
build_field_di_node(
|
||||
cx,
|
||||
enum_type_di_node,
|
||||
"discriminant",
|
||||
cx.size_and_align_of(tag_base_type),
|
||||
enum_type_and_layout.fields.offset(tag_field),
|
||||
DIFlags::FlagZero,
|
||||
discr_type_di_node,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
generator_type_and_layout: TyAndLayout<'tcx>,
|
||||
generator_type_di_node: &'ll DIType,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } = generator_type_and_layout.variants else {
|
||||
bug!("This function only supports layouts with direcly encoded tags.")
|
||||
};
|
||||
|
||||
let (generator_def_id, generator_substs) = match generator_type_and_layout.ty.kind() {
|
||||
&ty::Generator(def_id, substs, _) => (def_id, substs.as_generator()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let (generator_layout, state_specific_upvar_names) =
|
||||
generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
|
||||
|
||||
let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
|
||||
let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx);
|
||||
|
||||
// Build the type node for each field.
|
||||
let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_range
|
||||
.clone()
|
||||
.map(|variant_index| {
|
||||
let variant_struct_type_di_node = super::build_generator_variant_struct_type_di_node(
|
||||
cx,
|
||||
variant_index,
|
||||
generator_type_and_layout,
|
||||
generator_type_di_node,
|
||||
generator_layout,
|
||||
&state_specific_upvar_names,
|
||||
&common_upvar_names,
|
||||
);
|
||||
|
||||
let span = generator_layout.variant_source_info[variant_index].span;
|
||||
let source_info = if !span.is_dummy() {
|
||||
let loc = cx.lookup_debug_loc(span.lo());
|
||||
Some((file_metadata(cx, &loc.file), loc.line as c_uint))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
VariantFieldInfo { variant_index, variant_struct_type_di_node, source_info }
|
||||
})
|
||||
.collect();
|
||||
|
||||
let tag_base_type = tag_base_type(cx, generator_type_and_layout);
|
||||
let discr_type_name = "Discriminant$";
|
||||
let discr_type_di_node = super::build_enumeration_type_di_node(
|
||||
cx,
|
||||
discr_type_name,
|
||||
tag_base_type,
|
||||
&mut generator_substs
|
||||
.discriminants(generator_def_id, cx.tcx)
|
||||
.map(|(variant_index, discr)| (discr, GeneratorSubsts::variant_name(variant_index))),
|
||||
generator_type_di_node,
|
||||
);
|
||||
|
||||
build_union_fields_for_direct_tag_enum_or_generator(
|
||||
cx,
|
||||
generator_type_and_layout,
|
||||
generator_type_di_node,
|
||||
&variant_field_infos[..],
|
||||
discr_type_di_node,
|
||||
tag_field,
|
||||
)
|
||||
}
|
||||
|
||||
/// This is a helper function shared between enums and generators that makes sure fields have the
|
||||
/// expect names.
|
||||
fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
variant_field_infos: &[VariantFieldInfo<'ll>],
|
||||
discr_type_di_node: &'ll DIType,
|
||||
tag_field: usize,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
let mut unions_fields = SmallVec::with_capacity(variant_field_infos.len() + 1);
|
||||
|
||||
// We create a field in the union for each variant ...
|
||||
unions_fields.extend(variant_field_infos.into_iter().map(|variant_member_info| {
|
||||
let (file_di_node, line_number) = variant_member_info
|
||||
.source_info
|
||||
.unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER));
|
||||
|
||||
let field_name = variant_union_field_name(variant_member_info.variant_index);
|
||||
let (size, align) = size_and_align_of(enum_type_and_layout);
|
||||
|
||||
// We use LLVMRustDIBuilderCreateMemberType() member type directly because
|
||||
// the build_field_di_node() function does not support specifying a source location,
|
||||
// which is something that we don't do anywhere else.
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateMemberType(
|
||||
DIB(cx),
|
||||
enum_type_di_node,
|
||||
field_name.as_ptr().cast(),
|
||||
field_name.len(),
|
||||
file_di_node,
|
||||
line_number,
|
||||
// NOTE: We use the size and align of the entire type, not from variant_layout
|
||||
// since the later is sometimes smaller (if it has fewer fields).
|
||||
size.bits(),
|
||||
align.bits() as u32,
|
||||
// Union fields are always at offset zero
|
||||
Size::ZERO.bits(),
|
||||
DIFlags::FlagZero,
|
||||
variant_member_info.variant_struct_type_di_node,
|
||||
)
|
||||
}
|
||||
}));
|
||||
|
||||
debug_assert_eq!(
|
||||
cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
|
||||
cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout))
|
||||
);
|
||||
|
||||
// ... and a field for the discriminant.
|
||||
unions_fields.push(build_field_di_node(
|
||||
cx,
|
||||
enum_type_di_node,
|
||||
"discriminant",
|
||||
cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
|
||||
enum_type_and_layout.fields.offset(tag_field),
|
||||
DIFlags::FlagZero,
|
||||
discr_type_di_node,
|
||||
));
|
||||
|
||||
unions_fields
|
||||
}
|
||||
|
||||
/// Information about a single field of the top-level DW_TAG_union_type.
|
||||
struct VariantFieldInfo<'ll> {
|
||||
variant_index: VariantIdx,
|
||||
variant_struct_type_di_node: &'ll DIType,
|
||||
source_info: Option<(&'ll DIFile, c_uint)>,
|
||||
}
|
||||
|
||||
fn variant_union_field_name(variant_index: VariantIdx) -> Cow<'static, str> {
|
||||
const PRE_ALLOCATED: [&str; 16] = [
|
||||
"variant0",
|
||||
"variant1",
|
||||
"variant2",
|
||||
"variant3",
|
||||
"variant4",
|
||||
"variant5",
|
||||
"variant6",
|
||||
"variant7",
|
||||
"variant8",
|
||||
"variant9",
|
||||
"variant10",
|
||||
"variant11",
|
||||
"variant12",
|
||||
"variant13",
|
||||
"variant14",
|
||||
"variant15",
|
||||
];
|
||||
|
||||
PRE_ALLOCATED
|
||||
.get(variant_index.as_usize())
|
||||
.map(|&s| Cow::from(s))
|
||||
.unwrap_or_else(|| format!("variant{}", variant_index.as_usize()).into())
|
||||
}
|
437
compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
Normal file
437
compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
Normal file
@ -0,0 +1,437 @@
|
||||
use rustc_codegen_ssa::debuginfo::{
|
||||
type_names::{compute_debuginfo_type_name, cpp_like_debuginfo},
|
||||
wants_c_like_enum_debuginfo,
|
||||
};
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::{
|
||||
bug,
|
||||
mir::{Field, GeneratorLayout, GeneratorSavedLocal},
|
||||
ty::{
|
||||
self,
|
||||
layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
|
||||
util::Discr,
|
||||
AdtDef, GeneratorSubsts, Ty, VariantDef,
|
||||
},
|
||||
};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::{HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
common::CodegenCx,
|
||||
debuginfo::{
|
||||
metadata::{
|
||||
build_field_di_node, build_generic_type_param_di_nodes, type_di_node,
|
||||
type_map::{self, Stub},
|
||||
unknown_file_metadata, UNKNOWN_LINE_NUMBER,
|
||||
},
|
||||
utils::{create_DIArray, get_namespace_for_item, DIB},
|
||||
},
|
||||
llvm::{
|
||||
self,
|
||||
debuginfo::{DIFlags, DIType},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
size_and_align_of,
|
||||
type_map::{DINodeCreationResult, UniqueTypeId},
|
||||
SmallVec,
|
||||
};
|
||||
|
||||
mod cpp_like;
|
||||
mod native;
|
||||
|
||||
pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
let enum_type = unique_type_id.expect_ty();
|
||||
let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
|
||||
bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
|
||||
};
|
||||
|
||||
let enum_type_and_layout = cx.layout_of(enum_type);
|
||||
|
||||
if wants_c_like_enum_debuginfo(enum_type_and_layout) {
|
||||
return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
|
||||
}
|
||||
|
||||
if cpp_like_debuginfo(cx.tcx) {
|
||||
cpp_like::build_enum_type_di_node(cx, unique_type_id)
|
||||
} else {
|
||||
native::build_enum_type_di_node(cx, unique_type_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn build_generator_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
if cpp_like_debuginfo(cx.tcx) {
|
||||
cpp_like::build_generator_di_node(cx, unique_type_id)
|
||||
} else {
|
||||
native::build_generator_di_node(cx, unique_type_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields.
|
||||
///
|
||||
/// The resulting debuginfo will be a DW_TAG_enumeration_type.
|
||||
fn build_c_style_enum_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_adt_def: AdtDef<'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
|
||||
DINodeCreationResult {
|
||||
di_node: build_enumeration_type_di_node(
|
||||
cx,
|
||||
&compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
|
||||
tag_base_type(cx, enum_type_and_layout),
|
||||
&mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
|
||||
(discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
|
||||
}),
|
||||
containing_scope,
|
||||
),
|
||||
already_stored_in_typemap: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the type with which we want to describe the tag of the given enum or generator.
|
||||
fn tag_base_type<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert!(match enum_type_and_layout.ty.kind() {
|
||||
ty::Generator(..) => true,
|
||||
ty::Adt(adt_def, _) => adt_def.is_enum(),
|
||||
_ => false,
|
||||
});
|
||||
|
||||
match enum_type_and_layout.layout.variants() {
|
||||
// A single-variant enum has no discriminant.
|
||||
Variants::Single { .. } => {
|
||||
bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
|
||||
}
|
||||
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
|
||||
// Niche tags are always normalized to unsized integers of the correct size.
|
||||
match tag.value {
|
||||
Primitive::Int(t, _) => t,
|
||||
Primitive::F32 => Integer::I32,
|
||||
Primitive::F64 => Integer::I64,
|
||||
Primitive::Pointer => {
|
||||
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be
|
||||
// a RawPtr. CodeView doesn't know what to do with enums whose base type is a
|
||||
// pointer so we fix this up to just be `usize`.
|
||||
// DWARF might be able to deal with this but with an integer type we are on
|
||||
// the safe side there too.
|
||||
cx.data_layout().ptr_sized_integer()
|
||||
}
|
||||
}
|
||||
.to_ty(cx.tcx, false)
|
||||
}
|
||||
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
|
||||
// Direct tags preserve the sign.
|
||||
tag.value.to_ty(cx.tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants.
|
||||
/// This is a helper function and does not register anything in the type map by itself.
|
||||
///
|
||||
/// `variants` is an iterator of (discr-value, variant-name).
|
||||
///
|
||||
// NOTE: Handling of discriminant values is somewhat inconsistent. They can appear as u128,
|
||||
// u64, and i64. Here everything gets mapped to i64 because that's what LLVM's API expects.
|
||||
fn build_enumeration_type_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
type_name: &str,
|
||||
base_type: Ty<'tcx>,
|
||||
variants: &mut dyn Iterator<Item = (Discr<'tcx>, Cow<'tcx, str>)>,
|
||||
containing_scope: &'ll DIType,
|
||||
) -> &'ll DIType {
|
||||
let is_unsigned = match base_type.kind() {
|
||||
ty::Int(_) => false,
|
||||
ty::Uint(_) => true,
|
||||
_ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
|
||||
};
|
||||
|
||||
let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants
|
||||
.map(|(discr, variant_name)| {
|
||||
unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
|
||||
DIB(cx),
|
||||
variant_name.as_ptr().cast(),
|
||||
variant_name.len(),
|
||||
// FIXME: what if enumeration has i128 discriminant?
|
||||
discr.val as i64,
|
||||
is_unsigned,
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (size, align) = cx.size_and_align_of(base_type);
|
||||
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateEnumerationType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
type_name.as_ptr().cast(),
|
||||
type_name.len(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
size.bits(),
|
||||
align.bits() as u32,
|
||||
create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
|
||||
type_di_node(cx, base_type),
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the debuginfo node for the struct type describing a single variant of an enum.
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_structure_type (top-level type for enum)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// DW_TAG_variant (variant 1)
|
||||
/// DW_TAG_variant (variant 2)
|
||||
/// DW_TAG_variant (variant 3)
|
||||
/// ---> DW_TAG_structure_type (type of variant 1)
|
||||
/// ---> DW_TAG_structure_type (type of variant 2)
|
||||
/// ---> DW_TAG_structure_type (type of variant 3)
|
||||
/// ```
|
||||
///
|
||||
/// In CPP-like mode, we have the exact same descriptions for each variant too:
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_union_type (top-level type for enum)
|
||||
/// DW_TAG_member (member for variant 1)
|
||||
/// DW_TAG_member (member for variant 2)
|
||||
/// DW_TAG_member (member for variant 3)
|
||||
/// ---> DW_TAG_structure_type (type of variant 1)
|
||||
/// ---> DW_TAG_structure_type (type of variant 2)
|
||||
/// ---> DW_TAG_structure_type (type of variant 3)
|
||||
/// DW_TAG_enumeration_type (type of tag)
|
||||
/// ```
|
||||
///
|
||||
/// The node looks like:
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_structure_type
|
||||
/// DW_AT_name <name-of-variant>
|
||||
/// DW_AT_byte_size 0x00000010
|
||||
/// DW_AT_alignment 0x00000008
|
||||
/// DW_TAG_member
|
||||
/// DW_AT_name <name-of-field-0>
|
||||
/// DW_AT_type <0x0000018e>
|
||||
/// DW_AT_alignment 0x00000004
|
||||
/// DW_AT_data_member_location 4
|
||||
/// DW_TAG_member
|
||||
/// DW_AT_name <name-of-field-1>
|
||||
/// DW_AT_type <0x00000195>
|
||||
/// DW_AT_alignment 0x00000008
|
||||
/// DW_AT_data_member_location 8
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// The type of a variant is always a struct type with the name of the variant
|
||||
/// and a DW_TAG_member for each field (but not the discriminant).
|
||||
fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_type: Ty<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
variant_index: VariantIdx,
|
||||
variant_def: &VariantDef,
|
||||
variant_layout: TyAndLayout<'tcx>,
|
||||
) -> &'ll DIType {
|
||||
debug_assert_eq!(variant_layout.ty, enum_type);
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
type_map::stub(
|
||||
cx,
|
||||
Stub::Struct,
|
||||
UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index),
|
||||
variant_def.name.as_str(),
|
||||
// NOTE: We use size and align of enum_type, not from variant_layout:
|
||||
cx.size_and_align_of(enum_type),
|
||||
Some(enum_type_di_node),
|
||||
DIFlags::FlagZero,
|
||||
),
|
||||
|cx, struct_type_di_node| {
|
||||
(0..variant_layout.fields.count())
|
||||
.map(|field_index| {
|
||||
let field_name = if variant_def.ctor_kind != CtorKind::Fn {
|
||||
// Fields have names
|
||||
Cow::from(variant_def.fields[field_index].name.as_str())
|
||||
} else {
|
||||
// Tuple-like
|
||||
super::tuple_field_name(field_index)
|
||||
};
|
||||
|
||||
let field_layout = variant_layout.field(cx, field_index);
|
||||
|
||||
build_field_di_node(
|
||||
cx,
|
||||
struct_type_di_node,
|
||||
&field_name,
|
||||
(field_layout.size, field_layout.align.abi),
|
||||
variant_layout.fields.offset(field_index),
|
||||
DIFlags::FlagZero,
|
||||
type_di_node(cx, field_layout.ty),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
|cx| build_generic_type_param_di_nodes(cx, enum_type),
|
||||
)
|
||||
.di_node
|
||||
}
|
||||
|
||||
/// Build the struct type for describing a single generator state.
|
||||
/// See [build_generator_variant_struct_type_di_node].
|
||||
///
|
||||
/// ```txt
|
||||
///
|
||||
/// DW_TAG_structure_type (top-level type for enum)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// DW_TAG_variant (variant 1)
|
||||
/// DW_TAG_variant (variant 2)
|
||||
/// DW_TAG_variant (variant 3)
|
||||
/// ---> DW_TAG_structure_type (type of variant 1)
|
||||
/// ---> DW_TAG_structure_type (type of variant 2)
|
||||
/// ---> DW_TAG_structure_type (type of variant 3)
|
||||
///
|
||||
/// ```
|
||||
pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
variant_index: VariantIdx,
|
||||
generator_type_and_layout: TyAndLayout<'tcx>,
|
||||
generator_type_di_node: &'ll DIType,
|
||||
generator_layout: &GeneratorLayout<'tcx>,
|
||||
state_specific_upvar_names: &IndexVec<GeneratorSavedLocal, Option<Symbol>>,
|
||||
common_upvar_names: &[String],
|
||||
) -> &'ll DIType {
|
||||
let variant_name = GeneratorSubsts::variant_name(variant_index);
|
||||
let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
|
||||
cx.tcx,
|
||||
generator_type_and_layout.ty,
|
||||
variant_index,
|
||||
);
|
||||
|
||||
let variant_layout = generator_type_and_layout.for_variant(cx, variant_index);
|
||||
|
||||
let generator_substs = match generator_type_and_layout.ty.kind() {
|
||||
ty::Generator(_, substs, _) => substs.as_generator(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
type_map::stub(
|
||||
cx,
|
||||
Stub::Struct,
|
||||
unique_type_id,
|
||||
&variant_name,
|
||||
size_and_align_of(generator_type_and_layout),
|
||||
Some(generator_type_di_node),
|
||||
DIFlags::FlagZero,
|
||||
),
|
||||
|cx, variant_struct_type_di_node| {
|
||||
// Fields that just belong to this variant/state
|
||||
let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
|
||||
.map(|field_index| {
|
||||
let generator_saved_local = generator_layout.variant_fields[variant_index]
|
||||
[Field::from_usize(field_index)];
|
||||
let field_name_maybe = state_specific_upvar_names[generator_saved_local];
|
||||
let field_name = field_name_maybe
|
||||
.as_ref()
|
||||
.map(|s| Cow::from(s.as_str()))
|
||||
.unwrap_or_else(|| super::tuple_field_name(field_index));
|
||||
|
||||
let field_type = variant_layout.field(cx, field_index).ty;
|
||||
|
||||
build_field_di_node(
|
||||
cx,
|
||||
variant_struct_type_di_node,
|
||||
&field_name,
|
||||
cx.size_and_align_of(field_type),
|
||||
variant_layout.fields.offset(field_index),
|
||||
DIFlags::FlagZero,
|
||||
type_di_node(cx, field_type),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Fields that are common to all states
|
||||
let common_fields: SmallVec<_> = generator_substs
|
||||
.prefix_tys()
|
||||
.enumerate()
|
||||
.map(|(index, upvar_ty)| {
|
||||
build_field_di_node(
|
||||
cx,
|
||||
variant_struct_type_di_node,
|
||||
&common_upvar_names[index],
|
||||
cx.size_and_align_of(upvar_ty),
|
||||
generator_type_and_layout.fields.offset(index),
|
||||
DIFlags::FlagZero,
|
||||
type_di_node(cx, upvar_ty),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
state_specific_fields.into_iter().chain(common_fields.into_iter()).collect()
|
||||
},
|
||||
|cx| build_generic_type_param_di_nodes(cx, generator_type_and_layout.ty),
|
||||
)
|
||||
.di_node
|
||||
}
|
||||
|
||||
/// Returns the discriminant value corresponding to the variant index.
|
||||
///
|
||||
/// Will return `None` if there is less than two variants (because then the enum won't have)
|
||||
/// a tag, and if this is the dataful variant of a niche-layout enum (because then there is no
|
||||
/// single discriminant value).
|
||||
fn compute_discriminant_value<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
variant_index: VariantIdx,
|
||||
) -> Option<u64> {
|
||||
match enum_type_and_layout.layout.variants() {
|
||||
&Variants::Single { .. } => None,
|
||||
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some(
|
||||
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val
|
||||
as u64,
|
||||
),
|
||||
&Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant },
|
||||
tag,
|
||||
..
|
||||
} => {
|
||||
if variant_index == dataful_variant {
|
||||
None
|
||||
} else {
|
||||
let value = (variant_index.as_u32() as u128)
|
||||
.wrapping_sub(niche_variants.start().as_u32() as u128)
|
||||
.wrapping_add(niche_start);
|
||||
let value = tag.value.size(cx).truncate(value);
|
||||
// NOTE(eddyb) do *NOT* remove this assert, until
|
||||
// we pass the full 128-bit value to LLVM, otherwise
|
||||
// truncation will be silent and remain undetected.
|
||||
assert_eq!(value as u64 as u128, value);
|
||||
Some(value as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,441 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
common::CodegenCx,
|
||||
debuginfo::{
|
||||
metadata::{
|
||||
closure_saved_names_of_captured_variables,
|
||||
enums::tag_base_type,
|
||||
file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node,
|
||||
type_map::{self, Stub, StubInfo, UniqueTypeId},
|
||||
unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
},
|
||||
utils::{create_DIArray, get_namespace_for_item, DIB},
|
||||
},
|
||||
llvm::{
|
||||
self,
|
||||
debuginfo::{DIFile, DIFlags, DIType},
|
||||
},
|
||||
};
|
||||
use libc::c_uint;
|
||||
use rustc_codegen_ssa::{
|
||||
debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo},
|
||||
traits::ConstMethods,
|
||||
};
|
||||
use rustc_middle::{
|
||||
bug,
|
||||
ty::{
|
||||
self,
|
||||
layout::{LayoutOf, TyAndLayout},
|
||||
},
|
||||
};
|
||||
use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants};
|
||||
use smallvec::smallvec;
|
||||
|
||||
/// Build the debuginfo node for an enum type. The listing below shows how such a
|
||||
/// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type`
|
||||
/// with a single `DW_TAG_variant_part` that in turn contains a `DW_TAG_variant`
|
||||
/// for each variant of the enum. The variant-part also contains a single member
|
||||
/// describing the discriminant, and a nested struct type for each of the variants.
|
||||
///
|
||||
/// ```txt
|
||||
/// ---> DW_TAG_structure_type (top-level type for enum)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// DW_TAG_variant (variant 1)
|
||||
/// DW_TAG_variant (variant 2)
|
||||
/// DW_TAG_variant (variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
/// ```
|
||||
pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
let enum_type = unique_type_id.expect_ty();
|
||||
let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
|
||||
bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
|
||||
};
|
||||
|
||||
let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
|
||||
let enum_type_and_layout = cx.layout_of(enum_type);
|
||||
let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
|
||||
|
||||
debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
type_map::stub(
|
||||
cx,
|
||||
Stub::Struct,
|
||||
unique_type_id,
|
||||
&enum_type_name,
|
||||
size_and_align_of(enum_type_and_layout),
|
||||
Some(containing_scope),
|
||||
DIFlags::FlagZero,
|
||||
),
|
||||
|cx, enum_type_di_node| {
|
||||
// Build the struct type for each variant. These will be referenced by the
|
||||
// DW_TAG_variant DIEs inside of the DW_TAG_variant_part DIE.
|
||||
// We also called the names for the corresponding DW_TAG_variant DIEs here.
|
||||
let variant_member_infos: SmallVec<_> = enum_adt_def
|
||||
.variant_range()
|
||||
.map(|variant_index| VariantMemberInfo {
|
||||
variant_index,
|
||||
variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()),
|
||||
variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
|
||||
cx,
|
||||
enum_type,
|
||||
enum_type_di_node,
|
||||
variant_index,
|
||||
enum_adt_def.variant(variant_index),
|
||||
enum_type_and_layout.for_variant(cx, variant_index),
|
||||
),
|
||||
source_info: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
smallvec![build_enum_variant_part_di_node(
|
||||
cx,
|
||||
enum_type_and_layout,
|
||||
enum_type_di_node,
|
||||
&variant_member_infos[..],
|
||||
)]
|
||||
},
|
||||
// We don't seem to be emitting generic args on the enum type, it seems. Rather
|
||||
// they get attached to the struct type of each variant.
|
||||
NO_GENERICS,
|
||||
)
|
||||
}
|
||||
|
||||
/// Build the debuginfo node for a generator environment. It looks the same as the debuginfo for
|
||||
/// an enum. See [build_enum_type_di_node] for more information.
|
||||
///
|
||||
/// ```txt
|
||||
///
|
||||
/// ---> DW_TAG_structure_type (top-level type for the generator)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// DW_TAG_variant (variant 1)
|
||||
/// DW_TAG_variant (variant 2)
|
||||
/// DW_TAG_variant (variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
///
|
||||
/// ```
|
||||
pub(super) fn build_generator_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
let generator_type = unique_type_id.expect_ty();
|
||||
let &ty::Generator(generator_def_id, _, _ ) = generator_type.kind() else {
|
||||
bug!("build_generator_di_node() called with non-generator type: `{:?}`", generator_type)
|
||||
};
|
||||
|
||||
let containing_scope = get_namespace_for_item(cx, generator_def_id);
|
||||
let generator_type_and_layout = cx.layout_of(generator_type);
|
||||
|
||||
debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout));
|
||||
|
||||
let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false);
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
type_map::stub(
|
||||
cx,
|
||||
Stub::Struct,
|
||||
unique_type_id,
|
||||
&generator_type_name,
|
||||
size_and_align_of(generator_type_and_layout),
|
||||
Some(containing_scope),
|
||||
DIFlags::FlagZero,
|
||||
),
|
||||
|cx, generator_type_di_node| {
|
||||
let (generator_layout, state_specific_upvar_names) =
|
||||
generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
|
||||
|
||||
let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else {
|
||||
bug!(
|
||||
"Encountered generator with non-direct-tag layout: {:?}",
|
||||
generator_type_and_layout
|
||||
)
|
||||
};
|
||||
|
||||
let common_upvar_names =
|
||||
closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
|
||||
|
||||
// Build variant struct types
|
||||
let variant_struct_type_di_nodes: SmallVec<_> = variants
|
||||
.indices()
|
||||
.map(|variant_index| {
|
||||
// FIXME: This is problematic because just a number is not a valid identifier.
|
||||
// GeneratorSubsts::variant_name(variant_index), would be consistent
|
||||
// with enums?
|
||||
let variant_name = format!("{}", variant_index.as_usize()).into();
|
||||
|
||||
let span = generator_layout.variant_source_info[variant_index].span;
|
||||
let source_info = if !span.is_dummy() {
|
||||
let loc = cx.lookup_debug_loc(span.lo());
|
||||
Some((file_metadata(cx, &loc.file), loc.line))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
VariantMemberInfo {
|
||||
variant_index,
|
||||
variant_name,
|
||||
variant_struct_type_di_node:
|
||||
super::build_generator_variant_struct_type_di_node(
|
||||
cx,
|
||||
variant_index,
|
||||
generator_type_and_layout,
|
||||
generator_type_di_node,
|
||||
generator_layout,
|
||||
&state_specific_upvar_names,
|
||||
&common_upvar_names,
|
||||
),
|
||||
source_info,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
smallvec![build_enum_variant_part_di_node(
|
||||
cx,
|
||||
generator_type_and_layout,
|
||||
generator_type_di_node,
|
||||
&variant_struct_type_di_nodes[..],
|
||||
)]
|
||||
},
|
||||
// We don't seem to be emitting generic args on the generator type, it seems. Rather
|
||||
// they get attached to the struct type of each variant.
|
||||
NO_GENERICS,
|
||||
)
|
||||
}
|
||||
|
||||
/// Builds the DW_TAG_variant_part of an enum or generator debuginfo node:
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_structure_type (top-level type for enum)
|
||||
/// ---> DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// DW_TAG_variant (variant 1)
|
||||
/// DW_TAG_variant (variant 2)
|
||||
/// DW_TAG_variant (variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
/// ```
|
||||
fn build_enum_variant_part_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
variant_member_infos: &[VariantMemberInfo<'_, 'll>],
|
||||
) -> &'ll DIType {
|
||||
let tag_member_di_node =
|
||||
build_discr_member_di_node(cx, enum_type_and_layout, enum_type_di_node);
|
||||
|
||||
let variant_part_unique_type_id =
|
||||
UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty);
|
||||
|
||||
let stub = StubInfo::new(
|
||||
cx,
|
||||
variant_part_unique_type_id,
|
||||
|cx, variant_part_unique_type_id_str| unsafe {
|
||||
let variant_part_name = "";
|
||||
llvm::LLVMRustDIBuilderCreateVariantPart(
|
||||
DIB(cx),
|
||||
enum_type_di_node,
|
||||
variant_part_name.as_ptr().cast(),
|
||||
variant_part_name.len(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
enum_type_and_layout.size.bits(),
|
||||
enum_type_and_layout.align.abi.bits() as u32,
|
||||
DIFlags::FlagZero,
|
||||
tag_member_di_node,
|
||||
create_DIArray(DIB(cx), &[]),
|
||||
variant_part_unique_type_id_str.as_ptr().cast(),
|
||||
variant_part_unique_type_id_str.len(),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
type_map::build_type_with_children(
|
||||
cx,
|
||||
stub,
|
||||
|cx, variant_part_di_node| {
|
||||
variant_member_infos
|
||||
.iter()
|
||||
.map(|variant_member_info| {
|
||||
build_enum_variant_member_di_node(
|
||||
cx,
|
||||
enum_type_and_layout,
|
||||
variant_part_di_node,
|
||||
variant_member_info,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
NO_GENERICS,
|
||||
)
|
||||
.di_node
|
||||
}
|
||||
|
||||
/// Builds the DW_TAG_member describing where we can find the tag of an enum.
|
||||
/// Returns `None` if the enum does not have a tag.
|
||||
///
|
||||
/// ```txt
|
||||
///
|
||||
/// DW_TAG_structure_type (top-level type for enum)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// ---> DW_TAG_member (discriminant member)
|
||||
/// DW_TAG_variant (variant 1)
|
||||
/// DW_TAG_variant (variant 2)
|
||||
/// DW_TAG_variant (variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
///
|
||||
/// ```
|
||||
fn build_discr_member_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_or_generator_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_or_generator_type_di_node: &'ll DIType,
|
||||
) -> Option<&'ll DIType> {
|
||||
let tag_name = match enum_or_generator_type_and_layout.ty.kind() {
|
||||
ty::Generator(..) => "__state",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
// NOTE: This is actually wrong. This will become a member of
|
||||
// of the DW_TAG_variant_part. But, due to LLVM's API, that
|
||||
// can only be constructed with this DW_TAG_member already in created.
|
||||
// In LLVM IR the wrong scope will be listed but when DWARF is
|
||||
// generated from it, the DW_TAG_member will be a child the
|
||||
// DW_TAG_variant_part.
|
||||
let containing_scope = enum_or_generator_type_di_node;
|
||||
|
||||
match enum_or_generator_type_and_layout.layout.variants() {
|
||||
// A single-variant enum has no discriminant.
|
||||
&Variants::Single { .. } => None,
|
||||
|
||||
&Variants::Multiple { tag_field, .. } => {
|
||||
let tag_base_type = tag_base_type(cx, enum_or_generator_type_and_layout);
|
||||
let (size, align) = cx.size_and_align_of(tag_base_type);
|
||||
|
||||
unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateMemberType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
tag_name.as_ptr().cast(),
|
||||
tag_name.len(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
size.bits(),
|
||||
align.bits() as u32,
|
||||
enum_or_generator_type_and_layout.fields.offset(tag_field).bits(),
|
||||
DIFlags::FlagArtificial,
|
||||
type_di_node(cx, tag_base_type),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the debuginfo node for `DW_TAG_variant`:
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_structure_type (top-level type for enum)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// ---> DW_TAG_variant (variant 1)
|
||||
/// ---> DW_TAG_variant (variant 2)
|
||||
/// ---> DW_TAG_variant (variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
/// ```
|
||||
///
|
||||
/// This node looks like:
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_variant
|
||||
/// DW_AT_discr_value 0
|
||||
/// DW_TAG_member
|
||||
/// DW_AT_name None
|
||||
/// DW_AT_type <0x000002a1>
|
||||
/// DW_AT_alignment 0x00000002
|
||||
/// DW_AT_data_member_location 0
|
||||
/// ```
|
||||
///
|
||||
/// The DW_AT_discr_value is optional, and is omitted if
|
||||
/// - This is the only variant of a univariant enum (i.e. their is no discriminant)
|
||||
/// - This is the "dataful" variant of a niche-layout enum
|
||||
/// (where only the other variants are identified by a single value)
|
||||
///
|
||||
/// There is only ever a single member, the type of which is a struct that describes the
|
||||
/// fields of the variant (excluding the discriminant). The name of the member is the name
|
||||
/// of the variant as given in the source code. The DW_AT_data_member_location is always
|
||||
/// zero.
|
||||
///
|
||||
/// Note that the LLVM DIBuilder API is a bit unintuitive here. The DW_TAG_variant subtree
|
||||
/// (including the DW_TAG_member) is built by a single call to
|
||||
/// `LLVMRustDIBuilderCreateVariantMemberType()`.
|
||||
fn build_enum_variant_member_di_node<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
variant_part_di_node: &'ll DIType,
|
||||
variant_member_info: &VariantMemberInfo<'_, 'll>,
|
||||
) -> &'ll DIType {
|
||||
let variant_index = variant_member_info.variant_index;
|
||||
let discr_value = super::compute_discriminant_value(cx, enum_type_and_layout, variant_index);
|
||||
|
||||
let (file_di_node, line_number) = variant_member_info
|
||||
.source_info
|
||||
.unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER));
|
||||
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateVariantMemberType(
|
||||
DIB(cx),
|
||||
variant_part_di_node,
|
||||
variant_member_info.variant_name.as_ptr().cast(),
|
||||
variant_member_info.variant_name.len(),
|
||||
file_di_node,
|
||||
line_number,
|
||||
enum_type_and_layout.size.bits(),
|
||||
enum_type_and_layout.align.abi.bits() as u32,
|
||||
Size::ZERO.bits(),
|
||||
discr_value.map(|v| cx.const_u64(v)),
|
||||
DIFlags::FlagZero,
|
||||
variant_member_info.variant_struct_type_di_node,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Information needed for building a `DW_TAG_variant`:
|
||||
///
|
||||
/// ```txt
|
||||
/// DW_TAG_structure_type (top-level type for enum)
|
||||
/// DW_TAG_variant_part (variant part)
|
||||
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
||||
/// DW_TAG_member (discriminant member)
|
||||
/// ---> DW_TAG_variant (variant 1)
|
||||
/// ---> DW_TAG_variant (variant 2)
|
||||
/// ---> DW_TAG_variant (variant 3)
|
||||
/// DW_TAG_structure_type (type of variant 1)
|
||||
/// DW_TAG_structure_type (type of variant 2)
|
||||
/// DW_TAG_structure_type (type of variant 3)
|
||||
struct VariantMemberInfo<'a, 'll> {
|
||||
variant_index: VariantIdx,
|
||||
variant_name: Cow<'a, str>,
|
||||
variant_struct_type_di_node: &'ll DIType,
|
||||
source_info: Option<(&'ll DIFile, c_uint)>,
|
||||
}
|
270
compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
Normal file
270
compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
Normal file
@ -0,0 +1,270 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use rustc_data_structures::{
|
||||
fingerprint::Fingerprint,
|
||||
fx::FxHashMap,
|
||||
stable_hasher::{HashStable, NodeIdHashingMode, StableHasher},
|
||||
};
|
||||
use rustc_middle::{
|
||||
bug,
|
||||
ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt},
|
||||
};
|
||||
use rustc_target::abi::{Align, Size, VariantIdx};
|
||||
|
||||
use crate::{
|
||||
common::CodegenCx,
|
||||
debuginfo::utils::{create_DIArray, debug_context, DIB},
|
||||
llvm::{
|
||||
self,
|
||||
debuginfo::{DIFlags, DIScope, DIType},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{unknown_file_metadata, SmallVec, UNKNOWN_LINE_NUMBER};
|
||||
|
||||
mod private {
|
||||
// This type cannot be constructed outside of this module because
|
||||
// it has a private field. We make use of this in order to prevent
|
||||
// `UniqueTypeId` from being constructed directly, without asserting
|
||||
// the preconditions.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)]
|
||||
pub struct HiddenZst;
|
||||
}
|
||||
|
||||
/// A unique identifier for anything that we create a debuginfo node for.
|
||||
/// The types it contains are expected to already be normalized (which
|
||||
/// is debug_asserted in the constructors).
|
||||
///
|
||||
/// Note that there are some things that only show up in debuginfo, like
|
||||
/// the separate type descriptions for each enum variant. These get an ID
|
||||
/// too because they have their own debuginfo node in LLVM IR.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)]
|
||||
pub(super) enum UniqueTypeId<'tcx> {
|
||||
/// The ID of a regular type as it shows up at the language level.
|
||||
Ty(Ty<'tcx>, private::HiddenZst),
|
||||
/// The ID for the single DW_TAG_variant_part nested inside the top-level
|
||||
/// DW_TAG_structure_type that describes enums and generators.
|
||||
VariantPart(Ty<'tcx>, private::HiddenZst),
|
||||
/// The ID for the artificial struct type describing a single enum variant.
|
||||
VariantStructType(Ty<'tcx>, VariantIdx, private::HiddenZst),
|
||||
/// The ID of the artificial type we create for VTables.
|
||||
VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, private::HiddenZst),
|
||||
}
|
||||
|
||||
impl<'tcx> UniqueTypeId<'tcx> {
|
||||
pub fn for_ty(tcx: TyCtxt<'tcx>, t: Ty<'tcx>) -> Self {
|
||||
debug_assert_eq!(t, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t));
|
||||
UniqueTypeId::Ty(t, private::HiddenZst)
|
||||
}
|
||||
|
||||
pub fn for_enum_variant_part(tcx: TyCtxt<'tcx>, enum_ty: Ty<'tcx>) -> Self {
|
||||
debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty));
|
||||
UniqueTypeId::VariantPart(enum_ty, private::HiddenZst)
|
||||
}
|
||||
|
||||
pub fn for_enum_variant_struct_type(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
enum_ty: Ty<'tcx>,
|
||||
variant_idx: VariantIdx,
|
||||
) -> Self {
|
||||
debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty));
|
||||
UniqueTypeId::VariantStructType(enum_ty, variant_idx, private::HiddenZst)
|
||||
}
|
||||
|
||||
pub fn for_vtable_ty(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
self_type: Ty<'tcx>,
|
||||
implemented_trait: Option<PolyExistentialTraitRef<'tcx>>,
|
||||
) -> Self {
|
||||
debug_assert_eq!(
|
||||
self_type,
|
||||
tcx.normalize_erasing_regions(ParamEnv::reveal_all(), self_type)
|
||||
);
|
||||
debug_assert_eq!(
|
||||
implemented_trait,
|
||||
tcx.normalize_erasing_regions(ParamEnv::reveal_all(), implemented_trait)
|
||||
);
|
||||
UniqueTypeId::VTableTy(self_type, implemented_trait, private::HiddenZst)
|
||||
}
|
||||
|
||||
/// Generates a string version of this [UniqueTypeId], which can be used as the `UniqueId`
|
||||
/// argument of the various `LLVMRustDIBuilderCreate*Type()` methods.
|
||||
///
|
||||
/// Right now this takes the form of a hex-encoded opaque hash value.
|
||||
pub fn generate_unique_id_string(self, tcx: TyCtxt<'tcx>) -> String {
|
||||
let mut hasher = StableHasher::new();
|
||||
let mut hcx = tcx.create_stable_hashing_context();
|
||||
hcx.while_hashing_spans(false, |hcx| {
|
||||
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
|
||||
self.hash_stable(hcx, &mut hasher);
|
||||
});
|
||||
});
|
||||
hasher.finish::<Fingerprint>().to_hex()
|
||||
}
|
||||
|
||||
pub fn expect_ty(self) -> Ty<'tcx> {
|
||||
match self {
|
||||
UniqueTypeId::Ty(ty, _) => ty,
|
||||
_ => bug!("Expected `UniqueTypeId::Ty` but found `{:?}`", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `TypeMap` is where the debug context holds the type metadata nodes
|
||||
/// created so far. The debuginfo nodes are identified by `UniqueTypeId`.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TypeMap<'ll, 'tcx> {
|
||||
pub(super) unique_id_to_di_node: RefCell<FxHashMap<UniqueTypeId<'tcx>, &'ll DIType>>,
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> TypeMap<'ll, 'tcx> {
|
||||
/// Adds a `UniqueTypeId` to metadata mapping to the `TypeMap`. The method will
|
||||
/// fail if the mapping already exists.
|
||||
pub(super) fn insert(&self, unique_type_id: UniqueTypeId<'tcx>, metadata: &'ll DIType) {
|
||||
if self.unique_id_to_di_node.borrow_mut().insert(unique_type_id, metadata).is_some() {
|
||||
bug!("type metadata for unique ID '{:?}' is already in the `TypeMap`!", unique_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn di_node_for_unique_id(
|
||||
&self,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
) -> Option<&'ll DIType> {
|
||||
self.unique_id_to_di_node.borrow().get(&unique_type_id).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DINodeCreationResult<'ll> {
|
||||
pub di_node: &'ll DIType,
|
||||
pub already_stored_in_typemap: bool,
|
||||
}
|
||||
|
||||
impl<'ll> DINodeCreationResult<'ll> {
|
||||
pub fn new(di_node: &'ll DIType, already_stored_in_typemap: bool) -> Self {
|
||||
DINodeCreationResult { di_node, already_stored_in_typemap }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Stub<'ll> {
|
||||
Struct,
|
||||
Union,
|
||||
VtableTy { vtable_holder: &'ll DIType },
|
||||
}
|
||||
|
||||
pub struct StubInfo<'ll, 'tcx> {
|
||||
metadata: &'ll DIType,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> StubInfo<'ll, 'tcx> {
|
||||
pub(super) fn new(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
build: impl FnOnce(&CodegenCx<'ll, 'tcx>, /* unique_type_id_str: */ &str) -> &'ll DIType,
|
||||
) -> StubInfo<'ll, 'tcx> {
|
||||
let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
|
||||
let di_node = build(cx, &unique_type_id_str);
|
||||
StubInfo { metadata: di_node, unique_type_id }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a stub debuginfo node onto which fields and nested types can be attached.
|
||||
pub(super) fn stub<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
kind: Stub<'ll>,
|
||||
unique_type_id: UniqueTypeId<'tcx>,
|
||||
name: &str,
|
||||
(size, align): (Size, Align),
|
||||
containing_scope: Option<&'ll DIScope>,
|
||||
flags: DIFlags,
|
||||
) -> StubInfo<'ll, 'tcx> {
|
||||
let empty_array = create_DIArray(DIB(cx), &[]);
|
||||
let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
|
||||
|
||||
let metadata = match kind {
|
||||
Stub::Struct | Stub::VtableTy { .. } => {
|
||||
let vtable_holder = match kind {
|
||||
Stub::VtableTy { vtable_holder } => Some(vtable_holder),
|
||||
_ => None,
|
||||
};
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateStructType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
name.as_ptr().cast(),
|
||||
name.len(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
size.bits(),
|
||||
align.bits() as u32,
|
||||
flags,
|
||||
None,
|
||||
empty_array,
|
||||
0,
|
||||
vtable_holder,
|
||||
unique_type_id_str.as_ptr().cast(),
|
||||
unique_type_id_str.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Stub::Union => unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateUnionType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
name.as_ptr().cast(),
|
||||
name.len(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
size.bits(),
|
||||
align.bits() as u32,
|
||||
flags,
|
||||
Some(empty_array),
|
||||
0,
|
||||
unique_type_id_str.as_ptr().cast(),
|
||||
unique_type_id_str.len(),
|
||||
)
|
||||
},
|
||||
};
|
||||
StubInfo { metadata, unique_type_id }
|
||||
}
|
||||
|
||||
/// This function enables creating debuginfo nodes that can recursively refer to themselves.
|
||||
/// It will first insert the given stub into the type map and only then execute the `members`
|
||||
/// and `generics` closures passed in. These closures have access to the stub so they can
|
||||
/// directly attach fields to them. If the type of a field transitively refers back
|
||||
/// to the type currently being built, the stub will already be found in the type map,
|
||||
/// which effectively breaks the recursion cycle.
|
||||
pub(super) fn build_type_with_children<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
stub_info: StubInfo<'ll, 'tcx>,
|
||||
members: impl FnOnce(&CodegenCx<'ll, 'tcx>, &'ll DIType) -> SmallVec<&'ll DIType>,
|
||||
generics: impl FnOnce(&CodegenCx<'ll, 'tcx>) -> SmallVec<&'ll DIType>,
|
||||
) -> DINodeCreationResult<'ll> {
|
||||
debug_assert_eq!(
|
||||
debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id),
|
||||
None
|
||||
);
|
||||
|
||||
debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata);
|
||||
|
||||
let members: SmallVec<_> =
|
||||
members(cx, stub_info.metadata).into_iter().map(|node| Some(node)).collect();
|
||||
let generics: SmallVec<Option<&'ll DIType>> =
|
||||
generics(cx).into_iter().map(|node| Some(node)).collect();
|
||||
|
||||
if !(members.is_empty() && generics.is_empty()) {
|
||||
unsafe {
|
||||
let members_array = create_DIArray(DIB(cx), &members[..]);
|
||||
let generics_array = create_DIArray(DIB(cx), &generics[..]);
|
||||
llvm::LLVMRustDICompositeTypeReplaceArrays(
|
||||
DIB(cx),
|
||||
stub_info.metadata,
|
||||
Some(members_array),
|
||||
Some(generics_array),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DINodeCreationResult { di_node: stub_info.metadata, already_stored_in_typemap: true }
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
use rustc_codegen_ssa::mir::debuginfo::VariableKind::*;
|
||||
|
||||
use self::metadata::{file_metadata, type_metadata, TypeMap};
|
||||
use self::metadata::{file_metadata, type_di_node};
|
||||
use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
|
||||
use self::namespace::mangled_name_of_instance;
|
||||
use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
|
||||
@ -20,7 +20,7 @@ use crate::value::Value;
|
||||
use rustc_codegen_ssa::debuginfo::type_names;
|
||||
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||
use rustc_index::vec::IndexVec;
|
||||
@ -32,7 +32,7 @@ use rustc_session::config::{self, DebugInfo};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span};
|
||||
use rustc_target::abi::{Primitive, Size};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
use libc::c_uint;
|
||||
use smallvec::SmallVec;
|
||||
@ -48,7 +48,7 @@ mod namespace;
|
||||
mod utils;
|
||||
|
||||
pub use self::create_scope_map::compute_mir_scopes;
|
||||
pub use self::metadata::create_global_var_metadata;
|
||||
pub use self::metadata::build_global_var_di_node;
|
||||
pub use self::metadata::extend_scope_to_file;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
@ -57,24 +57,18 @@ const DW_TAG_auto_variable: c_uint = 0x100;
|
||||
const DW_TAG_arg_variable: c_uint = 0x101;
|
||||
|
||||
/// A context object for maintaining all state needed by the debuginfo module.
|
||||
pub struct CrateDebugContext<'a, 'tcx> {
|
||||
llcontext: &'a llvm::Context,
|
||||
llmod: &'a llvm::Module,
|
||||
builder: &'a mut DIBuilder<'a>,
|
||||
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'a DIFile>>,
|
||||
created_enum_disr_types: RefCell<FxHashMap<(DefId, Primitive), &'a DIType>>,
|
||||
pub struct CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
llcontext: &'ll llvm::Context,
|
||||
llmod: &'ll llvm::Module,
|
||||
builder: &'ll mut DIBuilder<'ll>,
|
||||
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>,
|
||||
|
||||
type_map: TypeMap<'a, 'tcx>,
|
||||
namespace_map: RefCell<DefIdMap<&'a DIScope>>,
|
||||
|
||||
recursion_marker_type: OnceCell<&'a DIType>,
|
||||
|
||||
// This collection is used to assert that composite types (structs, enums,
|
||||
// ...) have their members only set once:
|
||||
composite_types_completed: RefCell<FxHashSet<&'a DIType>>,
|
||||
type_map: metadata::TypeMap<'ll, 'tcx>,
|
||||
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
|
||||
recursion_marker_type: OnceCell<&'ll DIType>,
|
||||
}
|
||||
|
||||
impl Drop for CrateDebugContext<'_, '_> {
|
||||
impl Drop for CodegenUnitDebugContext<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _));
|
||||
@ -82,22 +76,20 @@ impl Drop for CrateDebugContext<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> {
|
||||
pub fn new(llmod: &'a llvm::Module) -> Self {
|
||||
debug!("CrateDebugContext::new");
|
||||
impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
pub fn new(llmod: &'ll llvm::Module) -> Self {
|
||||
debug!("CodegenUnitDebugContext::new");
|
||||
let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
|
||||
// DIBuilder inherits context from the module, so we'd better use the same one
|
||||
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
|
||||
CrateDebugContext {
|
||||
CodegenUnitDebugContext {
|
||||
llcontext,
|
||||
llmod,
|
||||
builder,
|
||||
created_files: Default::default(),
|
||||
created_enum_disr_types: Default::default(),
|
||||
type_map: Default::default(),
|
||||
namespace_map: RefCell::new(Default::default()),
|
||||
recursion_marker_type: OnceCell::new(),
|
||||
composite_types_completed: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,7 +407,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
signature.push(if fn_abi.ret.is_ignore() {
|
||||
None
|
||||
} else {
|
||||
Some(type_metadata(cx, fn_abi.ret.layout.ty))
|
||||
Some(type_di_node(cx, fn_abi.ret.layout.ty))
|
||||
});
|
||||
|
||||
// Arguments types
|
||||
@ -440,11 +432,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
}
|
||||
_ => t,
|
||||
};
|
||||
Some(type_metadata(cx, t))
|
||||
Some(type_di_node(cx, t))
|
||||
}));
|
||||
} else {
|
||||
signature
|
||||
.extend(fn_abi.args.iter().map(|arg| Some(type_metadata(cx, arg.layout.ty))));
|
||||
.extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty))));
|
||||
}
|
||||
|
||||
create_DIArray(DIB(cx), &signature[..])
|
||||
@ -467,7 +459,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
if let GenericArgKind::Type(ty) = kind.unpack() {
|
||||
let actual_type =
|
||||
cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
|
||||
let actual_type_metadata = type_metadata(cx, actual_type);
|
||||
let actual_type_metadata = type_di_node(cx, actual_type);
|
||||
let name = name.as_str();
|
||||
Some(unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
|
||||
@ -522,7 +514,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
if cx.sess().opts.debuginfo == DebugInfo::Full
|
||||
&& !impl_self_ty.needs_subst()
|
||||
{
|
||||
Some(type_metadata(cx, impl_self_ty))
|
||||
Some(type_di_node(cx, impl_self_ty))
|
||||
} else {
|
||||
Some(namespace::item_namespace(cx, def.did()))
|
||||
}
|
||||
@ -563,13 +555,13 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) }
|
||||
}
|
||||
|
||||
fn create_vtable_metadata(
|
||||
fn create_vtable_debuginfo(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
vtable: Self::Value,
|
||||
) {
|
||||
metadata::create_vtable_metadata(self, ty, trait_ref, vtable)
|
||||
metadata::create_vtable_di_node(self, ty, trait_ref, vtable)
|
||||
}
|
||||
|
||||
fn extend_scope_to_file(
|
||||
@ -597,7 +589,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
let loc = self.lookup_debug_loc(span.lo());
|
||||
let file_metadata = file_metadata(self, &loc.file);
|
||||
|
||||
let type_metadata = type_metadata(self, variable_type);
|
||||
let type_metadata = type_di_node(self, variable_type);
|
||||
|
||||
let (argument_index, dwarf_tag) = match variable_kind {
|
||||
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Utility Functions.
|
||||
|
||||
use super::namespace::item_namespace;
|
||||
use super::CrateDebugContext;
|
||||
use super::CodegenUnitDebugContext;
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
||||
@ -35,7 +35,7 @@ pub fn create_DIArray<'ll>(
|
||||
#[inline]
|
||||
pub fn debug_context<'a, 'll, 'tcx>(
|
||||
cx: &'a CodegenCx<'ll, 'tcx>,
|
||||
) -> &'a CrateDebugContext<'ll, 'tcx> {
|
||||
) -> &'a CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
cx.dbg_cx.as_ref().unwrap()
|
||||
}
|
||||
|
||||
|
@ -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>>,
|
||||
|
@ -16,8 +16,7 @@ async fn async_fn_test() {
|
||||
|
||||
// FIXME: No way to reliably check the filename.
|
||||
|
||||
// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$<async_fn_debug_msvc::async_fn_test::async_fn_env$0>",
|
||||
// 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,
|
||||
@ -40,10 +39,10 @@ async fn async_fn_test() {
|
||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK: [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
|
||||
|
@ -18,7 +18,7 @@ async fn async_fn_test() {
|
||||
|
||||
// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[ASYNC_FN]]
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[ASYNC_FN]],
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
|
||||
@ -50,7 +50,7 @@ async fn async_fn_test() {
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[ASYNC_FN]],
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]],
|
||||
// CHECK-SAME: flags: DIFlagArtificial
|
||||
|
||||
fn main() {
|
||||
|
@ -20,8 +20,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
|
||||
// FIXME: No way to reliably check the filename.
|
||||
|
||||
// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$<generator_debug_msvc::generator_test::generator_env$0>"
|
||||
// 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,
|
||||
@ -44,10 +43,10 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK: [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
|
||||
|
@ -22,7 +22,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
|
||||
// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{generator_env#0}", scope: [[GEN_FN]]
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN_FN]],
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
|
||||
@ -54,7 +54,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN_FN]],
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]],
|
||||
// CHECK-SAME: flags: DIFlagArtificial
|
||||
|
||||
fn main() {
|
||||
|
@ -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$<generator_objects::main::generator_env$0>]
|
||||
// cdb-check: [variant] : Unresumed
|
||||
// cdb-check: [+0x[...]] _ref__a : 0x[...] : 5 [Type: int *]
|
||||
|
||||
// cdb-command: g
|
||||
// cdb-command: dx b
|
||||
// cdb-check: b : Suspend0 [Type: enum$<generator_objects::main::generator_env$0>]
|
||||
// cdb-check: [variant] : Suspend0
|
||||
// cdb-check: [+0x[...]] c : 6 [Type: int]
|
||||
// cdb-check: [+0x[...]] d : 7 [Type: int]
|
||||
// cdb-check: [+0x[...]] _ref__a : 0x[...] : 5 [Type: int *]
|
||||
|
||||
// cdb-command: g
|
||||
// cdb-command: dx b
|
||||
// cdb-check: b : Suspend1 [Type: enum$<generator_objects::main::generator_env$0>]
|
||||
// cdb-check: [variant] : Suspend1
|
||||
// cdb-check: [+0x[...]] c : 7 [Type: int]
|
||||
// cdb-check: [+0x[...]] d : 8 [Type: int]
|
||||
// cdb-check: [+0x[...]] _ref__a : 0x[...] : 6 [Type: int *]
|
||||
|
||||
// cdb-command: g
|
||||
// cdb-command: dx b
|
||||
// cdb-check: b : Returned [Type: enum$<generator_objects::main::generator_env$0>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<generator_objects::main::generator_env$0>]
|
||||
// cdb-check: [variant] : Returned
|
||||
// cdb-check: [+0x[...]] _ref__a : 0x[...] : 6 [Type: int *]
|
||||
|
||||
#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
|
||||
#![omit_gdb_pretty_printer_section]
|
||||
|
||||
@ -66,6 +97,7 @@ fn main() {
|
||||
_zzz(); // #break
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn _zzz() {
|
||||
()
|
||||
}
|
||||
|
@ -4,14 +4,14 @@
|
||||
// cdb-command: g
|
||||
|
||||
// cdb-command: dx a
|
||||
// cdb-check:a : Some({...}) [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check:a : Some({...}) [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [variant] : Some
|
||||
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
|
||||
|
||||
// cdb-command: dx b
|
||||
// cdb-check:b : None [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check:b : None [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [variant] : None
|
||||
|
||||
// cdb-command: dx c
|
||||
@ -78,7 +78,7 @@ pub enum NicheLayoutEnum {
|
||||
Tag2,
|
||||
}
|
||||
|
||||
pub enum Empty { }
|
||||
pub enum Empty {}
|
||||
|
||||
fn main() {
|
||||
let a = Some(CStyleEnum::Low);
|
||||
@ -97,4 +97,6 @@ fn main() {
|
||||
zzz(); // #break
|
||||
}
|
||||
|
||||
fn zzz() { () }
|
||||
fn zzz() {
|
||||
()
|
||||
}
|
||||
|
@ -33,10 +33,10 @@
|
||||
// gdb-check:type = type_names::mod1::Enum2
|
||||
|
||||
// gdb-command:whatis generic_enum_1
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3<type_names::mod1::Struct2>
|
||||
|
||||
// gdb-command:whatis generic_enum_2
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3<type_names::Struct1>
|
||||
|
||||
// TUPLES
|
||||
// gdb-command:whatis tuple1
|
||||
@ -159,10 +159,10 @@
|
||||
|
||||
// FOREIGN TYPES
|
||||
// gdb-command:whatis foreign1
|
||||
// gdb-check:type = *mut ForeignType1
|
||||
// gdb-check:type = *mut type_names::{extern#0}::ForeignType1
|
||||
|
||||
// gdb-command:whatis foreign2
|
||||
// gdb-check:type = *mut ForeignType2
|
||||
// gdb-check:type = *mut type_names::mod1::{extern#0}::ForeignType2
|
||||
|
||||
// === CDB TESTS ==================================================================================
|
||||
|
||||
@ -178,9 +178,9 @@
|
||||
// cdb-command:dv /t *_enum_*
|
||||
// cdb-check:union enum$<type_names::Enum1> simple_enum_1 = [...]
|
||||
// cdb-check:union enum$<type_names::Enum1> simple_enum_2 = [...]
|
||||
// cdb-check:type_names::mod1::Enum2 simple_enum_3 = [...]
|
||||
// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_1 = [...]
|
||||
// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_2 = [...]
|
||||
// cdb-check:union enum$<type_names::mod1::Enum2> simple_enum_3 = [...]
|
||||
// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::mod1::Struct2> > generic_enum_1 = [...]
|
||||
// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::Struct1> > generic_enum_2 = [...]
|
||||
|
||||
// TUPLES
|
||||
// cdb-command:dv /t tuple*
|
||||
@ -258,8 +258,8 @@
|
||||
|
||||
// FOREIGN TYPES
|
||||
// cdb-command:dv /t foreign*
|
||||
// cdb-check:struct ForeignType2 * foreign2 = [...]
|
||||
// cdb-check:struct ForeignType1 * foreign1 = [...]
|
||||
// cdb-check:struct type_names::mod1::extern$0::ForeignType2 * foreign2 = [...]
|
||||
// cdb-check:struct type_names::extern$0::ForeignType1 * foreign1 = [...]
|
||||
|
||||
#![allow(unused_variables)]
|
||||
#![feature(omit_gdb_pretty_printer_section)]
|
||||
@ -283,7 +283,6 @@ extern "C" {
|
||||
}
|
||||
|
||||
mod mod1 {
|
||||
pub use self::Enum2::{Variant1, Variant2};
|
||||
pub struct Struct2;
|
||||
|
||||
pub enum Enum2 {
|
||||
@ -367,14 +366,14 @@ fn main() {
|
||||
// Enums
|
||||
let simple_enum_1 = Variant1;
|
||||
let simple_enum_2 = Variant2(0);
|
||||
let simple_enum_3 = mod1::Variant2(Struct1);
|
||||
let simple_enum_3 = mod1::Enum2::Variant2(Struct1);
|
||||
|
||||
let generic_enum_1: mod1::mod2::Enum3<mod1::Struct2> = mod1::mod2::Variant1;
|
||||
let generic_enum_2 = mod1::mod2::Variant2(Struct1);
|
||||
|
||||
// Tuples
|
||||
let tuple1 = (8u32, Struct1, mod1::mod2::Variant2(mod1::Struct2));
|
||||
let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Variant1, 'x');
|
||||
let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Enum2::Variant1, 'x');
|
||||
|
||||
// Box
|
||||
let box1 = (Box::new(1f32), 0i32);
|
||||
@ -404,7 +403,7 @@ fn main() {
|
||||
|
||||
let vec1 = vec![0_usize, 2, 3];
|
||||
let slice1 = &*vec1;
|
||||
let vec2 = vec![mod1::Variant2(Struct1)];
|
||||
let vec2 = vec![mod1::Enum2::Variant2(Struct1)];
|
||||
let slice2 = &*vec2;
|
||||
|
||||
// Trait Objects
|
||||
|
@ -661,6 +661,21 @@ impl<'test> TestCx<'test> {
|
||||
}
|
||||
|
||||
fn run_debuginfo_cdb_test_no_opt(&self) {
|
||||
let exe_file = self.make_exe_name();
|
||||
|
||||
// Existing PDB files are update in-place. When changing the debuginfo
|
||||
// the compiler generates for something, this can lead to the situation
|
||||
// where both the old and the new version of the debuginfo for the same
|
||||
// type is present in the PDB, which is very confusing.
|
||||
// Therefore we delete any existing PDB file before compiling the test
|
||||
// case.
|
||||
// FIXME: If can reliably detect that MSVC's link.exe is used, then
|
||||
// passing `/INCREMENTAL:NO` might be a cleaner way to do this.
|
||||
let pdb_file = exe_file.with_extension(".pdb");
|
||||
if pdb_file.exists() {
|
||||
std::fs::remove_file(pdb_file).unwrap();
|
||||
}
|
||||
|
||||
// compile test file (it should have 'compile-flags:-g' in the header)
|
||||
let should_run = self.run_if_enabled();
|
||||
let compile_result = self.compile_test(should_run, EmitMetadata::No);
|
||||
@ -671,8 +686,6 @@ impl<'test> TestCx<'test> {
|
||||
return;
|
||||
}
|
||||
|
||||
let exe_file = self.make_exe_name();
|
||||
|
||||
let prefixes = {
|
||||
static PREFIXES: &[&str] = &["cdb", "cdbg"];
|
||||
// No "native rust support" variation for CDB yet.
|
||||
|
Loading…
Reference in New Issue
Block a user