Skip to content

repr(C) is not respected for enums with uninhabited variants #68065

@jswrenn

Description

@jswrenn

Uninhabited repr(C) enums are size zero, not the size of their discriminant. For instance:

enum Never {}

#[repr(C, u64)]
enum E {
    _X(Never),
    _Y(Never),
}

fn main (){
    use core::mem::size_of;

    let never_size = size_of::<Never>();
    assert_eq!(0, never_size);
    
    let disr_size = size_of::<u64>();
    assert_eq!(8, disr_size);
    
    let expected_enum_size = disr_size + never_size;
    let actual_enum_size = size_of::<E>();
    
    assert_eq!(expected_enum_size, actual_enum_size); // FAIL! 8 != 0
}

At best, this leads to nonsensical errors. For instance, this:

enum Never {}

#[repr(C, u64)]
enum E {
    _X(Never),
    _Y(E),
}

produces this error message:

error[E0072]: recursive type `E` has infinite size
 --> src/lib.rs:4:1
  |
4 | enum E {
  | ^^^^^^ recursive type has infinite size
5 |     _X(Never),
6 |     _Y(E),
  |        - recursive without indirection
  |
  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `E` representable

At worst, it violates the specification of RFC2195: Really Tagged Unions, which dictates that E should be layout-compatible to the union of its variants:

use core::mem::size_of;

#[derive(Copy, Clone)]
enum Never {}
// This:

#[repr(C, u64)]
enum E {
    _X(Never),
    _Y(Never),
}

// ...should have the same layout as `ERepr`:

#[derive(Copy, Clone)]
#[repr(C)]
union ERepr {
    A: EX,
    B: EY,
}

type ETag = u64;

#[derive(Copy, Clone)] struct EX(ETag, Never);
#[derive(Copy, Clone)] struct EY(ETag, Never);

// ...but it doesn't:

fn main() {
    assert_eq!(size_of::<E>(), size_of::<ERepr>()); // FAIL: 0 != 8
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-reprArea: the `#[repr(stuff)]` attributeC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions