Skip to content

DW_AT_discr_value elided in DWARF for some enum variants #104625

Open
@bcantrill

Description

Code

There is a regression from 1.64.0 to 1.65.0 in the DWARF emitted for some variants for some enums, as the below program shows:

#[repr(u32)]
pub enum DontKnowWhyThisIsNeededButItIs {
    Blargh,
}

//
// When compiled on 1.65.0 or later, the `ThisIsBad` variant in the below
// `Foo` enum does not have a `DW_AT_discr_value` in the DWARF, as displayed
// by `dwarfdump`:
//
//
// ```
// ...
// < 2><0x00000049>      DW_TAG_structure_type
//                         DW_AT_name                  Foo
//                         DW_AT_byte_size             0x0000000c
//                         DW_AT_alignment             0x00000004
// < 3><0x00000050>        DW_TAG_variant_part
//                           DW_AT_discr                 <0x00000055>
// < 4><0x00000055>          DW_TAG_member
//                             DW_AT_type                  <0x00000180>
//                             DW_AT_alignment             0x00000004
//                             DW_AT_data_member_location  0
//                             DW_AT_artificial            yes(1)
// < 4><0x0000005c>          DW_TAG_variant
//                             DW_AT_discr_value           1
// < 5><0x0000005e>            DW_TAG_member
//                               DW_AT_name                  ThisIsFine
//                               DW_AT_type                  <0x00000094>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// < 4><0x0000006a>          DW_TAG_variant
// < 5><0x0000006b>            DW_TAG_member
//                               DW_AT_name                  ThisIsBad
//                               DW_AT_type                  <0x0000009b>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// < 4><0x00000077>          DW_TAG_variant
//                             DW_AT_discr_value           3
// < 5><0x00000079>            DW_TAG_member
//                               DW_AT_name                  ThisIsAlsoFine
//                               DW_AT_type                  <0x000000b9>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// < 4><0x00000085>          DW_TAG_variant
//                             DW_AT_discr_value           4
// < 5><0x00000087>            DW_TAG_member
//                               DW_AT_name                  ThisIsFineToo
//                               DW_AT_type                  <0x000000cc>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// ...
// ```
//
// By contrast, when compiled on 1.64.0, all variants have a
// `DW_AT_discr_value` as expected:
//
// ```
// ...
// < 2><0x00000049>      DW_TAG_structure_type
//                         DW_AT_name                  Foo
//                         DW_AT_byte_size             0x0000000c
//                         DW_AT_alignment             0x00000004
// < 3><0x00000050>        DW_TAG_variant_part
//                           DW_AT_discr                 <0x00000055>
// < 4><0x00000055>          DW_TAG_member
//                             DW_AT_type                  <0x00000181>
//                             DW_AT_alignment             0x00000001
//                             DW_AT_data_member_location  0
//                             DW_AT_artificial            yes(1)
// < 4><0x0000005c>          DW_TAG_variant
//                             DW_AT_discr_value           0
// < 5><0x0000005e>            DW_TAG_member
//                               DW_AT_name                  ThisIsFine
//                               DW_AT_type                  <0x00000095>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// < 4><0x0000006a>          DW_TAG_variant
//                             DW_AT_discr_value           1
// < 5><0x0000006c>            DW_TAG_member
//                               DW_AT_name                  ThisIsBad
//                               DW_AT_type                  <0x0000009c>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// < 4><0x00000078>          DW_TAG_variant
//                             DW_AT_discr_value           2
// < 5><0x0000007a>            DW_TAG_member
//                               DW_AT_name                  ThisIsAlsoFine
//                               DW_AT_type                  <0x000000ba>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// < 4><0x00000086>          DW_TAG_variant
//                             DW_AT_discr_value           3
// < 5><0x00000088>            DW_TAG_member
//                               DW_AT_name                  ThisIsFineToo
//                               DW_AT_type                  <0x000000cd>
//                               DW_AT_alignment             0x00000004
//                               DW_AT_data_member_location  0
// ...
// ```
// 
// Note that this is pretty squirrely; if the `#[repr(u32)]` is absent on the
// `DontKnowWhyThisIsNeededButItIs` enum (for example), the `ThisIsBad` member
// will correctly have a `DW_AT_discr_value` -- as it will if the
// `DontKnowWhyThisIsNeededButItIs` is absent entirely, or if the first tuple
// has fewer than 5 elements.  Finally (and perhaps most vexing?) it will also
// be chased away if there is a second variant that has the identical signature
// as `ThisIsBad` -- in which case all variants will correctly have a
// `DW_AT_discr_value`.
//
pub enum Foo {
    ThisIsFine,
    ThisIsBad(
        (u8, u8, u8, u8, u8),
        DontKnowWhyThisIsNeededButItIs,
    ),
    ThisIsAlsoFine(
        (u8, u8, u8, u8, u8),
    ),
    ThisIsFineToo(
        (u8, u8, u8, u8),
        DontKnowWhyThisIsNeededButItIs,
    ),
    ThisIsMaybeFine(
        (u8, u8, u8, u8),
        DontKnowWhyThisIsNeededButItIs,
    ),
}

#[allow(dead_code)]
static QUUX: Option<Foo> = None;

fn main() {}

Version it worked on

As the comment in the program indicates, the DWARF for the Foo enum looks like this on 1.64.0 and earlier:

< 2><0x00000049>      DW_TAG_structure_type
                        DW_AT_name                  Foo
                        DW_AT_byte_size             0x0000000c
                        DW_AT_alignment             0x00000004
< 3><0x00000050>        DW_TAG_variant_part
                          DW_AT_discr                 <0x00000055>
< 4><0x00000055>          DW_TAG_member
                            DW_AT_type                  <0x00000181>
                            DW_AT_alignment             0x00000001
                            DW_AT_data_member_location  0
                            DW_AT_artificial            yes(1)
< 4><0x0000005c>          DW_TAG_variant
                            DW_AT_discr_value           0
< 5><0x0000005e>            DW_TAG_member
                              DW_AT_name                  ThisIsFine
                              DW_AT_type                  <0x00000095>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0
< 4><0x0000006a>          DW_TAG_variant
                            DW_AT_discr_value           1
< 5><0x0000006c>            DW_TAG_member
                              DW_AT_name                  ThisIsBad
                              DW_AT_type                  <0x0000009c>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0
< 4><0x00000078>          DW_TAG_variant
                            DW_AT_discr_value           2
< 5><0x0000007a>            DW_TAG_member
                              DW_AT_name                  ThisIsAlsoFine
                              DW_AT_type                  <0x000000ba>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0
< 4><0x00000086>          DW_TAG_variant
                            DW_AT_discr_value           3
< 5><0x00000088>            DW_TAG_member
                              DW_AT_name                  ThisIsFineToo
                              DW_AT_type                  <0x000000cd>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0

rustc --version --verbose:

rustc 1.64.0 (a55dd71d5 2022-09-19)
binary: rustc
commit-hash: a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52
commit-date: 2022-09-19
host: x86_64-unknown-linux-gnu
release: 1.64.0
LLVM version: 14.0.6

Version with regression

But on 1.65.0 and later, it looks like this:

< 2><0x00000049>      DW_TAG_structure_type
                        DW_AT_name                  Foo
                        DW_AT_byte_size             0x0000000c
                        DW_AT_alignment             0x00000004
< 3><0x00000050>        DW_TAG_variant_part
                          DW_AT_discr                 <0x00000055>
< 4><0x00000055>          DW_TAG_member
                            DW_AT_type                  <0x00000180>
                            DW_AT_alignment             0x00000004
                            DW_AT_data_member_location  0
                            DW_AT_artificial            yes(1)
< 4><0x0000005c>          DW_TAG_variant
                            DW_AT_discr_value           1
< 5><0x0000005e>            DW_TAG_member
                              DW_AT_name                  ThisIsFine
                              DW_AT_type                  <0x00000094>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0
< 4><0x0000006a>          DW_TAG_variant
< 5><0x0000006b>            DW_TAG_member
                              DW_AT_name                  ThisIsBad
                              DW_AT_type                  <0x0000009b>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0
< 4><0x00000077>          DW_TAG_variant
                            DW_AT_discr_value           3
< 5><0x00000079>            DW_TAG_member
                              DW_AT_name                  ThisIsAlsoFine
                              DW_AT_type                  <0x000000b9>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0
< 4><0x00000085>          DW_TAG_variant
                            DW_AT_discr_value           4
< 5><0x00000087>            DW_TAG_member
                              DW_AT_name                  ThisIsFineToo
                              DW_AT_type                  <0x000000cc>
                              DW_AT_alignment             0x00000004
                              DW_AT_data_member_location  0

rustc --version --verbose:

rustc 1.65.0 (897e37553 2022-11-02)
binary: rustc
commit-hash: 897e37553bba8b42751c67658967889d11ecd120
commit-date: 2022-11-02
host: x86_64-unknown-linux-gnu
release: 1.65.0
LLVM version: 15.0.0

Unsurprisingly, there is an LLVM version bump across these two versions.

If it helps to have context about how we hit this, we saw this in the debugger (Humility) for our embedded Rust operating system (Hubris); see oxidecomputer/humility#263 for details.

Thanks in advance to anyone who looks at this -- the DWARF emitted by Rust has been invaluable for our work!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.P-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions