Skip to content

Commit 141546c

Browse files
committed
Generate better debuginfo for niche-layout enums
Previously, we would generate a single struct with the layout of the dataful variant plus an extra field whose name contained the value of the niche (this would only really work for things like `Option<&_>` where we can determine that the `None` case maps to `0` but for enums that have multiple tag only variants, this doesn't work). Now, we generate a union of two structs, one which is the layout of the dataful variant and one which just has a way of reading the discriminant. We also generate an enum which maps the discriminant value to the tag only variants. We also encode information about the range of values which correspond to the dataful variant in the type name and then use natvis to determine which union field we should display to the user. As a result of this change, all niche-layout enums render correctly in WinDbg and Visual Studio!
1 parent 2a025c1 commit 141546c

File tree

3 files changed

+191
-81
lines changed

3 files changed

+191
-81
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+127-72
Original file line numberDiff line numberDiff line change
@@ -1594,77 +1594,144 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
15941594
ref variants,
15951595
tag_field,
15961596
} => {
1597+
let calculate_niche_value = |i: VariantIdx| {
1598+
if i == dataful_variant {
1599+
None
1600+
} else {
1601+
let value = (i.as_u32() as u128)
1602+
.wrapping_sub(niche_variants.start().as_u32() as u128)
1603+
.wrapping_add(niche_start);
1604+
let value = tag.value.size(cx).truncate(value);
1605+
// NOTE(eddyb) do *NOT* remove this assert, until
1606+
// we pass the full 128-bit value to LLVM, otherwise
1607+
// truncation will be silent and remain undetected.
1608+
assert_eq!(value as u64 as u128, value);
1609+
Some(value as u64)
1610+
}
1611+
};
1612+
1613+
// For MSVC, we will generate a union of two structs, one for the dataful variant and one that just points to
1614+
// the discriminant field. We also create an enum that contains tag values for the non-dataful variants and
1615+
// make the discriminant field that type. We then use natvis to render the enum type correctly in Windbg/VS.
15971616
if fallback {
1598-
let variant = self.layout.for_variant(cx, dataful_variant);
1599-
// Create a description of the non-null variant.
1600-
let (variant_type_metadata, member_description_factory) = describe_enum_variant(
1617+
let unique_type_id = debug_context(cx)
1618+
.type_map
1619+
.borrow_mut()
1620+
.get_unique_type_id_of_enum_variant(cx, self.enum_type, "discriminant$");
1621+
1622+
let variant_metadata = create_struct_stub(
1623+
cx,
1624+
self.layout.ty,
1625+
&"discriminant$",
1626+
unique_type_id,
1627+
Some(self_metadata),
1628+
DIFlags::FlagArtificial,
1629+
);
1630+
1631+
let dataful_variant_layout = self.layout.for_variant(cx, dataful_variant);
1632+
1633+
let mut discr_enum_ty = tag.value.to_ty(cx.tcx);
1634+
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be a RawPtr.
1635+
// CodeView doesn't know what to do with enums whose base type is a pointer so we fix this up
1636+
// to just be `usize`.
1637+
if let ty::RawPtr(_) = discr_enum_ty.kind() {
1638+
discr_enum_ty = cx.tcx.types.usize;
1639+
}
1640+
1641+
let tags: Vec<_> = variants
1642+
.iter_enumerated()
1643+
.filter_map(|(variant_idx, _)| {
1644+
calculate_niche_value(variant_idx).map(|tag| {
1645+
let variant = variant_info_for(variant_idx);
1646+
let name = variant.variant_name();
1647+
1648+
Some(unsafe {
1649+
llvm::LLVMRustDIBuilderCreateEnumerator(
1650+
DIB(cx),
1651+
name.as_ptr().cast(),
1652+
name.len(),
1653+
tag as i64,
1654+
!discr_enum_ty.is_signed(),
1655+
)
1656+
})
1657+
})
1658+
})
1659+
.collect();
1660+
1661+
let discr_enum = unsafe {
1662+
llvm::LLVMRustDIBuilderCreateEnumerationType(
1663+
DIB(cx),
1664+
self_metadata,
1665+
"tag$".as_ptr().cast(),
1666+
"tag$".len(),
1667+
unknown_file_metadata(cx),
1668+
UNKNOWN_LINE_NUMBER,
1669+
tag.value.size(cx).bits(),
1670+
tag.value.align(cx).abi.bits() as u32,
1671+
create_DIArray(DIB(cx), &tags),
1672+
type_metadata(cx, discr_enum_ty, self.span),
1673+
true,
1674+
)
1675+
};
1676+
1677+
let (size, align) =
1678+
cx.size_and_align_of(dataful_variant_layout.field(cx, tag_field).ty);
1679+
let members = vec![MemberDescription {
1680+
name: "discriminant".to_string(),
1681+
type_metadata: discr_enum,
1682+
offset: dataful_variant_layout.fields.offset(tag_field),
1683+
size,
1684+
align,
1685+
flags: DIFlags::FlagArtificial,
1686+
discriminant: None,
1687+
source_info: None,
1688+
}];
1689+
1690+
set_members_of_composite_type(cx, self.enum_type, variant_metadata, members, None);
1691+
1692+
let variant_info = variant_info_for(dataful_variant);
1693+
let (variant_type_metadata, member_desc_factory) = describe_enum_variant(
16011694
cx,
1602-
variant,
1603-
variant_info_for(dataful_variant),
1695+
dataful_variant_layout,
1696+
variant_info,
16041697
Some(NicheTag),
16051698
self_metadata,
16061699
self.span,
16071700
);
16081701

1609-
let variant_member_descriptions =
1610-
member_description_factory.create_member_descriptions(cx);
1702+
let member_descriptions = member_desc_factory.create_member_descriptions(cx);
16111703

16121704
set_members_of_composite_type(
16131705
cx,
16141706
self.enum_type,
16151707
variant_type_metadata,
1616-
variant_member_descriptions,
1708+
member_descriptions,
16171709
Some(&self.common_members),
16181710
);
16191711

1620-
// Encode the information about the null variant in the union
1621-
// member's name.
1622-
let mut name = String::from("RUST$ENCODED$ENUM$");
1623-
// Right now it's not even going to work for `niche_start > 0`,
1624-
// and for multiple niche variants it only supports the first.
1625-
fn compute_field_path<'a, 'tcx>(
1626-
cx: &CodegenCx<'a, 'tcx>,
1627-
name: &mut String,
1628-
layout: TyAndLayout<'tcx>,
1629-
offset: Size,
1630-
size: Size,
1631-
) {
1632-
for i in 0..layout.fields.count() {
1633-
let field_offset = layout.fields.offset(i);
1634-
if field_offset > offset {
1635-
continue;
1636-
}
1637-
let inner_offset = offset - field_offset;
1638-
let field = layout.field(cx, i);
1639-
if inner_offset + size <= field.size {
1640-
write!(name, "{}$", i).unwrap();
1641-
compute_field_path(cx, name, field, inner_offset, size);
1642-
}
1643-
}
1644-
}
1645-
compute_field_path(
1646-
cx,
1647-
&mut name,
1648-
self.layout,
1649-
self.layout.fields.offset(tag_field),
1650-
self.layout.field(cx, tag_field).size,
1651-
);
1652-
let variant_info = variant_info_for(*niche_variants.start());
1653-
variant_info.map_struct_name(|variant_name| {
1654-
name.push_str(variant_name);
1655-
});
1656-
1657-
// Create the (singleton) list of descriptions of union members.
1658-
vec![MemberDescription {
1659-
name,
1660-
type_metadata: variant_type_metadata,
1661-
offset: Size::ZERO,
1662-
size: variant.size,
1663-
align: variant.align.abi,
1664-
flags: DIFlags::FlagZero,
1665-
discriminant: None,
1666-
source_info: variant_info.source_info(cx),
1667-
}]
1712+
vec![
1713+
MemberDescription {
1714+
// Name the dataful variant so that we can identify it for natvis
1715+
name: "dataful_variant".to_string(),
1716+
type_metadata: variant_type_metadata,
1717+
offset: Size::ZERO,
1718+
size: self.layout.size,
1719+
align: self.layout.align.abi,
1720+
flags: DIFlags::FlagZero,
1721+
discriminant: None,
1722+
source_info: variant_info.source_info(cx),
1723+
},
1724+
MemberDescription {
1725+
name: "discriminant$".into(),
1726+
type_metadata: variant_metadata,
1727+
offset: Size::ZERO,
1728+
size: self.layout.size,
1729+
align: self.layout.align.abi,
1730+
flags: DIFlags::FlagZero,
1731+
discriminant: None,
1732+
source_info: None,
1733+
},
1734+
]
16681735
} else {
16691736
variants
16701737
.iter_enumerated()
@@ -1692,19 +1759,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
16921759
Some(&self.common_members),
16931760
);
16941761

1695-
let niche_value = if i == dataful_variant {
1696-
None
1697-
} else {
1698-
let value = (i.as_u32() as u128)
1699-
.wrapping_sub(niche_variants.start().as_u32() as u128)
1700-
.wrapping_add(niche_start);
1701-
let value = tag.value.size(cx).truncate(value);
1702-
// NOTE(eddyb) do *NOT* remove this assert, until
1703-
// we pass the full 128-bit value to LLVM, otherwise
1704-
// truncation will be silent and remain undetected.
1705-
assert_eq!(value as u64 as u128, value);
1706-
Some(value as u64)
1707-
};
1762+
let niche_value = calculate_niche_value(i);
17081763

17091764
MemberDescription {
17101765
name: variant_info.variant_name(),
@@ -2040,9 +2095,9 @@ fn prepare_enum_metadata(
20402095

20412096
if use_enum_fallback(cx) {
20422097
let discriminant_type_metadata = match layout.variants {
2043-
Variants::Single { .. }
2044-
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => None,
2045-
Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. } => {
2098+
Variants::Single { .. } => None,
2099+
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, ref tag, .. }
2100+
| Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. } => {
20462101
Some(discriminant_type_metadata(tag.value))
20472102
}
20482103
};

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+50-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
use rustc_data_structures::fx::FxHashSet;
44
use rustc_hir as hir;
55
use rustc_hir::def_id::DefId;
6-
use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt};
6+
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty, TyCtxt};
7+
use rustc_target::abi::{TagEncoding, Variants};
78

89
use std::fmt::Write;
910

@@ -46,14 +47,10 @@ pub fn push_debuginfo_type_name<'tcx>(
4647
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
4748
ty::Adt(def, substs) => {
4849
if def.is_enum() && cpp_like_names {
49-
output.push_str("_enum<");
50-
}
51-
52-
push_item_name(tcx, def.did, qualified, output);
53-
push_type_params(tcx, substs, output, visited);
54-
55-
if def.is_enum() && cpp_like_names {
56-
output.push('>');
50+
msvc_enum_fallback(tcx, t, def, substs, output, visited);
51+
} else {
52+
push_item_name(tcx, def.did, qualified, output);
53+
push_type_params(tcx, substs, output, visited);
5754
}
5855
}
5956
ty::Tuple(component_types) => {
@@ -241,6 +238,50 @@ pub fn push_debuginfo_type_name<'tcx>(
241238
}
242239
}
243240

241+
fn msvc_enum_fallback(
242+
tcx: TyCtxt<'tcx>,
243+
ty: Ty<'tcx>,
244+
def: &AdtDef,
245+
substs: SubstsRef<'tcx>,
246+
output: &mut String,
247+
visited: &mut FxHashSet<Ty<'tcx>>,
248+
) {
249+
let layout = tcx.layout_of(tcx.param_env(def.did).and(ty)).expect("layout error");
250+
251+
if let Variants::Multiple {
252+
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
253+
tag,
254+
variants,
255+
..
256+
} = &layout.variants
257+
{
258+
let dataful_variant_layout = &variants[*dataful_variant];
259+
260+
// calculate the range of values for the dataful variant
261+
let dataful_discriminant_range =
262+
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;
263+
264+
let min = dataful_discriminant_range.start();
265+
let min = tag.value.size(&tcx).truncate(*min);
266+
267+
let max = dataful_discriminant_range.end();
268+
let max = tag.value.size(&tcx).truncate(*max);
269+
270+
output.push_str("_enum<");
271+
push_item_name(tcx, def.did, true, output);
272+
push_type_params(tcx, substs, output, visited);
273+
274+
let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();
275+
276+
output.push_str(&format!(", {}, {}, {}>", min, max, dataful_variant_name));
277+
} else {
278+
output.push_str("_enum<");
279+
push_item_name(tcx, def.did, true, output);
280+
push_type_params(tcx, substs, output, visited);
281+
output.push('>');
282+
}
283+
}
284+
244285
fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
245286
if qualified {
246287
output.push_str(&tcx.crate_name(def_id.krate).as_str());

src/etc/natvis/intrinsic.natvis

+14
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,18 @@
187187
<ExpandedItem Condition="tag() == 15" Optional="true">Variant15</ExpandedItem>
188188
</Expand>
189189
</Type>
190+
191+
<!-- $T1 is the name of the enum, $T2 is the low value of the dataful variant tag, $T3 is the high value of the dataful variant tag, $T4 is the name of the dataful variant -->
192+
<Type Name="_enum&lt;*, *, *, *&gt;">
193+
<Intrinsic Name="tag" Expression="discriminant$.discriminant" />
194+
<Intrinsic Name="is_dataful" Expression="tag() &gt;= $T2 &amp;&amp; tag() &lt;= $T3" />
195+
<DisplayString Condition="is_dataful()">{"$T4",sb}({dataful_variant})</DisplayString>
196+
<DisplayString Condition="!is_dataful()">{discriminant$.discriminant,en}</DisplayString>
197+
<Expand>
198+
<ExpandedItem Condition="is_dataful()">dataful_variant</ExpandedItem>
199+
<Synthetic Condition="is_dataful()" Name="[variant]">
200+
<DisplayString>{"$T4",sb}</DisplayString>
201+
</Synthetic>
202+
</Expand>
203+
</Type>
190204
</AutoVisualizer>

0 commit comments

Comments
 (0)