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

Enum with non-primitive fields can be errorneously cast to primitive type #136508

Open
alexpyattaev opened this issue Feb 3, 2025 · 2 comments
Open
Labels
A-coercions Area: implicit and explicit `expr as Type` coercions C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@alexpyattaev
Copy link

alexpyattaev commented Feb 3, 2025

I tried this code:

#[repr(u8)]
pub enum FooBar {
    Foo(i32),
    Bar(String),
}

impl FooBar {
    fn discriminant(&self) -> u8 {
        unsafe { *(self as *const Self as *const u8) }
    }
}
fn main() {
    println!("{}", FooBar::Foo as u8);
    println!("{}", FooBar::Bar as u8);
    println!("{}", FooBar::Foo(42).discriminant());
    println!("{}", FooBar::Bar("baz".to_owned()).discriminant());
}

I expected to see this happen: The code should fail to compile, as casting to u8 for this enum is not allowed.

Instead, this happened: code compiles fine, and prints garbage. The garbage printed appears to be part of the address of the function that would instantiate the respective variant of the enum. As there are no warnings issued, this can be very nasty. Clippy does detect this and correctly warns.

Playground for easy reproduction on any compiler of choice.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3e3b23e01f545caf4f4fc88ab45edd61

Note

  • Making any one field not contain any value will make this fail to compile (which is the expected result)
  • This can be especially nasty if you have an enum with no payload in any of the fields (which is safe to cast) and then add payload to all of the fields. The code will still compile, even though its meaning has changed completely.
@alexpyattaev alexpyattaev added the C-bug Category: This is a bug. label Feb 3, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Feb 3, 2025
@alexpyattaev alexpyattaev changed the title Enum with non-primitive fields can be cast to primitive type (and produces incorrect results) Enum with non-primitive fields can be errorneously cast to primitive type Feb 3, 2025
@ehuss
Copy link
Contributor

ehuss commented Feb 3, 2025

I think there may be a slight misunderstanding. An as cast isn't allowed for an enum value, like:

    println!("{}", FooBar::Foo(1) as u8);
    println!("{}", FooBar::Bar(String::new()) as u8);

In the example above, FooBar::Foo and FooBar::Bar are functions, not enum variants. We do define that casting from functions to integers is allowed (although it's not super well defined).

@alexpyattaev
Copy link
Author

alexpyattaev commented Feb 4, 2025

Yes, this is precisely what is happening. The concern is that one can write the enum "variant casting code" for an enum that does not carry payload (which will look like Enum::Variant as u8) and get a nasty surprise when it still compiles for case where enum was changed to carry data, and now the cast does crazy stuff that makes no sense. In general, casting a function pointer to anything that is smaller than usize makes no sense (and clippy knows about it), yet the compiler will happily do it here without any warnings.

@jieyouxu jieyouxu added T-lang Relevant to the language team, which will review and decide on the PR/issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-coercions Area: implicit and explicit `expr as Type` coercions and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Feb 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-coercions Area: implicit and explicit `expr as Type` coercions C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants