Skip to content

Specify that "option-like" enums must be #[repr(Rust)] to be ABI-compatible with their non-1ZST field. #141947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 13, 2025

Conversation

zachs18
Copy link
Contributor

@zachs18 zachs18 commented Jun 3, 2025

Add that the enum must be #[repr(Rust)] and not #[repr(packed)] or #[repr(align)] in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: #130628 (comment) but repr was not mentioned. In practice, only #[repr(Rust)] (or no repr attribute, which is equivalent) works for this, so add that to the docs.


Restrict to #[repr(Rust)] only, since:

  • #[repr(C)] and the primitive representations (#[repr(u8)] etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
  • #[repr(transparent)] enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for #[repr(align)]: my current wording that it is completely disallowed may be too strong: it seems like #[repr(align(<= alignment of T))] enum Foo { X, Y(T) } currently does still have the same ABI as T in practice, though this may not be something we want to promise. (#[repr(align(> alignment of T))] definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about packed to match align, but #[repr(packed)] currently can't be applied to enums at all anyway, so might be unnecessary.


I think this needs T-lang approval?

cc @workingjubilee

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.
@rustbot
Copy link
Collaborator

rustbot commented Jun 3, 2025

r? @thomcc

rustbot has assigned @thomcc.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 3, 2025
@workingjubilee
Copy link
Member

workingjubilee commented Jun 3, 2025

Wording about repr(align(N)) should probably be shaped in the form of advising people about the i64, u64, and f64 gotcha: on some platforms it is 8-aligned, on some platforms one or all are 4-aligned, and if you use repr(align(8)) because the type is 8 bytes, then you just lost the guarantee on those platforms. Likewise for all the primitive types, though even-lower alignments often aren't a concern here.

@workingjubilee
Copy link
Member

I agree though that this needs review from

r? lang

@rustbot rustbot added the T-lang Relevant to the language team label Jun 3, 2025
@rustbot rustbot assigned traviscross and unassigned thomcc Jun 3, 2025
Comment on lines +1836 to +1837
/// - The enum `E` uses the [`Rust` representation], and is not modified by the `align` or
/// `packed` representation modifiers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing alignment of something can change its ABI, and both of these repr attributes, as modifiers, can do that. So I am okay with having the guarantee be conditional on any usage of the repr attributes. If T-lang wants us to guarantee that the attributes only invalidate the nullptr opt if they actually would result in a change in the alignment of the type, that may be doable, but it would require auditing the code involved and landing a number of tests, so I don't know that it should happen in this PR. Also, see my next comment.

@@ -1833,6 +1833,8 @@ mod prim_ref {}
/// - If `T` is guaranteed to be subject to the [null pointer
/// optimization](option/index.html#representation), and `E` is an enum satisfying the following
/// requirements, then `T` and `E` are ABI-compatible. Such an enum `E` is called "option-like".
/// - The enum `E` uses the [`Rust` representation], and is not modified by the `align` or
/// `packed` representation modifiers.
/// - The enum `E` has exactly two variants.
/// - One variant has exactly one field, of type `T`.
/// - All fields of the other variant are zero-sized with 1-byte alignment.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing more closely, I feel guaranteeing e.g. repr(align(2)) won't change the layout if T is 2-aligned would be inconsistent with this clause.

We could potentially raise this alignment to whatever the alignment of T is, for example. I don't want to alter the guarantee around the 1ZST case, however, as it places a number of annoying requirements on the implementation. Being able to special-case specifically on 1ZSTs simplifies a lot of things. I would be okay with guaranteeing that repr(align(1)) doesn't change anything, though, because it doesn't. There's also some inclarity about certain special cases around ZSTs already when they combine with other repr attributes like repr(C).

Copy link
Member

@workingjubilee workingjubilee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To summarize my thoughts, this is fine with me as-is, speaking re: my (relative) familiarity with the layout code's bits and bobs.

Copy link
Contributor

@traviscross traviscross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rustbot labels +I-lang-nominated +P-lang-drag-1 +S-waiting-on-team

The diff looks right to me, and I agree with @workingjubilee's comments as well, but it would indeed be better for the team to have a look here first, so let's not merge this yet. This is the sort of thing where we all need to have a look at it, if nothing else, just to ensure we all keep context on what exactly we've said the rule is.

@rustbot rustbot added I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). labels Jun 10, 2025
@workingjubilee workingjubilee changed the title Specify that "option-like" enums must be #[repr(Rust)] to be ABI-compatibile with their non-1ZST field. Specify that "option-like" enums must be #[repr(Rust)] to be ABI-compatible with their non-1ZST field. Jun 10, 2025
@Amanieu Amanieu removed the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Jun 11, 2025
@workingjubilee
Copy link
Member

@traviscross Based on the triage meeting notes, lang had a fair vibe about this. Was there an intention to r+ this as a simple amendment/clarification to the prior decision, FCP it, or is there a further discussion that I maybe should show up for?

I think if lang is happy with this except with the repr(align(N)) where N > 1 question, and it doesn't need an FCP, then we should ship this as-is and look at strengthening the guarantee in a followup. If it does need an FCP then we might as well do the haggling now.

@traviscross
Copy link
Contributor

Thanks, yes, I mentioned in the call I was going to...

@bors r=workingjubilee,traviscross rollup

@bors
Copy link
Collaborator

bors commented Jun 12, 2025

📌 Commit c0851d7 has been approved by workingjubilee,traviscross

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). labels Jun 12, 2025
@traviscross traviscross removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels Jun 12, 2025
@workingjubilee workingjubilee added the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Jun 12, 2025
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Jun 12, 2025
…traviscross

Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: rust-lang#130628 (comment) but `repr` was not mentioned. In practice, only `#[repr(Rust)]` (or no `repr` attribute, which is equivalent) works for this, so add that to the docs.

-----

Restrict to `#[repr(Rust)]` only, since:
* `#[repr(C)]` and the primitive representations (`#[repr(u8)]` etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
* `#[repr(transparent)]` enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for `#[repr(align)]`: my current wording that it is completely disallowed may be too strong: it seems like `#[repr(align(<= alignment of T))] enum Foo { X, Y(T) }` currently does still have the same ABI as `T` in practice, though this may not be something we want to promise. (`#[repr(align(> alignment of T))]` definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about `packed` to match `align`, but `#[repr(packed)]` currently can't be applied to `enum`s at all anyway, so might be unnecessary.

-----

I think this needs T-lang approval?

cc `@workingjubilee`
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Jun 12, 2025
…traviscross

Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: rust-lang#130628 (comment) but `repr` was not mentioned. In practice, only `#[repr(Rust)]` (or no `repr` attribute, which is equivalent) works for this, so add that to the docs.

-----

Restrict to `#[repr(Rust)]` only, since:
* `#[repr(C)]` and the primitive representations (`#[repr(u8)]` etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
* `#[repr(transparent)]` enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for `#[repr(align)]`: my current wording that it is completely disallowed may be too strong: it seems like `#[repr(align(<= alignment of T))] enum Foo { X, Y(T) }` currently does still have the same ABI as `T` in practice, though this may not be something we want to promise. (`#[repr(align(> alignment of T))]` definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about `packed` to match `align`, but `#[repr(packed)]` currently can't be applied to `enum`s at all anyway, so might be unnecessary.

-----

I think this needs T-lang approval?

cc ``@workingjubilee``
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jun 12, 2025
…traviscross

Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: rust-lang#130628 (comment) but `repr` was not mentioned. In practice, only `#[repr(Rust)]` (or no `repr` attribute, which is equivalent) works for this, so add that to the docs.

-----

Restrict to `#[repr(Rust)]` only, since:
* `#[repr(C)]` and the primitive representations (`#[repr(u8)]` etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
* `#[repr(transparent)]` enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for `#[repr(align)]`: my current wording that it is completely disallowed may be too strong: it seems like `#[repr(align(<= alignment of T))] enum Foo { X, Y(T) }` currently does still have the same ABI as `T` in practice, though this may not be something we want to promise. (`#[repr(align(> alignment of T))]` definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about `packed` to match `align`, but `#[repr(packed)]` currently can't be applied to `enum`s at all anyway, so might be unnecessary.

-----

I think this needs T-lang approval?

cc ```@workingjubilee```
bors added a commit that referenced this pull request Jun 12, 2025
Rollup of 9 pull requests

Successful merges:

 - #138016 (Added `Clone` implementation for `ChunkBy`)
 - #140770 (add `extern "custom"` functions)
 - #141162 (refactor  `AttributeGate` and `rustc_attr!` to emit notes during feature checking)
 - #141474 (Add `ParseMode::Diagnostic` and fix multiline spans in diagnostic attribute lints)
 - #141947 (Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.)
 - #142135 (docs: autogenerate compiler flag stubs based on -Zhelp)
 - #142252 (Improve clarity of `core::sync::atomic` docs about "Considerations" in regards to CAS operations)
 - #142337 (miri: add flag to suppress float non-determinism)
 - #142353 (compiler: Ease off the accelerator on `unsupported_calling_conventions`)

r? `@ghost`
`@rustbot` modify labels: rollup
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jun 12, 2025
…traviscross

Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: rust-lang#130628 (comment) but `repr` was not mentioned. In practice, only `#[repr(Rust)]` (or no `repr` attribute, which is equivalent) works for this, so add that to the docs.

-----

Restrict to `#[repr(Rust)]` only, since:
* `#[repr(C)]` and the primitive representations (`#[repr(u8)]` etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
* `#[repr(transparent)]` enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for `#[repr(align)]`: my current wording that it is completely disallowed may be too strong: it seems like `#[repr(align(<= alignment of T))] enum Foo { X, Y(T) }` currently does still have the same ABI as `T` in practice, though this may not be something we want to promise. (`#[repr(align(> alignment of T))]` definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about `packed` to match `align`, but `#[repr(packed)]` currently can't be applied to `enum`s at all anyway, so might be unnecessary.

-----

I think this needs T-lang approval?

cc ````@workingjubilee````
bors added a commit that referenced this pull request Jun 12, 2025
Rollup of 8 pull requests

Successful merges:

 - #138016 (Added `Clone` implementation for `ChunkBy`)
 - #140770 (add `extern "custom"` functions)
 - #141162 (refactor  `AttributeGate` and `rustc_attr!` to emit notes during feature checking)
 - #141474 (Add `ParseMode::Diagnostic` and fix multiline spans in diagnostic attribute lints)
 - #141947 (Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.)
 - #142252 (Improve clarity of `core::sync::atomic` docs about "Considerations" in regards to CAS operations)
 - #142337 (miri: add flag to suppress float non-determinism)
 - #142353 (compiler: Ease off the accelerator on `unsupported_calling_conventions`)

r? `@ghost`
`@rustbot` modify labels: rollup
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jun 12, 2025
…traviscross

Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: rust-lang#130628 (comment) but `repr` was not mentioned. In practice, only `#[repr(Rust)]` (or no `repr` attribute, which is equivalent) works for this, so add that to the docs.

-----

Restrict to `#[repr(Rust)]` only, since:
* `#[repr(C)]` and the primitive representations (`#[repr(u8)]` etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
* `#[repr(transparent)]` enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for `#[repr(align)]`: my current wording that it is completely disallowed may be too strong: it seems like `#[repr(align(<= alignment of T))] enum Foo { X, Y(T) }` currently does still have the same ABI as `T` in practice, though this may not be something we want to promise. (`#[repr(align(> alignment of T))]` definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about `packed` to match `align`, but `#[repr(packed)]` currently can't be applied to `enum`s at all anyway, so might be unnecessary.

-----

I think this needs T-lang approval?

cc `````@workingjubilee`````
bors added a commit that referenced this pull request Jun 12, 2025
Rollup of 7 pull requests

Successful merges:

 - #138016 (Added `Clone` implementation for `ChunkBy`)
 - #141162 (refactor  `AttributeGate` and `rustc_attr!` to emit notes during feature checking)
 - #141474 (Add `ParseMode::Diagnostic` and fix multiline spans in diagnostic attribute lints)
 - #141947 (Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.)
 - #142252 (Improve clarity of `core::sync::atomic` docs about "Considerations" in regards to CAS operations)
 - #142337 (miri: add flag to suppress float non-determinism)
 - #142353 (compiler: Ease off the accelerator on `unsupported_calling_conventions`)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit that referenced this pull request Jun 13, 2025
Rollup of 6 pull requests

Successful merges:

 - #138016 (Added `Clone` implementation for `ChunkBy`)
 - #141162 (refactor  `AttributeGate` and `rustc_attr!` to emit notes during feature checking)
 - #141474 (Add `ParseMode::Diagnostic` and fix multiline spans in diagnostic attribute lints)
 - #141947 (Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.)
 - #142252 (Improve clarity of `core::sync::atomic` docs about "Considerations" in regards to CAS operations)
 - #142337 (miri: add flag to suppress float non-determinism)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 0c81fd0 into rust-lang:master Jun 13, 2025
10 checks passed
@rustbot rustbot added this to the 1.89.0 milestone Jun 13, 2025
rust-timer added a commit that referenced this pull request Jun 13, 2025
Rollup merge of #141947 - zachs18:patch-4, r=workingjubilee,traviscross

Specify that "option-like" enums must be `#[repr(Rust)]` to be ABI-compatible with their non-1ZST field.

Add that the enum must be `#[repr(Rust)]` and not `#[repr(packed)]` or `#[repr(align)]` in order to be ABI-compatible with its null-pointer-optimized field.

The specific rules here were decided on here: #130628 (comment) but `repr` was not mentioned. In practice, only `#[repr(Rust)]` (or no `repr` attribute, which is equivalent) works for this, so add that to the docs.

-----

Restrict to `#[repr(Rust)]` only, since:
* `#[repr(C)]` and the primitive representations (`#[repr(u8)]` etc) definitely disqualify the enum from NPO, since they have defined layouts that store the tag separately to the payload.
* `#[repr(transparent)]` enums are covered two bullet points above this (line 1830), and cannot have multiple variants, so would fail the "The enum has exactly two variants" requirement anyway.

As for `#[repr(align)]`: my current wording that it is completely disallowed may be too strong: it seems like `#[repr(align(<= alignment of T))] enum Foo { X, Y(T) }` currently does still have the same ABI as `T` in practice, though this may not be something we want to promise. (`#[repr(align(> alignment of T))]` definitely disqualifies the enum from being ABI-compatible with T currently).

I added the note about `packed` to match `align`, but `#[repr(packed)]` currently can't be applied to `enum`s at all anyway, so might be unnecessary.

-----

I think this needs T-lang approval?

cc ``````@workingjubilee``````
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-lang Relevant to the language team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants