Skip to content
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

Explain how to get the discriminant out of a #[repr(T)] enum with payload #104892

Merged
merged 2 commits into from
Nov 27, 2022

Conversation

lukas-code
Copy link
Member

example stolen from rust-lang/reference#1055

@rustbot label A-docs

@rustbot
Copy link
Collaborator

rustbot commented Nov 25, 2022

r? @scottmcm

(rustbot has picked a reviewer for you, use r? to override)

@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 Nov 25, 2022
@rustbot
Copy link
Collaborator

rustbot commented Nov 25, 2022

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@rustbot rustbot added the A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools label Nov 25, 2022
@lukas-code lukas-code changed the title Explain how to get the discriminant out of a #[repr(T)] enum Explain how to get the discriminant out of a #[repr(T)] enum with payload Nov 25, 2022
Copy link
Member

@scottmcm scottmcm left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Overall it looks good, though I have a few hopefully-simple requests.

@rustbot author

///
/// impl Enum {
/// fn discriminant(&self) -> u8 {
/// unsafe { *(self as *const Self as *const u8) }
Copy link
Member

Choose a reason for hiding this comment

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

We're trying to avoid as on pointers now, where possible, in favour of methods. So please change this to something using cast & friends, maybe

Suggested change
/// unsafe { *(self as *const Self as *const u8) }
/// unsafe { <*const _>::from(self).cast::<u8>().read() }

Or you could consider something like

    pub fn discriminant(&self) -> &u8 {
        unsafe { NonNull::from(self).cast::<u8>().as_ref() }
    }

Copy link
Member

Choose a reason for hiding this comment

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

Oh, and it would be good to have a comment on the unsafe block repeating why it's sound. Maybe

// SAFETY: because `Self` is marked `repr(u8)`,
// its layout is a `repr(C)` `union` between `repr(C)` structs,
// each of which has the `u8` discriminant as its first field,
// so we can read the discriminant without offsetting the pointer.

Copy link
Member Author

Choose a reason for hiding this comment

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

TIL that <*const _>::from(reference) works. Since there is no impl From<&T> for *const T, it probably selects From<*const T> for *const T and then coerces the parameter?

We should probably document the "canonical" way to convert a reference to a raw pointer when coercion isn't an option somewhere. So far I've seen reference as *const _, ptr::addr_of!(*reference) and now <*const _>::from(reference).

Copy link
Member Author

Choose a reason for hiding this comment

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

I've removed the as cast on pointers now, but kept *ptr over ptr.read() since u8 is Copy and that seems "safer" to me.

@@ -1129,6 +1129,33 @@ impl<T> fmt::Debug for Discriminant<T> {
/// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2)));
/// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3)));
/// ```
///
/// Note that it is *undefined behavior* to [`transmute`] from [`Discriminant`] to a primitive.
/// To access the value of a discriminant with a primitive representation, use pointer casts instead:
Copy link
Member

Choose a reason for hiding this comment

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

Note that the test below currently "passes" even without the repr, though it's UB without the repr -- and MIRI doesn't complain about it either.

So thus I think it's important to put extra emphasis on how this only applies for non-default reprs, and it'd be nice to link to some documentation about those other reprs.

Here's a first draft of an idea, please improve it:

Note that it is undefined behavior to [transmute] from [Discriminant] to a primitive.

If an enum has opted-in to having a defined layout, then it's possible to use pointers to read the memory location storing the discriminant. That cannot be done for anything using the default enum layout, however, as it's undefined where the discriminant is stored -- it might not even be stored at all!

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 25, 2022
@lukas-code lukas-code marked this pull request as draft November 26, 2022 12:09
/// of some variant will not change between compilations with the same compiler. See the [Reference]
/// for more information.
///
/// [Reference]: ../../reference/items/enumerations.html#discriminants
Copy link
Member Author

Choose a reason for hiding this comment

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

This section doesn't exist until rust-lang/reference#1055 is merged, but the linked page still contains some relevant information.

Copy link
Member Author

Choose a reason for hiding this comment

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

The CI didn't like this, so we need to revert 946d51e later.

@lukas-code lukas-code marked this pull request as ready for review November 26, 2022 15:16
@lukas-code
Copy link
Member Author

I've updated it now to include some links to the Reference on enum reprs. I'd rather not link to the RFC, since RFCs tend to get outdated over time and that has led to confusion before. I intentionally left out repr(C) enums here, since the ABI of C enums is notably underspecified and may or may not be c_int.

I also added the easy way to get the discriminant of a c-like enum now, to avoid having someone use unsafe code when it isn't necessary.

Docs preview available here: https://lukas-code.github.io/rust-docs/core/mem/fn.discriminant.html

@lukas-code
Copy link
Member Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Nov 26, 2022
@scottmcm
Copy link
Member

Thanks for the updates! Having the safe way there first is a great addition.

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Nov 27, 2022

📌 Commit 946d51e has been approved by scottmcm

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. labels Nov 27, 2022
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Nov 27, 2022
Explain how to get the discriminant out of a `#[repr(T)] enum` with payload

example stolen from rust-lang/reference#1055

`@rustbot` label A-docs
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Nov 27, 2022
Explain how to get the discriminant out of a `#[repr(T)] enum` with payload

example stolen from rust-lang/reference#1055

``@rustbot`` label A-docs
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Nov 27, 2022
Explain how to get the discriminant out of a `#[repr(T)] enum` with payload

example stolen from rust-lang/reference#1055

```@rustbot``` label A-docs
bors added a commit to rust-lang-ci/rust that referenced this pull request Nov 27, 2022
…iaskrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang#95836 (Use `rust_out{exe_suffix}` for doctests)
 - rust-lang#104882 (notify lcnr on changes to `ObligationCtxt`)
 - rust-lang#104892 (Explain how to get the discriminant out of a `#[repr(T)] enum` with payload)
 - rust-lang#104917 (Allow non-org members to label `requires-debug-assertions`)
 - rust-lang#104931 (Pretty-print generators with their `generator_kind`)
 - rust-lang#104934 (Remove redundant `all` in cfg)
 - rust-lang#104944 (Support unit tests for jsondoclint)
 - rust-lang#104946 (rustdoc: improve popover focus handling JS)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 9ba78ac into rust-lang:master Nov 27, 2022
@rustbot rustbot added this to the 1.67.0 milestone Nov 27, 2022
@lukas-code lukas-code deleted the discriminant branch November 27, 2022 20:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants