Skip to content

Tracking issue for future-incompatibility lint conflicting_repr_hints #68585

Open

Description

@Centril

This is the summary issue for a bug fix related to repr(...) hints. The goal of this page is describe why this change was made and how you can fix code that is affected by it. It also provides a place to ask questions or register a complaint if you feel the change should not be made. For more information on the policy around future-compatibility warnings, see our breaking change policy guidelines.

What is the warning for?

The compiler incorrectly accepts #[repr(...)] hints on data types where the hints are incompatible and have conflicts. Examples of this includes:

// from `src/test/ui/conflicting-repr-hints.rs

#[repr(u32, u64)] //~ ERROR conflicting representation hints
enum D {
    D,
}

We are presently issuing a deny-by-default lint when conflicting representation hints are used together but we expect to transition the lint to hard errors in the future.

How can you fix your code?

Generally speaking, the conflict should be fixed by more clearly expressing your intent so that the conflict is resolved.

Activity

  1. added
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    on Jan 27, 2020
  2. added a commit that references this issue on Jan 29, 2020
  3. Manishearth commented on Jan 31, 2020

    @Manishearth
    Member

    As discussed a bit in #68428 perhaps we should remove the warning and add semantics for #[repr(C, Int)] (namely, "fold into repr(C)").

    C++ supports enum class Foo: uint16_t, and we heavily recommend you only pass repr(C) types across FFI boundaries, so it makes sense that the correct thing to write for this is #[repr(C, u16)]. Furthermore, for dataful enums, #[repr(C, u16)] actually does work, and it's weird for this not to be consistent.

  4. Centril commented on Feb 24, 2020

    @Centril
    ContributorAuthor

    Beta crater results in #68586 (comment):

    error[E0566]: conflicting representation hints

    Number of crates regressed: 2

    * [`enum-utils v0.1.2`](https://crater-reports.s3.amazonaws.com/beta-1.42-1/beta-2020-02-05/reg/enum-utils-0.1.2/log.txt)
    
    * [`ovr-mobile-sys v0.4.0`](https://crater-reports.s3.amazonaws.com/beta-1.42-rustdoc-1/beta-2020-02-05/reg/ovr-mobile-sys-0.4.0/log.txt)
    
    • enum-utils has 0 rev deps, (btw, cc @ecstatic-morse)
    • ovr-mobile-sys was the servo dependency and it seems to have been fixed already.

    Based on these very limited results I don't think it's justified to retcon this into the specification, and making it an error again in the near future should be possible.

  5. ecstatic-morse commented on Feb 24, 2020

    @ecstatic-morse
    Contributor

    enum-utils has a test that ensures #[repr(C, u16)] works. You shouldn't consider it when determining fallout.

  6. marmeladema commented on Feb 18, 2021

    @marmeladema
    Contributor

    Just encountered this error.
    I wonder how does it relate to

    Will that make both those RFC obsolete?
    Also, will it be turned into a hard error in edition 2021?

  7. added a commit that references this issue on Aug 21, 2024
  8. added a commit that references this issue on Aug 21, 2024
  9. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    A-attributesArea: Attributes (`#[…]`, `#![…]`)
    on Sep 14, 2024
  10. changed the title [-]Tracking issue for conflicting `repr(...)` hints future compatibility[/-] [+]Tracking issue for future-incompatibility lint `conflicting_repr_hints`[/+] on Sep 14, 2024
  11. added
    A-reprArea: the `#[repr(stuff)]` attribute
    T-langRelevant to the language team
    on Mar 23, 2025
  12. Jeremiah-Griffin commented on May 3, 2025

    @Jeremiah-Griffin

    This lint presently errors if multiple of the same repr is applied to the same item as seen here:

    #[repr(u16)]
    #[repr(u16)]
    enum Numbers{
        Zero = 0,
        One = 1,
    }

    https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=01dc845a8cec1b1e7f8d90445ad89b80

    Supporting multiple identical reprs is useful when annotating the same item with multiple proc macros which themselves annotate a repr for the item. I feel this restriction should thus be loosened.

  13. NUnitTester commented on Jul 6, 2025

    @NUnitTester

    Currently, I get error that conflicting representations is not allowed, but it should not conflicting. Because I can also define the width representation of a enum (at least in C++).

    #[repr(C)]
    #[repr(u32)]
  14. Skgland commented on Jul 11, 2025

    @Skgland
    Contributor

    #[repr(C, u64)]
    pub enum TestEnum {
    First { ... },
    Second,
    Third,
    TenMillionth,
    }

    This is allowed when the enum has at least one non-unit variant (even if the only non-unit variant doesn't have any fields).
    Your example (after removing or filling in ...) compiles play-ground

    Edit:

    Note: RFC-2195

    • for C-like enums repr({integer}) is allowed, but repr(C, {integer}) is not
    • for non C-like enums repr({integer}) and repr(C, {integer}) are allowed and behave differently
      • union of tag-prefixed structs vs. struct of a tag and a union of variant-data
  15. eliminmax commented on Dec 4, 2025

    @eliminmax

    As of C23, C also allows specifying the backing type of its enums, with similar syntax to C++, so #[repr(C, u8)] would seem to be the most logical annotation for a Rust version of a C enum like the following:

    #include <stdint.h>
    typedef enum OctetEnum: uint8_t {
        A = 3,
        B = 21,
        C = 50,
    } OctetEnum;
  16. kupiakos commented on May 13, 2026

    @kupiakos
    Contributor

    My proposal is that a field-less repr(u16) enum be defined as ABI-compatible with a C/C++ enum using uint16_t as its fixed backing integer - no C required. This would mean that repr(C, Int) on a field-less enum would remain invalid (and not useful to spell).

    Furthermore, for dataful enums, #[repr(C, u16)] actually does work, and it's weird for this not to be consistent.

    The issue I have with using repr(C, u16) to indicate compatibility with enum Foo : uint16_t is because C on an enum with fields functions to change its layout and not just the ABI of the discriminant. repr(u16) is laid out as a union-of-structs, where the first field of each union is a discriminant enum, while repr(C, u16) is a struct with a discriminant enum field and a union data field.

    RFC 2195 states that this Rust enum:

    #[repr(Int)]
    enum MyEnum {
        A(u32),
        B(f32, u64),
        C { x: u32, y: u8 },
        D,
    }

    can be correctly manipulated in C++ with this definition:

    #include <stdint.h>
    
    enum class MyEnumTag: CppEquivalentOfInt { A, B, C, D };
    struct MyEnumPayloadA { MyEnumTag tag; uint32_t payload; };
    struct MyEnumPayloadB { MyEnumTag tag; float _0; uint64_t _1;  };
    struct MyEnumPayloadC { MyEnumTag tag; uint32_t x; uint8_t y; };
    struct MyEnumPayloadD { MyEnumTag tag; };
    
    union MyEnum {
        MyEnumVariantA A;
        MyEnumVariantB B;
        MyEnumVariantC C;
        MyEnumVariantD D;
    };

    Because of this, Rust already supports interoperating with C/C++ enums using a fixed backing Int without spelling repr(C): but only when it's the discriminant field for an enum with fields. It would be consistent to extend this to field-less enums as well by defining ABI compatibility between field-less #[repr(Int)] enum foo and enum foo: CEquivalentOfInt/enum class foo : CppEquivalentOfInt without requiring repr(C).

    I struggle to see serious downsides to making this promise: is there any optimization or sanitization that this would preclude?

    If CFI becomes a higher priority to support, then I think this promise would be necessary to support existing codebases that interoperate with enums using a fixed backing integer.

  17. eliminmax commented on Jun 22, 2026

    @eliminmax

    I would argue that for fieldless enums, #[repr(C, Int)] and #[repr(Int)] should be equivalent, for the sake of the principle of least astonishment. I know when I was trying to define a fieldless enum for use in a project that used a mix of Rust and C23, and the compiler flagged it. I can't imagine any problems that would occur from treating them as equivalent, but I could very well be missing something.

    That said, It's a bit off-topic, but I noticed a significant problem with the C++ code quoted from RFC 2194 in the previous comment. I'm hiding it in a <details> block to avoid cluttering this issue. If anyone involved in Rust development could direct me to a more appropriate place to discuss the issue, please let me know.

    Amateur language lawyering ahead
    RFC 2195 states that this Rust enum: ...

    can be correctly manipulated in C++ with this definition:

    #include <stdint.h>
    
    enum class MyEnumTag: CppEquivalentOfInt { A, B, C, D };
    struct MyEnumPayloadA { MyEnumTag tag; uint32_t payload; };
    struct MyEnumPayloadB { MyEnumTag tag; float _0; uint64_t _1;  };
    struct MyEnumPayloadC { MyEnumTag tag; uint32_t x; uint8_t y; };
    struct MyEnumPayloadD { MyEnumTag tag; };
    union MyEnum {
        MyEnumVariantA A;
        MyEnumVariantB B;
        MyEnumVariantC C;
        MyEnumVariantD D;
    };
    

    I am coming from an hobbyist C and Rust background, not C++, but as I understand, that is invalid in both C and C++, for different reasons.

    For C, it's trivial: the enum class declaration is not valid, as it has no concept of classes. The RFC text suggests just using the backing integer type, but, as of C23, simply omitting the word class should make this valid in C, though adoption of new C versions is quite slow.

    As I understand it, C++ only allows the most recently-set union variant to be accessed, so even though the tag is at the same offset regardless of variant, it is undefined behavior to read it unless, by some miracle of statistics, it always happens to go through the correct variant.

    Because splitting the tag and the payload into separate members of a struct could cause alignment issues, I don't believe there's any way to rewrite this to be valid, standards-compliant C++ for all possible #[repr(C)] enums.

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

    A-attributesArea: Attributes (`#[…]`, `#![…]`)A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.A-reprArea: the `#[repr(stuff)]` attributeC-future-incompatibilityCategory: Future-incompatibility lintsC-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions