mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
Auto merge of #85292 - wesleywiser:enum_debuginfo, r=michaelwoerister
Improve debugging experience for enums on windows-msvc This PR makes significant improvements over the status quo of debugging enums on the windows-msvc platform with either WinDbg or Visual Studio in three ways: 1. Improves the debugger experience for directly tagged enums. 2. Fixes a bug which caused the debugger to sometimes show the wrong debug info for niche layout enums. For example, `Option<&u32>` could sometimes use the debug info for `Option<&f64>` instead leading to nonsensical variable values in the debugger. 3. Significantly improves the debugger experience for niche-layout enums. Let's look at a few examples: ```rust pub enum CStyleEnum { Base = 2, Exponent = 16, } pub enum NicheLayoutEnum { Tag1, Data { my_data: CStyleEnum }, Tag2, Tag3, Tag4, } pub enum OtherEnum<T> { Case1(T), Case2(T), } fn main() { let a = Some(CStyleEnum::Base); let b = Option::<CStyleEnum>::None; let c = NicheLayoutEnum::Tag1; let d = NicheLayoutEnum::Data { my_data: CStyleEnum::Exponent }; let e = NicheLayoutEnum::Tag2; let f = Some(&1u32); let g = Option::<&'static u32>::None; let h = Some(&2u64); let i = Option::<&'static u64>::None; let j = Some(12u32); let k = Option::<u32>::None; let l = Some(12.34f64); let m = Option::<f64>::None; let n = CStyleEnum::Base; let o = CStyleEnum::Exponent; let p = Some("IAMA optional string!".to_string()); let q = OtherEnum::Case1(42u32); } ``` This is what WinDbg Preview shows using the latest rustc nightly: ![image](https://user-images.githubusercontent.com/831192/118285353-57c10780-b49f-11eb-97aa-db3abfc09508.png) Most of the variables don't show a meaningful value expect for a few cases that we have targeted natvis definitions covering. Even worse, drilling into many of these variables shows information that can be difficult to interpret without an understanding of the layout of Rust types: ![image](https://user-images.githubusercontent.com/831192/118285609-a1a9ed80-b49f-11eb-9c29-b14576984647.png) With the changes in this PR, we're able to write two natvis definitions that cover all enum cases generally. After building with these changes, WinDbg now shows this instead: ![image](https://user-images.githubusercontent.com/831192/118287730-be472500-b4a1-11eb-8cad-8f6a91c7516b.png) Drilling into the same variables, we can see much more useful information: ![image](https://user-images.githubusercontent.com/831192/118287888-e20a6b00-b4a1-11eb-927f-32cf33a31c16.png) Fixes #84670 Fixes #84671
This commit is contained in:
commit
2577825799
@ -1457,7 +1457,6 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
enum_type: Ty<'tcx>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
tag_type_metadata: Option<&'ll DIType>,
|
||||
containing_scope: &'ll DIScope,
|
||||
common_members: Vec<Option<&'ll DIType>>,
|
||||
span: Span,
|
||||
}
|
||||
@ -1486,13 +1485,9 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
_ => bug!(),
|
||||
};
|
||||
|
||||
// This will always find the metadata in the type map.
|
||||
let fallback = use_enum_fallback(cx);
|
||||
let self_metadata = if fallback {
|
||||
self.containing_scope
|
||||
} else {
|
||||
type_metadata(cx, self.enum_type, self.span)
|
||||
};
|
||||
// This will always find the metadata in the type map.
|
||||
let self_metadata = type_metadata(cx, self.enum_type, self.span);
|
||||
|
||||
match self.layout.variants {
|
||||
Variants::Single { index } => {
|
||||
@ -1507,7 +1502,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
cx,
|
||||
self.layout,
|
||||
variant_info,
|
||||
NoTag,
|
||||
None,
|
||||
self_metadata,
|
||||
self.span,
|
||||
);
|
||||
@ -1539,13 +1534,26 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
..
|
||||
} => {
|
||||
let tag_info = if fallback {
|
||||
RegularTag {
|
||||
// For MSVC, we generate a union of structs for each variant with an explicit
|
||||
// discriminant field roughly equivalent to the following C:
|
||||
// ```c
|
||||
// union enum$<{name}> {
|
||||
// struct {variant 0 name} {
|
||||
// tag$ variant$;
|
||||
// <variant 0 fields>
|
||||
// } variant0;
|
||||
// <other variant structs>
|
||||
// }
|
||||
// ```
|
||||
// The natvis in `intrinsic.nativs` then matches on `this.variant0.variant$` to
|
||||
// determine which variant is active and then displays it.
|
||||
Some(DirectTag {
|
||||
tag_field: Field::from(tag_field),
|
||||
tag_type_metadata: self.tag_type_metadata.unwrap(),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// This doesn't matter in this case.
|
||||
NoTag
|
||||
None
|
||||
};
|
||||
variants
|
||||
.iter_enumerated()
|
||||
@ -1574,7 +1582,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
|
||||
MemberDescription {
|
||||
name: if fallback {
|
||||
String::new()
|
||||
format!("variant{}", i.as_u32())
|
||||
} else {
|
||||
variant_info.variant_name()
|
||||
},
|
||||
@ -1599,77 +1607,135 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
ref variants,
|
||||
tag_field,
|
||||
} => {
|
||||
let calculate_niche_value = |i: VariantIdx| {
|
||||
if i == dataful_variant {
|
||||
None
|
||||
} else {
|
||||
let value = (i.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)
|
||||
}
|
||||
};
|
||||
|
||||
// For MSVC, we will generate a union of two fields, one for the dataful variant
|
||||
// and one that just points to the discriminant. We also create an enum 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.
|
||||
if fallback {
|
||||
let variant = self.layout.for_variant(cx, dataful_variant);
|
||||
// Create a description of the non-null variant.
|
||||
let (variant_type_metadata, member_description_factory) = describe_enum_variant(
|
||||
let dataful_variant_layout = self.layout.for_variant(cx, dataful_variant);
|
||||
|
||||
let mut discr_enum_ty = tag.value.to_ty(cx.tcx);
|
||||
// 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`.
|
||||
if let ty::RawPtr(_) = discr_enum_ty.kind() {
|
||||
discr_enum_ty = cx.tcx.types.usize;
|
||||
}
|
||||
|
||||
let tags: Vec<_> = variants
|
||||
.iter_enumerated()
|
||||
.filter_map(|(variant_idx, _)| {
|
||||
calculate_niche_value(variant_idx).map(|tag| {
|
||||
let variant = variant_info_for(variant_idx);
|
||||
let name = variant.variant_name();
|
||||
|
||||
Some(unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateEnumerator(
|
||||
DIB(cx),
|
||||
name.as_ptr().cast(),
|
||||
name.len(),
|
||||
tag as i64,
|
||||
!discr_enum_ty.is_signed(),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let discr_enum = unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateEnumerationType(
|
||||
DIB(cx),
|
||||
self_metadata,
|
||||
"Discriminant$".as_ptr().cast(),
|
||||
"Discriminant$".len(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
tag.value.size(cx).bits(),
|
||||
tag.value.align(cx).abi.bits() as u32,
|
||||
create_DIArray(DIB(cx), &tags),
|
||||
type_metadata(cx, discr_enum_ty, self.span),
|
||||
true,
|
||||
)
|
||||
};
|
||||
|
||||
let variant_info = variant_info_for(dataful_variant);
|
||||
let (variant_type_metadata, member_desc_factory) = describe_enum_variant(
|
||||
cx,
|
||||
variant,
|
||||
variant_info_for(dataful_variant),
|
||||
OptimizedTag,
|
||||
self.containing_scope,
|
||||
dataful_variant_layout,
|
||||
variant_info,
|
||||
Some(NicheTag),
|
||||
self_metadata,
|
||||
self.span,
|
||||
);
|
||||
|
||||
let variant_member_descriptions =
|
||||
member_description_factory.create_member_descriptions(cx);
|
||||
let member_descriptions = member_desc_factory.create_member_descriptions(cx);
|
||||
|
||||
set_members_of_composite_type(
|
||||
cx,
|
||||
self.enum_type,
|
||||
variant_type_metadata,
|
||||
variant_member_descriptions,
|
||||
member_descriptions,
|
||||
Some(&self.common_members),
|
||||
);
|
||||
|
||||
// Encode the information about the null variant in the union
|
||||
// member's name.
|
||||
let mut name = String::from("RUST$ENCODED$ENUM$");
|
||||
// Right now it's not even going to work for `niche_start > 0`,
|
||||
// and for multiple niche variants it only supports the first.
|
||||
fn compute_field_path<'a, 'tcx>(
|
||||
cx: &CodegenCx<'a, 'tcx>,
|
||||
name: &mut String,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
offset: Size,
|
||||
size: Size,
|
||||
) {
|
||||
for i in 0..layout.fields.count() {
|
||||
let field_offset = layout.fields.offset(i);
|
||||
if field_offset > offset {
|
||||
continue;
|
||||
}
|
||||
let inner_offset = offset - field_offset;
|
||||
let field = layout.field(cx, i);
|
||||
if inner_offset + size <= field.size {
|
||||
write!(name, "{}$", i).unwrap();
|
||||
compute_field_path(cx, name, field, inner_offset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
compute_field_path(
|
||||
cx,
|
||||
&mut name,
|
||||
self.layout,
|
||||
self.layout.fields.offset(tag_field),
|
||||
self.layout.field(cx, tag_field).size,
|
||||
);
|
||||
let variant_info = variant_info_for(*niche_variants.start());
|
||||
variant_info.map_struct_name(|variant_name| {
|
||||
name.push_str(variant_name);
|
||||
});
|
||||
let (size, align) =
|
||||
cx.size_and_align_of(dataful_variant_layout.field(cx, tag_field).ty);
|
||||
|
||||
// Create the (singleton) list of descriptions of union members.
|
||||
vec![MemberDescription {
|
||||
name,
|
||||
type_metadata: variant_type_metadata,
|
||||
offset: Size::ZERO,
|
||||
size: variant.size,
|
||||
align: variant.align.abi,
|
||||
flags: DIFlags::FlagZero,
|
||||
discriminant: None,
|
||||
source_info: variant_info.source_info(cx),
|
||||
}]
|
||||
vec![
|
||||
MemberDescription {
|
||||
// Name the dataful variant so that we can identify it for natvis
|
||||
name: "dataful_variant".to_string(),
|
||||
type_metadata: variant_type_metadata,
|
||||
offset: Size::ZERO,
|
||||
size: self.layout.size,
|
||||
align: self.layout.align.abi,
|
||||
flags: DIFlags::FlagZero,
|
||||
discriminant: None,
|
||||
source_info: variant_info.source_info(cx),
|
||||
},
|
||||
MemberDescription {
|
||||
name: "discriminant".into(),
|
||||
type_metadata: discr_enum,
|
||||
offset: dataful_variant_layout.fields.offset(tag_field),
|
||||
size,
|
||||
align,
|
||||
flags: DIFlags::FlagZero,
|
||||
discriminant: None,
|
||||
source_info: None,
|
||||
},
|
||||
]
|
||||
} else {
|
||||
variants
|
||||
.iter_enumerated()
|
||||
@ -1681,7 +1747,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
cx,
|
||||
variant,
|
||||
variant_info,
|
||||
OptimizedTag,
|
||||
Some(NicheTag),
|
||||
self_metadata,
|
||||
self.span,
|
||||
);
|
||||
@ -1697,19 +1763,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
Some(&self.common_members),
|
||||
);
|
||||
|
||||
let niche_value = if i == dataful_variant {
|
||||
None
|
||||
} else {
|
||||
let value = (i.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)
|
||||
};
|
||||
let niche_value = calculate_niche_value(i);
|
||||
|
||||
MemberDescription {
|
||||
name: variant_info.variant_name(),
|
||||
@ -1771,14 +1825,10 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: terminology here should be aligned with `abi::TagEncoding`.
|
||||
// `OptimizedTag` is `TagEncoding::Niche`, `RegularTag` is `TagEncoding::Direct`.
|
||||
// `NoTag` should be removed; users should use `Option<EnumTagInfo>` instead.
|
||||
#[derive(Copy, Clone)]
|
||||
enum EnumTagInfo<'ll> {
|
||||
RegularTag { tag_field: Field, tag_type_metadata: &'ll DIType },
|
||||
OptimizedTag,
|
||||
NoTag,
|
||||
DirectTag { tag_field: Field, tag_type_metadata: &'ll DIType },
|
||||
NicheTag,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -1859,7 +1909,7 @@ fn describe_enum_variant(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
layout: layout::TyAndLayout<'tcx>,
|
||||
variant: VariantInfo<'_, 'tcx>,
|
||||
discriminant_info: EnumTagInfo<'ll>,
|
||||
discriminant_info: Option<EnumTagInfo<'ll>>,
|
||||
containing_scope: &'ll DIScope,
|
||||
span: Span,
|
||||
) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) {
|
||||
@ -1882,12 +1932,11 @@ fn describe_enum_variant(
|
||||
let (offsets, args) = if use_enum_fallback(cx) {
|
||||
// If this is not a univariant enum, there is also the discriminant field.
|
||||
let (discr_offset, discr_arg) = match discriminant_info {
|
||||
RegularTag { tag_field, .. } => {
|
||||
Some(DirectTag { tag_field, .. }) => {
|
||||
// We have the layout of an enum variant, we need the layout of the outer enum
|
||||
let enum_layout = cx.layout_of(layout.ty);
|
||||
let offset = enum_layout.fields.offset(tag_field.as_usize());
|
||||
let args =
|
||||
("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, tag_field.as_usize()).ty);
|
||||
let args = ("variant$".to_owned(), enum_layout.field(cx, tag_field.as_usize()).ty);
|
||||
(Some(offset), Some(args))
|
||||
}
|
||||
_ => (None, None),
|
||||
@ -1918,7 +1967,7 @@ fn describe_enum_variant(
|
||||
offsets,
|
||||
args,
|
||||
tag_type_metadata: match discriminant_info {
|
||||
RegularTag { tag_type_metadata, .. } => Some(tag_type_metadata),
|
||||
Some(DirectTag { tag_type_metadata, .. }) => Some(tag_type_metadata),
|
||||
_ => None,
|
||||
},
|
||||
span,
|
||||
@ -2048,9 +2097,9 @@ fn prepare_enum_metadata(
|
||||
|
||||
if use_enum_fallback(cx) {
|
||||
let discriminant_type_metadata = match layout.variants {
|
||||
Variants::Single { .. }
|
||||
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => None,
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. } => {
|
||||
Variants::Single { .. } => None,
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, ref tag, .. }
|
||||
| Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. } => {
|
||||
Some(discriminant_type_metadata(tag.value))
|
||||
}
|
||||
};
|
||||
@ -2062,7 +2111,7 @@ fn prepare_enum_metadata(
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateUnionType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
None,
|
||||
enum_name.as_ptr().cast(),
|
||||
enum_name.len(),
|
||||
file_metadata,
|
||||
@ -2088,7 +2137,6 @@ fn prepare_enum_metadata(
|
||||
enum_type,
|
||||
layout,
|
||||
tag_type_metadata: discriminant_type_metadata,
|
||||
containing_scope,
|
||||
common_members: vec![],
|
||||
span,
|
||||
}),
|
||||
@ -2241,7 +2289,6 @@ fn prepare_enum_metadata(
|
||||
enum_type,
|
||||
layout,
|
||||
tag_type_metadata: None,
|
||||
containing_scope,
|
||||
common_members: outer_fields,
|
||||
span,
|
||||
}),
|
||||
@ -2437,7 +2484,7 @@ fn create_union_stub(
|
||||
|
||||
llvm::LLVMRustDIBuilderCreateUnionType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
Some(containing_scope),
|
||||
union_type_name.as_ptr().cast(),
|
||||
union_type_name.len(),
|
||||
unknown_file_metadata(cx),
|
||||
|
@ -2038,7 +2038,7 @@ extern "C" {
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateUnionType(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Scope: &'a DIScope,
|
||||
Scope: Option<&'a DIScope>,
|
||||
Name: *const c_char,
|
||||
NameLen: size_t,
|
||||
File: &'a DIFile,
|
||||
|
@ -3,7 +3,8 @@
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty, TyCtxt};
|
||||
use rustc_target::abi::{TagEncoding, Variants};
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
@ -45,8 +46,12 @@ pub 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) => {
|
||||
push_item_name(tcx, def.did, qualified, output);
|
||||
push_type_params(tcx, substs, output, visited);
|
||||
if def.is_enum() && cpp_like_names {
|
||||
msvc_enum_fallback(tcx, t, def, substs, output, visited);
|
||||
} else {
|
||||
push_item_name(tcx, def.did, qualified, output);
|
||||
push_type_params(tcx, substs, output, visited);
|
||||
}
|
||||
}
|
||||
ty::Tuple(component_types) => {
|
||||
if cpp_like_names {
|
||||
@ -233,6 +238,54 @@ pub fn push_debuginfo_type_name<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// MSVC names enums differently than other platforms so that the debugging visualization
|
||||
// format (natvis) is able to understand enums and render the active variant correctly in the
|
||||
// debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
|
||||
// `EnumMemberDescriptionFactor::create_member_descriptions`.
|
||||
fn msvc_enum_fallback(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
def: &AdtDef,
|
||||
substs: SubstsRef<'tcx>,
|
||||
output: &mut String,
|
||||
visited: &mut FxHashSet<Ty<'tcx>>,
|
||||
) {
|
||||
let layout = tcx.layout_of(tcx.param_env(def.did).and(ty)).expect("layout error");
|
||||
|
||||
if let Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
|
||||
tag,
|
||||
variants,
|
||||
..
|
||||
} = &layout.variants
|
||||
{
|
||||
let dataful_variant_layout = &variants[*dataful_variant];
|
||||
|
||||
// calculate the range of values for the dataful variant
|
||||
let dataful_discriminant_range =
|
||||
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;
|
||||
|
||||
let min = dataful_discriminant_range.start();
|
||||
let min = tag.value.size(&tcx).truncate(*min);
|
||||
|
||||
let max = dataful_discriminant_range.end();
|
||||
let max = tag.value.size(&tcx).truncate(*max);
|
||||
|
||||
output.push_str("enum$<");
|
||||
push_item_name(tcx, def.did, true, output);
|
||||
push_type_params(tcx, substs, output, visited);
|
||||
|
||||
let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();
|
||||
|
||||
output.push_str(&format!(", {}, {}, {}>", min, max, dataful_variant_name));
|
||||
} else {
|
||||
output.push_str("enum$<");
|
||||
push_item_name(tcx, def.did, true, output);
|
||||
push_type_params(tcx, substs, output, visited);
|
||||
output.push('>');
|
||||
}
|
||||
}
|
||||
|
||||
fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
|
||||
if qualified {
|
||||
output.push_str(&tcx.crate_name(def_id.krate).as_str());
|
||||
|
@ -149,4 +149,57 @@
|
||||
<Synthetic Name="[...]"><DisplayString>...</DisplayString></Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="enum$<*>">
|
||||
<Intrinsic Name="tag" Expression="variant0.variant$" />
|
||||
<DisplayString Condition="tag() == 0">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 1" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 2" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 3" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 4" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 5" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 6" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 7" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 8" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 9" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 10" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 11" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 12" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 13" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 14" Optional="true">{tag(),en}</DisplayString>
|
||||
<DisplayString Condition="tag() == 15" Optional="true">{tag(),en}</DisplayString>
|
||||
|
||||
<Expand>
|
||||
<ExpandedItem Condition="tag() == 0">variant0</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 1" Optional="true">variant1</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 2" Optional="true">variant2</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 3" Optional="true">variant3</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 4" Optional="true">variant4</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 5" Optional="true">variant5</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 6" Optional="true">variant6</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 7" Optional="true">variant7</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 8" Optional="true">variant8</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 9" Optional="true">variant9</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 10" Optional="true">variant10</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 11" Optional="true">variant11</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 12" Optional="true">variant12</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 13" Optional="true">variant13</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 14" Optional="true">variant14</ExpandedItem>
|
||||
<ExpandedItem Condition="tag() == 15" Optional="true">variant15</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- $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 -->
|
||||
<Type Name="enum$<*, *, *, *>">
|
||||
<Intrinsic Name="tag" Expression="discriminant" />
|
||||
<Intrinsic Name="is_dataful" Expression="tag() >= $T2 && tag() <= $T3" />
|
||||
<DisplayString Condition="is_dataful()">{"$T4",sb}({dataful_variant})</DisplayString>
|
||||
<DisplayString Condition="!is_dataful()">{discriminant,en}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="is_dataful()">dataful_variant</ExpandedItem>
|
||||
<Synthetic Condition="is_dataful()" Name="[variant]">
|
||||
<DisplayString>{"$T4",sb}</DisplayString>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
@ -14,14 +14,6 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="core::option::Option<*>">
|
||||
<DisplayString Condition="RUST$ENUM$DISR == 0x0">None</DisplayString>
|
||||
<DisplayString Condition="RUST$ENUM$DISR == 0x1">Some({__0})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[value]" ExcludeView="simple" Condition="RUST$ENUM$DISR == 1">__0</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="core::option::Option<*>" Priority="MediumLow">
|
||||
<DisplayString Condition="*(void**)this == nullptr">None</DisplayString>
|
||||
<DisplayString>Some({($T1 *)this})</DisplayString>
|
||||
@ -30,15 +22,6 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="core::result::Result<*>">
|
||||
<DisplayString Condition="RUST$ENUM$DISR == 0x0">Ok({__0})</DisplayString>
|
||||
<DisplayString Condition="RUST$ENUM$DISR == 0x1">Err({(*($T2*) &__0)})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[value]" Condition="RUST$ENUM$DISR == 0x0">__0</Item>
|
||||
<Item Name="[value]" Condition="RUST$ENUM$DISR == 0x1">(*($T2*) &__0)</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="core::ptr::non_null::NonNull<*>">
|
||||
<DisplayString>{(void*) pointer}</DisplayString>
|
||||
<Expand>
|
||||
|
@ -17,33 +17,33 @@ 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: "generator-0", scope: [[ASYNC_FN]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0"
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
||||
// For brevity, we only check the struct name and members of the last variant.
|
||||
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 15,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 15,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 12,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 14,
|
||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[ASYNC_FN]],
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "RUST$ENUM$DISR", scope: [[S1]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant$", scope: [[S1]],
|
||||
// CHECK-SAME: flags: DIFlagArtificial
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
|
@ -21,33 +21,33 @@ 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-0", scope: [[GEN_FN]]
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0"
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
|
||||
// For brevity, we only check the struct name and members of the last variant.
|
||||
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 14,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 18,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 18,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 15,
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]],
|
||||
// CHECK-SAME: file: [[FILE]], line: 17,
|
||||
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN_FN]],
|
||||
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
// CHECK-SAME: )
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "RUST$ENUM$DISR", scope: [[S1]],
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant$", scope: [[S1]],
|
||||
// CHECK-SAME: flags: DIFlagArtificial
|
||||
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
|
||||
// CHECK-NOT: flags: DIFlagArtificial
|
||||
|
97
src/test/debuginfo/msvc-pretty-enums.rs
Normal file
97
src/test/debuginfo/msvc-pretty-enums.rs
Normal file
@ -0,0 +1,97 @@
|
||||
// only-cdb
|
||||
// ignore-tidy-linelength
|
||||
// compile-flags:-g
|
||||
|
||||
// cdb-command: g
|
||||
|
||||
// Note: The natvis used to visualize niche-layout enums don't work correctly in cdb
|
||||
// so the best we can do is to make sure we are generating the right debuginfo
|
||||
|
||||
// cdb-command: dx -r2 a,!
|
||||
// cdb-check:a,! [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Some]
|
||||
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
|
||||
// cdb-check: [+0x000] discriminant : 0x2 [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Discriminant$]
|
||||
|
||||
// cdb-command: dx -r2 b,!
|
||||
// cdb-check:b,! [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Some]
|
||||
// cdb-check: [+0x000] __0 : 0x11 [Type: msvc_pretty_enums::CStyleEnum]
|
||||
// cdb-check: [+0x000] discriminant : None (0x11) [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Discriminant$]
|
||||
|
||||
// cdb-command: dx -r2 c,!
|
||||
// cdb-check:c,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
|
||||
// cdb-check: [+0x000] my_data : 0x11 [Type: msvc_pretty_enums::CStyleEnum]
|
||||
// cdb-check: [+0x000] discriminant : Tag1 (0x11) [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]
|
||||
|
||||
// cdb-command: dx -r2 d,!
|
||||
// cdb-check:d,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
|
||||
// cdb-check: [+0x000] my_data : High (0x10) [Type: msvc_pretty_enums::CStyleEnum]
|
||||
// cdb-check: [+0x000] discriminant : 0x10 [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]
|
||||
|
||||
// cdb-command: dx -r2 e,!
|
||||
// cdb-check:e,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
|
||||
// cdb-check: [+0x000] my_data : 0x13 [Type: msvc_pretty_enums::CStyleEnum]
|
||||
// cdb-check: [+0x000] discriminant : Tag2 (0x13) [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]
|
||||
|
||||
// cdb-command: dx -r2 f,!
|
||||
// cdb-check:f,! [Type: enum$<core::option::Option<u32*>, 1, [...], Some>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Some]
|
||||
// cdb-check: [+0x000] __0 : 0x[...] : 0x1 [Type: unsigned int *]
|
||||
// cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Discriminant$]
|
||||
|
||||
// cdb-command: dx -r2 g,!
|
||||
// cdb-check:g,! [Type: enum$<core::option::Option<u32*>, 1, [...], Some>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Some]
|
||||
// cdb-check: [+0x000] __0 : 0x0 [Type: unsigned int *]
|
||||
// cdb-check: [+0x000] discriminant : None (0x0) [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Discriminant$]
|
||||
|
||||
// cdb-command: dx h
|
||||
// cdb-check:h : Some [Type: enum$<core::option::Option<u32>>]
|
||||
// cdb-check: [+0x000] variant$ : Some (0x1) [Type: core::option::Option]
|
||||
// cdb-check: [+0x004] __0 : 0xc [Type: unsigned int]
|
||||
|
||||
// cdb-command: dx i
|
||||
// cdb-check:i : None [Type: enum$<core::option::Option<u32>>]
|
||||
// cdb-check: [+0x000] variant$ : None (0x0) [Type: core::option::Option]
|
||||
|
||||
// cdb-command: dx j
|
||||
// cdb-check:j : High (0x10) [Type: msvc_pretty_enums::CStyleEnum]
|
||||
|
||||
// cdb-command: dx -r2 k,!
|
||||
// cdb-check:k,! [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]
|
||||
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>::Some]
|
||||
// cdb-check: [+0x000] __0 [Type: alloc::string::String]
|
||||
// cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>::Discriminant$]
|
||||
|
||||
pub enum CStyleEnum {
|
||||
Low = 2,
|
||||
High = 16,
|
||||
}
|
||||
|
||||
pub enum NicheLayoutEnum {
|
||||
Tag1,
|
||||
Data { my_data: CStyleEnum },
|
||||
Tag2,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = Some(CStyleEnum::Low);
|
||||
let b = Option::<CStyleEnum>::None;
|
||||
let c = NicheLayoutEnum::Tag1;
|
||||
let d = NicheLayoutEnum::Data { my_data: CStyleEnum::High };
|
||||
let e = NicheLayoutEnum::Tag2;
|
||||
let f = Some(&1u32);
|
||||
let g = Option::<&'static u32>::None;
|
||||
let h = Some(12u32);
|
||||
let i = Option::<u32>::None;
|
||||
let j = CStyleEnum::High;
|
||||
let k = Some("IAMA optional string!".to_string());
|
||||
|
||||
zzz(); // #break
|
||||
}
|
||||
|
||||
fn zzz() { () }
|
@ -1,6 +1,7 @@
|
||||
// ignore-freebsd: gdb package too new
|
||||
// only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
|
||||
// ignore-android: FIXME(#10381)
|
||||
// ignore-tidy-linelength
|
||||
// compile-flags:-g
|
||||
// min-gdb-version: 7.7
|
||||
// min-lldb-version: 310
|
||||
@ -111,11 +112,11 @@
|
||||
// NOTE: OsString doesn't have a .natvis entry yet.
|
||||
|
||||
// cdb-command: dx some
|
||||
// cdb-check:some : Some(8) [Type: [...]::Option<i16>]
|
||||
// cdb-check:some : Some [Type: enum$<core::option::Option<i16>>]
|
||||
// cdb-command: dx none
|
||||
// cdb-check:none : None [Type: [...]::Option<i64>]
|
||||
// cdb-check:none : None [Type: enum$<core::option::Option<i64>>]
|
||||
// cdb-command: dx some_string
|
||||
// cdb-check:some_string : Some("IAMA optional string!") [[...]::Option<[...]::String>]
|
||||
// cdb-check:some_string [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]
|
||||
|
||||
#![allow(unused_variables)]
|
||||
use std::ffi::OsString;
|
||||
|
Loading…
Reference in New Issue
Block a user