Skip to content

Tracking issue for RFC 2363, "Allow arbitrary enums to have explicit discriminants" #60553

Closed

Description

@Centril

This is a tracking issue for the RFC "Allow arbitrary enums to have explicit discriminants" (rust-lang/rfcs#2363).

Steps:

Unresolved questions:

  1. Should discriminants of enumerations with fields be specified as variant attributes?
  2. Should this apply only to enumerations with an explicit representation?

Activity

  1. added
    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
    T-langRelevant to the language team
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on May 5, 2019
  2. eddyb commented on May 5, 2019

    @eddyb
    Contributor

    Mentoring-wise: I'd suggest trying out an example and then tracking down where the error comes from. I don't remember, it's either during parsing or in ast_validation.

    Most of the compiler should already support this, where doing the uniform thing is easier.
    The "niche" optimization might assume variants with any data have consecutive discriminants, but it should be easy to fix, and we can figure it out after an initial PR is open.

  3. jswrenn commented on May 9, 2019

    @jswrenn
    Member

    Has anyone stepped up to implement this? If not, I'd like to take a stab.

    The current error arises in parsing. I haven't fiddled with it yet, but I suspect a good first step is to remove the else before this if:

    } else if self.eat(&token::Eq) {
    disr_expr = Some(AnonConst {
    id: ast::DUMMY_NODE_ID,
    value: self.parse_expr()?,
    });
    if let Some(sp) = disr_expr.as_ref().map(|c| c.value.span) {
    any_disr.push(sp);
    }
    struct_def = VariantData::Unit(ast::DUMMY_NODE_ID);

  4. oli-obk commented on May 9, 2019

    @oli-obk
    Contributor

    Has anyone stepped up to implement this?

    I don't know of anyone

    If not, I'd like to take a stab.

    Go for it!

    I haven't fiddled with it yet, but I suspect a good first step is to remove the else before this if:

    That looks like the right start, yes.

  5. jswrenn commented on May 9, 2019

    @jswrenn
    Member

    I've started work on this here by removing the parsing restriction preventing non-unit variants from having explicit discriminants. As you predicted @eddyb, things seem to basically just work; e.g.:

    #![feature(core_intrinsics)]
    
    enum Enum {
      A(u16) = 7,
      B(u32) = 42,
    }
    
    fn main() {
      use std::intrinsics::discriminant_value;
    
      unsafe {
        assert_eq!(discriminant_value(&Enum::A(1)), 7);
        assert_eq!(discriminant_value(&Enum::B(2)), 42);
      }
    }

    As it stands, adding this feature required removing more lines of code than it involved writing new code! :D

    Future work that comes to mind include writing tests and putting this behind a feature flag. Shall I go ahead and file an initial PR?

  6. oli-obk commented on May 10, 2019

    @oli-obk
    Contributor

    Sweet! Yes please open a PR with the feature gate and throw some tests in for good measure (there may already be tests checking for the "discriminator values can only be used with a field-less enum" error message, those should now be emitting a feature gate error instead)

  7. added a commit that references this issue on Jun 25, 2019
  8. Centril commented on Jun 25, 2019

    @Centril
    ContributorAuthor

    The #![feature(arbitrary_enum_discriminant)] was implemented on 2019-06-26 in #60732 which was written by @jswrenn and reviewed by @pnkfelix and others.

  9. added
    B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.
    B-unstableBlocker: Implemented in the nightly compiler and unstable.
    on Jun 25, 2019
  10. jswrenn commented on Jul 14, 2019

    @jswrenn
    Member

    Documentation

    I've submitted a PR to update the reference to reflect this feature (in its current state): rust-lang/reference#639.

    I do not believe that either the Book or Rust By Example need updating:

    1. The Book does not mention discriminants at all.
    2. Rust By Example mentions C-like enums, but does not go into their memory layout. Since this feature is only relevant to advance users who care about the memory layout of enums, Rust By Example is probably ill-suited for the topic.

    Unresolved Questions

    1. Should discriminants of enumerations with fields be specified as variant attributes?

    Presently: no. Discriminants of all types of enumerations are specified with = followed by a constant expression. I implemented this approach because doing so was simplest, but I'd also argue this is the right approach since it means the syntax for specifying discriminants is consistent across variant types.

    1. Should this apply only to enumerations with an explicit representation?

    Presently: yes, this feature applies only to enumerations using a primitive representation. Since this feature is only relevant when you care about the precise memory layout of enums, this feature is only reasonable to use when the precise layout of an enum is well-specified.

  11. newpavlov commented on Aug 18, 2019

    @newpavlov
    Contributor

    What are layout guarantees for such enums? Ideally I would like to be able to write code likes this:

    #[repr(u8, C)]
    enum Foo {
        A = 0,
        B { a: u16 } = 1,
        C { a: u32, b: u16 } = 2,
    }

    And I want for this type to have exactly the same memory layout as:

    #[repr(C)]
    struct foo_t {
        pub discr: u8,
        pub payload: foo_u,
    }
    
    union foo_u {
        b: u16,
        c: c_t,
    }
    
    #[repr(C)]
    struct c_t {
        a: u32,
        b: u16,
    }

    Meaning if repr(C) is used:

    • Discriminant should always be stored in the beginning.
    • Discriminant can not be used for niche-optimizations.
    • Variant fields should follow repr(C).

    Having such guarantees would allow in some cases some neat zero-cost translations of C types to safe Rust ones.

  12. eddyb commented on Aug 18, 2019

    @eddyb
    Contributor

    @newpavlov You get most of that (the placement and size of the discriminant) from just #[repr(u8)], I'm not sure if #[repr(C)] adds more to that.

    Keep in mind that since your discriminants are the default ones (starting at 0 and adding 1 for each variant), you don't need this feature!

    We defined all these back in rust-lang/rfcs#2195, for Servo <-> Firefox I believe.

  13. 35 remaining items

  14. joshtriplett commented on Jun 15, 2022

    @joshtriplett
    Member

    There are a set of changes related to enums that we need to make, including discriminants, as casts, AsRepr, and similar. Lang has had a design meeting on that, and needs to have another once someone supplies a complete design that fits these pieces together.

  15. jswrenn commented on Jun 15, 2022

    @jswrenn
    Member

    Would anyone like to collaborate with me on this design? I'm very familiar with these issues, and have some design sketches on hand, but would love a collaborator help construct a cohesive vision of the future.

  16. added 3 commits that reference this issue on Oct 24, 2022
  17. added a commit that references this issue on Oct 25, 2022
  18. added a commit that references this issue on Oct 26, 2022
  19. ehuss commented on Jan 8, 2023

    @ehuss
    Contributor

    I'm going to close as I believe this is now essentially complete as of 1.66.

    There might be some longer-term decisions to make around as casting. There are some strange exceptions:

    • Fieldless enums with variants such as Tuple() or Struct{} allow as casting without a #[repr(…)], and have since 1.15.
    // Allowed since 1.15, even without a #[repr]
    enum Fieldless {
        Tuple(),
        Struct{},
        Unit,
    }
    assert_eq!(0, Fieldless::Tuple() as isize);
    assert_eq!(1, Fieldless::Struct{} as isize);
    assert_eq!(2, Fieldless::Unit as isize);
    • Similar to above, fieldless enums allow as casting as long as you don't specify an explicit discriminant. Using an explicit discriminant suddenly prevents all as casts.

    • Fieldless enums allow explicit discriminants on unit variants, but not struct or tuple (new 1.66). It's not clear to me why it was decided to allow this.

    #[repr(u8)]
    enum FieldlessWithDiscrimants {
        First = 10,
        Tuple(),
        Second = 20,
        Struct{},
        Unit,
    }
    assert_eq!(10, FieldlessWithDiscrimants::First as u8);
    assert_eq!(11, FieldlessWithDiscrimants::Tuple() as u8);
    assert_eq!(20, FieldlessWithDiscrimants::Second as u8);
    assert_eq!(21, FieldlessWithDiscrimants::Struct{} as u8);
    assert_eq!(22, FieldlessWithDiscrimants::Unit as u8);

    I suspect further changes may require an RFC or some design decisions with the lang team.

  20. added 4 commits that reference this issue on Aug 8, 2024
  21. added a commit that references this issue on Aug 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-design-concernsStatus: There are blocking design concerns.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions