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:
Michael Woerister 2022-03-03 11:15:25 +01:00
parent 0ac4658909
commit 07ebc13d87
18 changed files with 2307 additions and 1778 deletions

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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

View File

@ -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())
}

View 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)
}
}
}
}

View File

@ -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)>,
}

View 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 }
}

View File

@ -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),

View File

@ -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()
}

View File

@ -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,
}
}

View File

@ -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);

View File

@ -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]],

View File

@ -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() {

View File

@ -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]],

View File

@ -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() {

View File

@ -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() {
()
}

View File

@ -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