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
@ -140,8 +140,8 @@ pub(crate) unsafe fn codegen(
|
||||
llvm::LLVMDisposeBuilder(llbuilder);
|
||||
|
||||
if tcx.sess.opts.debuginfo != DebugInfo::None {
|
||||
let dbg_cx = debuginfo::CrateDebugContext::new(llmod);
|
||||
debuginfo::metadata::compile_unit_metadata(tcx, module_name, &dbg_cx);
|
||||
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
|
||||
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
|
||||
dbg_cx.finalize(tcx.sess);
|
||||
}
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
|
||||
llvm::LLVMSetGlobalConstant(g, llvm::True);
|
||||
}
|
||||
|
||||
debuginfo::create_global_var_metadata(self, def_id, g);
|
||||
debuginfo::build_global_var_di_node(self, def_id, g);
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
|
@ -95,7 +95,7 @@ pub struct CodegenCx<'ll, 'tcx> {
|
||||
pub isize_ty: &'ll Type,
|
||||
|
||||
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
|
||||
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
|
||||
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
|
||||
|
||||
eh_personality: Cell<Option<&'ll Value>>,
|
||||
eh_catch_typeinfo: Cell<Option<&'ll Value>>,
|
||||
@ -396,8 +396,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
};
|
||||
|
||||
let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None {
|
||||
let dctx = debuginfo::CrateDebugContext::new(llmod);
|
||||
debuginfo::metadata::compile_unit_metadata(tcx, codegen_unit.name().as_str(), &dctx);
|
||||
let dctx = debuginfo::CodegenUnitDebugContext::new(llmod);
|
||||
debuginfo::metadata::build_compile_unit_di_node(
|
||||
tcx,
|
||||
codegen_unit.name().as_str(),
|
||||
&dctx,
|
||||
);
|
||||
Some(dctx)
|
||||
} else {
|
||||
None
|
||||
|
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 self::metadata::{file_metadata, type_metadata, TypeMap};
|
||||
use self::metadata::{file_metadata, type_di_node};
|
||||
use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
|
||||
use self::namespace::mangled_name_of_instance;
|
||||
use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
|
||||
@ -20,7 +20,7 @@ use crate::value::Value;
|
||||
use rustc_codegen_ssa::debuginfo::type_names;
|
||||
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||
use rustc_index::vec::IndexVec;
|
||||
@ -32,7 +32,7 @@ use rustc_session::config::{self, DebugInfo};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span};
|
||||
use rustc_target::abi::{Primitive, Size};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
use libc::c_uint;
|
||||
use smallvec::SmallVec;
|
||||
@ -48,7 +48,7 @@ mod namespace;
|
||||
mod utils;
|
||||
|
||||
pub use self::create_scope_map::compute_mir_scopes;
|
||||
pub use self::metadata::create_global_var_metadata;
|
||||
pub use self::metadata::build_global_var_di_node;
|
||||
pub use self::metadata::extend_scope_to_file;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
@ -57,24 +57,18 @@ const DW_TAG_auto_variable: c_uint = 0x100;
|
||||
const DW_TAG_arg_variable: c_uint = 0x101;
|
||||
|
||||
/// A context object for maintaining all state needed by the debuginfo module.
|
||||
pub struct CrateDebugContext<'a, 'tcx> {
|
||||
llcontext: &'a llvm::Context,
|
||||
llmod: &'a llvm::Module,
|
||||
builder: &'a mut DIBuilder<'a>,
|
||||
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'a DIFile>>,
|
||||
created_enum_disr_types: RefCell<FxHashMap<(DefId, Primitive), &'a DIType>>,
|
||||
pub struct CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
llcontext: &'ll llvm::Context,
|
||||
llmod: &'ll llvm::Module,
|
||||
builder: &'ll mut DIBuilder<'ll>,
|
||||
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>,
|
||||
|
||||
type_map: TypeMap<'a, 'tcx>,
|
||||
namespace_map: RefCell<DefIdMap<&'a DIScope>>,
|
||||
|
||||
recursion_marker_type: OnceCell<&'a DIType>,
|
||||
|
||||
// This collection is used to assert that composite types (structs, enums,
|
||||
// ...) have their members only set once:
|
||||
composite_types_completed: RefCell<FxHashSet<&'a DIType>>,
|
||||
type_map: metadata::TypeMap<'ll, 'tcx>,
|
||||
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
|
||||
recursion_marker_type: OnceCell<&'ll DIType>,
|
||||
}
|
||||
|
||||
impl Drop for CrateDebugContext<'_, '_> {
|
||||
impl Drop for CodegenUnitDebugContext<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _));
|
||||
@ -82,22 +76,20 @@ impl Drop for CrateDebugContext<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> {
|
||||
pub fn new(llmod: &'a llvm::Module) -> Self {
|
||||
debug!("CrateDebugContext::new");
|
||||
impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
pub fn new(llmod: &'ll llvm::Module) -> Self {
|
||||
debug!("CodegenUnitDebugContext::new");
|
||||
let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
|
||||
// DIBuilder inherits context from the module, so we'd better use the same one
|
||||
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
|
||||
CrateDebugContext {
|
||||
CodegenUnitDebugContext {
|
||||
llcontext,
|
||||
llmod,
|
||||
builder,
|
||||
created_files: Default::default(),
|
||||
created_enum_disr_types: Default::default(),
|
||||
type_map: Default::default(),
|
||||
namespace_map: RefCell::new(Default::default()),
|
||||
recursion_marker_type: OnceCell::new(),
|
||||
composite_types_completed: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,7 +407,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
signature.push(if fn_abi.ret.is_ignore() {
|
||||
None
|
||||
} else {
|
||||
Some(type_metadata(cx, fn_abi.ret.layout.ty))
|
||||
Some(type_di_node(cx, fn_abi.ret.layout.ty))
|
||||
});
|
||||
|
||||
// Arguments types
|
||||
@ -440,11 +432,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
}
|
||||
_ => t,
|
||||
};
|
||||
Some(type_metadata(cx, t))
|
||||
Some(type_di_node(cx, t))
|
||||
}));
|
||||
} else {
|
||||
signature
|
||||
.extend(fn_abi.args.iter().map(|arg| Some(type_metadata(cx, arg.layout.ty))));
|
||||
.extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty))));
|
||||
}
|
||||
|
||||
create_DIArray(DIB(cx), &signature[..])
|
||||
@ -467,7 +459,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
if let GenericArgKind::Type(ty) = kind.unpack() {
|
||||
let actual_type =
|
||||
cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
|
||||
let actual_type_metadata = type_metadata(cx, actual_type);
|
||||
let actual_type_metadata = type_di_node(cx, actual_type);
|
||||
let name = name.as_str();
|
||||
Some(unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
|
||||
@ -522,7 +514,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
if cx.sess().opts.debuginfo == DebugInfo::Full
|
||||
&& !impl_self_ty.needs_subst()
|
||||
{
|
||||
Some(type_metadata(cx, impl_self_ty))
|
||||
Some(type_di_node(cx, impl_self_ty))
|
||||
} else {
|
||||
Some(namespace::item_namespace(cx, def.did()))
|
||||
}
|
||||
@ -569,7 +561,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
vtable: Self::Value,
|
||||
) {
|
||||
metadata::create_vtable_metadata(self, ty, trait_ref, vtable)
|
||||
metadata::create_vtable_di_node(self, ty, trait_ref, vtable)
|
||||
}
|
||||
|
||||
fn extend_scope_to_file(
|
||||
@ -597,7 +589,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
let loc = self.lookup_debug_loc(span.lo());
|
||||
let file_metadata = file_metadata(self, &loc.file);
|
||||
|
||||
let type_metadata = type_metadata(self, variable_type);
|
||||
let type_metadata = type_di_node(self, variable_type);
|
||||
|
||||
let (argument_index, dwarf_tag) = match variable_kind {
|
||||
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Utility Functions.
|
||||
|
||||
use super::namespace::item_namespace;
|
||||
use super::CrateDebugContext;
|
||||
use super::CodegenUnitDebugContext;
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
||||
@ -35,7 +35,7 @@ pub fn create_DIArray<'ll>(
|
||||
#[inline]
|
||||
pub fn debug_context<'a, 'll, 'tcx>(
|
||||
cx: &'a CodegenCx<'ll, 'tcx>,
|
||||
) -> &'a CrateDebugContext<'ll, 'tcx> {
|
||||
) -> &'a CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
cx.dbg_cx.as_ref().unwrap()
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,34 @@
|
||||
use rustc_middle::ty::{self, layout::TyAndLayout};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
// FIXME(eddyb) find a place for this (or a way to replace it).
|
||||
pub mod type_names;
|
||||
|
||||
/// Returns true if we want to generate a DW_TAG_enumeration_type description for
|
||||
/// this instead of a DW_TAG_struct_type with DW_TAG_variant_part.
|
||||
///
|
||||
/// NOTE: This is somewhat inconsistent right now: For empty enums and enums with a single
|
||||
/// fieldless variant, we generate DW_TAG_struct_type, although a
|
||||
/// DW_TAG_enumeration_type would be a better fit.
|
||||
pub fn wants_c_like_enum_debuginfo<'tcx>(enum_type_and_layout: TyAndLayout<'tcx>) -> bool {
|
||||
match enum_type_and_layout.ty.kind() {
|
||||
ty::Adt(adt_def, _) => {
|
||||
if !adt_def.is_enum() {
|
||||
return false;
|
||||
}
|
||||
|
||||
match adt_def.variants().len() {
|
||||
0 => false,
|
||||
1 => {
|
||||
// Univariant enums unless they are zero-sized
|
||||
enum_type_and_layout.size != Size::ZERO && adt_def.all_fields().count() == 0
|
||||
}
|
||||
_ => {
|
||||
// Enums with more than one variant if they have no fields
|
||||
adt_def.all_fields().count() == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,15 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
|
||||
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
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_target::abi::{Integer, TagEncoding, Variants};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::debuginfo::wants_c_like_enum_debuginfo;
|
||||
|
||||
// Compute the name of the type as it should be stored in debuginfo. Does not do
|
||||
// any caching, i.e., calling the function twice with the same type will also do
|
||||
// the work twice. The `qualified` parameter only affects the first level of the
|
||||
@ -71,7 +73,9 @@ fn push_debuginfo_type_name<'tcx>(
|
||||
ty::Float(float_ty) => output.push_str(float_ty.name_str()),
|
||||
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
|
||||
ty::Adt(def, substs) => {
|
||||
if def.is_enum() && cpp_like_debuginfo {
|
||||
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);
|
||||
} else {
|
||||
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.
|
||||
|
||||
// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0", {{.*}}, align: {{32|64}},
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
||||
// For brevity, we only check the struct name and members of the last variant.
|
||||
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
|
||||
@ -40,10 +39,10 @@ async fn async_fn_test() {
|
||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK: [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
|
||||
|
@ -18,7 +18,7 @@ async fn async_fn_test() {
|
||||
|
||||
// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[ASYNC_FN]]
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[ASYNC_FN]],
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
|
||||
@ -50,7 +50,7 @@ async fn async_fn_test() {
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[ASYNC_FN]],
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]],
|
||||
// CHECK-SAME: flags: DIFlagArtificial
|
||||
|
||||
fn main() {
|
||||
|
@ -20,7 +20,6 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
|
||||
// FIXME: No way to reliably check the filename.
|
||||
|
||||
// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0"
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
||||
// 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-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK: [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
|
||||
|
@ -22,7 +22,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
|
||||
// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{generator_env#0}", scope: [[GEN_FN]]
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN_FN]],
|
||||
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
|
||||
@ -54,7 +54,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN_FN]],
|
||||
// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]],
|
||||
// CHECK-SAME: flags: DIFlagArtificial
|
||||
|
||||
fn main() {
|
||||
|
@ -4,14 +4,14 @@
|
||||
// cdb-command: g
|
||||
|
||||
// cdb-command: dx a
|
||||
// cdb-check:a : Some({...}) [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check:a : Some({...}) [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [variant] : Some
|
||||
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
|
||||
|
||||
// cdb-command: dx b
|
||||
// cdb-check:b : None [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
|
||||
// cdb-check:b : None [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [<Raw View>] [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
|
||||
// cdb-check: [variant] : None
|
||||
|
||||
// cdb-command: dx c
|
||||
@ -78,7 +78,7 @@ pub enum NicheLayoutEnum {
|
||||
Tag2,
|
||||
}
|
||||
|
||||
pub enum Empty { }
|
||||
pub enum Empty {}
|
||||
|
||||
fn main() {
|
||||
let a = Some(CStyleEnum::Low);
|
||||
@ -97,4 +97,6 @@ fn main() {
|
||||
zzz(); // #break
|
||||
}
|
||||
|
||||
fn zzz() { () }
|
||||
fn zzz() {
|
||||
()
|
||||
}
|
||||
|
@ -33,10 +33,10 @@
|
||||
// gdb-check:type = type_names::mod1::Enum2
|
||||
|
||||
// gdb-command:whatis generic_enum_1
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3<type_names::mod1::Struct2>
|
||||
|
||||
// gdb-command:whatis generic_enum_2
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3
|
||||
// gdb-check:type = type_names::mod1::mod2::Enum3<type_names::Struct1>
|
||||
|
||||
// TUPLES
|
||||
// gdb-command:whatis tuple1
|
||||
@ -159,10 +159,10 @@
|
||||
|
||||
// FOREIGN TYPES
|
||||
// gdb-command:whatis foreign1
|
||||
// gdb-check:type = *mut ForeignType1
|
||||
// gdb-check:type = *mut type_names::{extern#0}::ForeignType1
|
||||
|
||||
// gdb-command:whatis foreign2
|
||||
// gdb-check:type = *mut ForeignType2
|
||||
// gdb-check:type = *mut type_names::mod1::{extern#0}::ForeignType2
|
||||
|
||||
// === CDB TESTS ==================================================================================
|
||||
|
||||
@ -178,9 +178,9 @@
|
||||
// cdb-command:dv /t *_enum_*
|
||||
// cdb-check:union enum$<type_names::Enum1> simple_enum_1 = [...]
|
||||
// cdb-check:union enum$<type_names::Enum1> simple_enum_2 = [...]
|
||||
// cdb-check:type_names::mod1::Enum2 simple_enum_3 = [...]
|
||||
// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_1 = [...]
|
||||
// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_2 = [...]
|
||||
// cdb-check:union enum$<type_names::mod1::Enum2> simple_enum_3 = [...]
|
||||
// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::mod1::Struct2> > generic_enum_1 = [...]
|
||||
// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::Struct1> > generic_enum_2 = [...]
|
||||
|
||||
// TUPLES
|
||||
// cdb-command:dv /t tuple*
|
||||
@ -258,8 +258,8 @@
|
||||
|
||||
// FOREIGN TYPES
|
||||
// cdb-command:dv /t foreign*
|
||||
// cdb-check:struct ForeignType2 * foreign2 = [...]
|
||||
// cdb-check:struct ForeignType1 * foreign1 = [...]
|
||||
// cdb-check:struct type_names::mod1::extern$0::ForeignType2 * foreign2 = [...]
|
||||
// cdb-check:struct type_names::extern$0::ForeignType1 * foreign1 = [...]
|
||||
|
||||
#![allow(unused_variables)]
|
||||
#![feature(omit_gdb_pretty_printer_section)]
|
||||
@ -283,7 +283,6 @@ extern "C" {
|
||||
}
|
||||
|
||||
mod mod1 {
|
||||
pub use self::Enum2::{Variant1, Variant2};
|
||||
pub struct Struct2;
|
||||
|
||||
pub enum Enum2 {
|
||||
@ -367,14 +366,14 @@ fn main() {
|
||||
// Enums
|
||||
let simple_enum_1 = Variant1;
|
||||
let simple_enum_2 = Variant2(0);
|
||||
let simple_enum_3 = mod1::Variant2(Struct1);
|
||||
let simple_enum_3 = mod1::Enum2::Variant2(Struct1);
|
||||
|
||||
let generic_enum_1: mod1::mod2::Enum3<mod1::Struct2> = mod1::mod2::Variant1;
|
||||
let generic_enum_2 = mod1::mod2::Variant2(Struct1);
|
||||
|
||||
// Tuples
|
||||
let tuple1 = (8u32, Struct1, mod1::mod2::Variant2(mod1::Struct2));
|
||||
let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Variant1, 'x');
|
||||
let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Enum2::Variant1, 'x');
|
||||
|
||||
// Box
|
||||
let box1 = (Box::new(1f32), 0i32);
|
||||
@ -404,7 +403,7 @@ fn main() {
|
||||
|
||||
let vec1 = vec![0_usize, 2, 3];
|
||||
let slice1 = &*vec1;
|
||||
let vec2 = vec![mod1::Variant2(Struct1)];
|
||||
let vec2 = vec![mod1::Enum2::Variant2(Struct1)];
|
||||
let slice2 = &*vec2;
|
||||
|
||||
// Trait Objects
|
||||
|
Loading…
x
Reference in New Issue
Block a user