debuginfo: Refactor debuginfo generation for types
This commit - 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) - moves TypeMap and functions that work directly work with it to a new type_map module - moves and reimplements 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 the position of discriminant debuginfo node so it is consistently nested inside the top-level union instead of, sometimes, next to it
This commit is contained in:
parent
0ac4658909
commit
07ebc13d87
18 changed files with 2307 additions and 1778 deletions
|
@ -140,8 +140,8 @@ pub(crate) unsafe fn codegen(
|
||||||
llvm::LLVMDisposeBuilder(llbuilder);
|
llvm::LLVMDisposeBuilder(llbuilder);
|
||||||
|
|
||||||
if tcx.sess.opts.debuginfo != DebugInfo::None {
|
if tcx.sess.opts.debuginfo != DebugInfo::None {
|
||||||
let dbg_cx = debuginfo::CrateDebugContext::new(llmod);
|
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
|
||||||
debuginfo::metadata::compile_unit_metadata(tcx, module_name, &dbg_cx);
|
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
|
||||||
dbg_cx.finalize(tcx.sess);
|
dbg_cx.finalize(tcx.sess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -428,7 +428,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
|
||||||
llvm::LLVMSetGlobalConstant(g, llvm::True);
|
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) {
|
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||||
llvm::set_thread_local_mode(g, self.tls_model);
|
llvm::set_thread_local_mode(g, self.tls_model);
|
||||||
|
|
|
@ -95,7 +95,7 @@ pub struct CodegenCx<'ll, 'tcx> {
|
||||||
pub isize_ty: &'ll Type,
|
pub isize_ty: &'ll Type,
|
||||||
|
|
||||||
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
|
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_personality: Cell<Option<&'ll Value>>,
|
||||||
eh_catch_typeinfo: 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 dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None {
|
||||||
let dctx = debuginfo::CrateDebugContext::new(llmod);
|
let dctx = debuginfo::CodegenUnitDebugContext::new(llmod);
|
||||||
debuginfo::metadata::compile_unit_metadata(tcx, codegen_unit.name().as_str(), &dctx);
|
debuginfo::metadata::build_compile_unit_di_node(
|
||||||
|
tcx,
|
||||||
|
codegen_unit.name().as_str(),
|
||||||
|
&dctx,
|
||||||
|
);
|
||||||
Some(dctx)
|
Some(dctx)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
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())
|
||||||
|
}
|
428
compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
Normal file
428
compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME(mw): Why are niche and regular tags treated differently? Because we want to preserve
|
||||||
|
// the sign?
|
||||||
|
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, .. } => {
|
||||||
|
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`.
|
||||||
|
cx.data_layout().ptr_sized_integer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_ty(cx.tcx, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
|
||||||
|
tag.value.to_ty(cx.tcx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a helper function. FIXME: elaborate docs.
|
||||||
|
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 enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants
|
||||||
|
.map(|(discr, variant_name)| {
|
||||||
|
let is_unsigned = match discr.ty.kind() {
|
||||||
|
ty::Int(_) => false,
|
||||||
|
ty::Uint(_) => true,
|
||||||
|
_ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
|
||||||
|
};
|
||||||
|
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(), // FIXME: Unused?
|
||||||
|
enum_type_and_layout.align.abi.bits() as u32, // FIXME: Unused?
|
||||||
|
Size::ZERO.bits(), // FIXME: Unused?
|
||||||
|
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)>,
|
||||||
|
}
|
271
compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
Normal file
271
compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[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 build 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 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::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
|
||||||
use self::namespace::mangled_name_of_instance;
|
use self::namespace::mangled_name_of_instance;
|
||||||
use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
|
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::debuginfo::type_names;
|
||||||
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
|
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
|
||||||
use rustc_codegen_ssa::traits::*;
|
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_data_structures::sync::Lrc;
|
||||||
use rustc_hir::def_id::{DefId, DefIdMap};
|
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||||
use rustc_index::vec::IndexVec;
|
use rustc_index::vec::IndexVec;
|
||||||
|
@ -32,7 +32,7 @@ use rustc_session::config::{self, DebugInfo};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span};
|
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 libc::c_uint;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -48,7 +48,7 @@ mod namespace;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use self::create_scope_map::compute_mir_scopes;
|
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;
|
pub use self::metadata::extend_scope_to_file;
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[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;
|
const DW_TAG_arg_variable: c_uint = 0x101;
|
||||||
|
|
||||||
/// A context object for maintaining all state needed by the debuginfo module.
|
/// A context object for maintaining all state needed by the debuginfo module.
|
||||||
pub struct CrateDebugContext<'a, 'tcx> {
|
pub struct CodegenUnitDebugContext<'ll, 'tcx> {
|
||||||
llcontext: &'a llvm::Context,
|
llcontext: &'ll llvm::Context,
|
||||||
llmod: &'a llvm::Module,
|
llmod: &'ll llvm::Module,
|
||||||
builder: &'a mut DIBuilder<'a>,
|
builder: &'ll mut DIBuilder<'ll>,
|
||||||
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'a DIFile>>,
|
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>,
|
||||||
created_enum_disr_types: RefCell<FxHashMap<(DefId, Primitive), &'a DIType>>,
|
|
||||||
|
|
||||||
type_map: TypeMap<'a, 'tcx>,
|
type_map: metadata::TypeMap<'ll, 'tcx>,
|
||||||
namespace_map: RefCell<DefIdMap<&'a DIScope>>,
|
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
|
||||||
|
recursion_marker_type: OnceCell<&'ll DIType>,
|
||||||
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>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for CrateDebugContext<'_, '_> {
|
impl Drop for CodegenUnitDebugContext<'_, '_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _));
|
llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _));
|
||||||
|
@ -82,22 +76,20 @@ impl Drop for CrateDebugContext<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> {
|
impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
||||||
pub fn new(llmod: &'a llvm::Module) -> Self {
|
pub fn new(llmod: &'ll llvm::Module) -> Self {
|
||||||
debug!("CrateDebugContext::new");
|
debug!("CodegenUnitDebugContext::new");
|
||||||
let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
|
let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
|
||||||
// DIBuilder inherits context from the module, so we'd better use the same one
|
// DIBuilder inherits context from the module, so we'd better use the same one
|
||||||
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
|
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
|
||||||
CrateDebugContext {
|
CodegenUnitDebugContext {
|
||||||
llcontext,
|
llcontext,
|
||||||
llmod,
|
llmod,
|
||||||
builder,
|
builder,
|
||||||
created_files: Default::default(),
|
created_files: Default::default(),
|
||||||
created_enum_disr_types: Default::default(),
|
|
||||||
type_map: Default::default(),
|
type_map: Default::default(),
|
||||||
namespace_map: RefCell::new(Default::default()),
|
namespace_map: RefCell::new(Default::default()),
|
||||||
recursion_marker_type: OnceCell::new(),
|
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() {
|
signature.push(if fn_abi.ret.is_ignore() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(type_metadata(cx, fn_abi.ret.layout.ty))
|
Some(type_di_node(cx, fn_abi.ret.layout.ty))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Arguments types
|
// Arguments types
|
||||||
|
@ -440,11 +432,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
}
|
}
|
||||||
_ => t,
|
_ => t,
|
||||||
};
|
};
|
||||||
Some(type_metadata(cx, t))
|
Some(type_di_node(cx, t))
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
signature
|
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[..])
|
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() {
|
if let GenericArgKind::Type(ty) = kind.unpack() {
|
||||||
let actual_type =
|
let actual_type =
|
||||||
cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
|
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();
|
let name = name.as_str();
|
||||||
Some(unsafe {
|
Some(unsafe {
|
||||||
Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
|
Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
|
||||||
|
@ -522,7 +514,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
if cx.sess().opts.debuginfo == DebugInfo::Full
|
if cx.sess().opts.debuginfo == DebugInfo::Full
|
||||||
&& !impl_self_ty.needs_subst()
|
&& !impl_self_ty.needs_subst()
|
||||||
{
|
{
|
||||||
Some(type_metadata(cx, impl_self_ty))
|
Some(type_di_node(cx, impl_self_ty))
|
||||||
} else {
|
} else {
|
||||||
Some(namespace::item_namespace(cx, def.did()))
|
Some(namespace::item_namespace(cx, def.did()))
|
||||||
}
|
}
|
||||||
|
@ -569,7 +561,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||||
vtable: Self::Value,
|
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(
|
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 loc = self.lookup_debug_loc(span.lo());
|
||||||
let file_metadata = file_metadata(self, &loc.file);
|
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 {
|
let (argument_index, dwarf_tag) = match variable_kind {
|
||||||
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
|
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Utility Functions.
|
// Utility Functions.
|
||||||
|
|
||||||
use super::namespace::item_namespace;
|
use super::namespace::item_namespace;
|
||||||
use super::CrateDebugContext;
|
use super::CodegenUnitDebugContext;
|
||||||
|
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
||||||
|
@ -35,7 +35,7 @@ pub fn create_DIArray<'ll>(
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn debug_context<'a, 'll, 'tcx>(
|
pub fn debug_context<'a, 'll, 'tcx>(
|
||||||
cx: &'a CodegenCx<'ll, 'tcx>,
|
cx: &'a CodegenCx<'ll, 'tcx>,
|
||||||
) -> &'a CrateDebugContext<'ll, 'tcx> {
|
) -> &'a CodegenUnitDebugContext<'ll, 'tcx> {
|
||||||
cx.dbg_cx.as_ref().unwrap()
|
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).
|
// FIXME(eddyb) find a place for this (or a way to replace it).
|
||||||
pub mod type_names;
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,13 +18,15 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
|
||||||
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
|
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
|
||||||
use rustc_middle::ty::layout::IntegerExt;
|
use rustc_middle::ty::layout::IntegerExt;
|
||||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||||
use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
|
use rustc_middle::ty::{self, AdtDef, ExistentialProjection, ParamEnv, Ty, TyCtxt};
|
||||||
use rustc_query_system::ich::NodeIdHashingMode;
|
use rustc_query_system::ich::NodeIdHashingMode;
|
||||||
use rustc_target::abi::{Integer, TagEncoding, Variants};
|
use rustc_target::abi::{Integer, TagEncoding, Variants};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use std::fmt::Write;
|
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
|
// 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
|
// 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
|
// the work twice. The `qualified` parameter only affects the first level of the
|
||||||
|
@ -71,7 +73,9 @@ fn push_debuginfo_type_name<'tcx>(
|
||||||
ty::Float(float_ty) => output.push_str(float_ty.name_str()),
|
ty::Float(float_ty) => output.push_str(float_ty.name_str()),
|
||||||
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
|
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
|
||||||
ty::Adt(def, substs) => {
|
ty::Adt(def, substs) => {
|
||||||
if def.is_enum() && cpp_like_debuginfo {
|
let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).expect("layout error");
|
||||||
|
|
||||||
|
if def.is_enum() && cpp_like_debuginfo && !wants_c_like_enum_debuginfo(ty_and_layout) {
|
||||||
msvc_enum_fallback(tcx, t, def, substs, output, visited);
|
msvc_enum_fallback(tcx, t, def, substs, output, visited);
|
||||||
} else {
|
} else {
|
||||||
push_item_name(tcx, def.did(), qualified, output);
|
push_item_name(tcx, def.did(), qualified, output);
|
||||||
|
|
|
@ -16,8 +16,7 @@ async fn async_fn_test() {
|
||||||
|
|
||||||
// FIXME: No way to reliably check the filename.
|
// 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", {{.*}}, align: {{32|64}},
|
||||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0"
|
|
||||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
||||||
// For brevity, we only check the struct name and members of the last variant.
|
// For brevity, we only check the struct name and members of the last variant.
|
||||||
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
|
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
|
||||||
|
@ -40,10 +39,10 @@ async fn async_fn_test() {
|
||||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||||
// CHECK-NOT: flags: DIFlagArtificial
|
// CHECK-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// 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-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// 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-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// CHECK-SAME: )
|
||||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
|
// 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: [[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-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-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
||||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
|
// 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: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||||
// CHECK-NOT: flags: DIFlagArtificial
|
// CHECK-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// 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
|
// CHECK-SAME: flags: DIFlagArtificial
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -20,7 +20,6 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||||
|
|
||||||
// FIXME: No way to reliably check the filename.
|
// 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: "generator_env$0"
|
||||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
||||||
// For brevity, we only check the struct name and members of the last variant.
|
// For brevity, we only check the struct name and members of the last variant.
|
||||||
|
@ -44,10 +43,10 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||||
// CHECK-NOT: flags: DIFlagArtificial
|
// CHECK-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// 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-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// 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-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// CHECK-SAME: )
|
||||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
|
// 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_FN:!.*]] = !DINamespace(name: "generator_test"
|
||||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{generator_env#0}", scope: [[GEN_FN]]
|
// 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-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
||||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
|
// 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: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||||
// CHECK-NOT: flags: DIFlagArtificial
|
// CHECK-NOT: flags: DIFlagArtificial
|
||||||
// CHECK-SAME: )
|
// 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
|
// CHECK-SAME: flags: DIFlagArtificial
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
// cdb-command: g
|
// cdb-command: g
|
||||||
|
|
||||||
// cdb-command: dx a
|
// cdb-command: dx a
|
||||||
// cdb-check:a : Some({...}) [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<enum$<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: [variant] : Some
|
||||||
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
|
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
|
||||||
|
|
||||||
// cdb-command: dx b
|
// cdb-command: dx b
|
||||||
// cdb-check:b : None [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<enum$<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-check: [variant] : None
|
||||||
|
|
||||||
// cdb-command: dx c
|
// cdb-command: dx c
|
||||||
|
@ -97,4 +97,6 @@ fn main() {
|
||||||
zzz(); // #break
|
zzz(); // #break
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zzz() { () }
|
fn zzz() {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
|
@ -33,10 +33,10 @@
|
||||||
// gdb-check:type = type_names::mod1::Enum2
|
// gdb-check:type = type_names::mod1::Enum2
|
||||||
|
|
||||||
// gdb-command:whatis generic_enum_1
|
// 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-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
|
// TUPLES
|
||||||
// gdb-command:whatis tuple1
|
// gdb-command:whatis tuple1
|
||||||
|
@ -159,10 +159,10 @@
|
||||||
|
|
||||||
// FOREIGN TYPES
|
// FOREIGN TYPES
|
||||||
// gdb-command:whatis foreign1
|
// gdb-command:whatis foreign1
|
||||||
// gdb-check:type = *mut ForeignType1
|
// gdb-check:type = *mut type_names::{extern#0}::ForeignType1
|
||||||
|
|
||||||
// gdb-command:whatis foreign2
|
// gdb-command:whatis foreign2
|
||||||
// gdb-check:type = *mut ForeignType2
|
// gdb-check:type = *mut type_names::mod1::{extern#0}::ForeignType2
|
||||||
|
|
||||||
// === CDB TESTS ==================================================================================
|
// === CDB TESTS ==================================================================================
|
||||||
|
|
||||||
|
@ -178,9 +178,9 @@
|
||||||
// cdb-command:dv /t *_enum_*
|
// cdb-command:dv /t *_enum_*
|
||||||
// cdb-check:union enum$<type_names::Enum1> simple_enum_1 = [...]
|
// cdb-check:union enum$<type_names::Enum1> simple_enum_1 = [...]
|
||||||
// cdb-check:union enum$<type_names::Enum1> simple_enum_2 = [...]
|
// cdb-check:union enum$<type_names::Enum1> simple_enum_2 = [...]
|
||||||
// cdb-check:type_names::mod1::Enum2 simple_enum_3 = [...]
|
// cdb-check:union enum$<type_names::mod1::Enum2> simple_enum_3 = [...]
|
||||||
// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_1 = [...]
|
// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::mod1::Struct2> > generic_enum_1 = [...]
|
||||||
// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_2 = [...]
|
// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::Struct1> > generic_enum_2 = [...]
|
||||||
|
|
||||||
// TUPLES
|
// TUPLES
|
||||||
// cdb-command:dv /t tuple*
|
// cdb-command:dv /t tuple*
|
||||||
|
@ -258,8 +258,8 @@
|
||||||
|
|
||||||
// FOREIGN TYPES
|
// FOREIGN TYPES
|
||||||
// cdb-command:dv /t foreign*
|
// cdb-command:dv /t foreign*
|
||||||
// cdb-check:struct ForeignType2 * foreign2 = [...]
|
// cdb-check:struct type_names::mod1::extern$0::ForeignType2 * foreign2 = [...]
|
||||||
// cdb-check:struct ForeignType1 * foreign1 = [...]
|
// cdb-check:struct type_names::extern$0::ForeignType1 * foreign1 = [...]
|
||||||
|
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![feature(omit_gdb_pretty_printer_section)]
|
#![feature(omit_gdb_pretty_printer_section)]
|
||||||
|
@ -283,7 +283,6 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod mod1 {
|
mod mod1 {
|
||||||
pub use self::Enum2::{Variant1, Variant2};
|
|
||||||
pub struct Struct2;
|
pub struct Struct2;
|
||||||
|
|
||||||
pub enum Enum2 {
|
pub enum Enum2 {
|
||||||
|
@ -367,14 +366,14 @@ fn main() {
|
||||||
// Enums
|
// Enums
|
||||||
let simple_enum_1 = Variant1;
|
let simple_enum_1 = Variant1;
|
||||||
let simple_enum_2 = Variant2(0);
|
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_1: mod1::mod2::Enum3<mod1::Struct2> = mod1::mod2::Variant1;
|
||||||
let generic_enum_2 = mod1::mod2::Variant2(Struct1);
|
let generic_enum_2 = mod1::mod2::Variant2(Struct1);
|
||||||
|
|
||||||
// Tuples
|
// Tuples
|
||||||
let tuple1 = (8u32, Struct1, mod1::mod2::Variant2(mod1::Struct2));
|
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
|
// Box
|
||||||
let box1 = (Box::new(1f32), 0i32);
|
let box1 = (Box::new(1f32), 0i32);
|
||||||
|
@ -404,7 +403,7 @@ fn main() {
|
||||||
|
|
||||||
let vec1 = vec![0_usize, 2, 3];
|
let vec1 = vec![0_usize, 2, 3];
|
||||||
let slice1 = &*vec1;
|
let slice1 = &*vec1;
|
||||||
let vec2 = vec![mod1::Variant2(Struct1)];
|
let vec2 = vec![mod1::Enum2::Variant2(Struct1)];
|
||||||
let slice2 = &*vec2;
|
let slice2 = &*vec2;
|
||||||
|
|
||||||
// Trait Objects
|
// Trait Objects
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue