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

Document #[repr] on non-C-like enums #25651

Closed
wants to merge 1 commit into from

Conversation

bluss
Copy link
Member

@bluss bluss commented May 20, 2015

Document #[repr] on non-C-like enums

rustc accepts the following today:

#[repr(u8)]
enum Flag<T> {
    Dropped,
    Alive(T),
}

and it has a good use (it appears to me): this inhibits the non-nullable
pointer optimization that the regular Option and similar enums allow.

Document this in the reference, and add tests to make sure it compiles.

This means that we guarantee with repr that the discriminant will be
present and with that size, but not sure if we want to guarantee
anything more (no guarantee on placement in struct).

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @steveklabnik (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see CONTRIBUTING.md for more information.

@bluss
Copy link
Member Author

bluss commented May 20, 2015

This documents current behavior.

Is repr(X) on non-C-like enums is a bug? In that case we need to fix that by rejecting code instead.

@josephglanville
Copy link

I hope it's not a bug, it would be a breaking change if so. I am using the behaviour and I would assume others are to.

The main reason was that the collections::enum_set::CLike is marked unstable and this seemed to do what I need anyways.

@alexcrichton
Copy link
Member

@bluss the #[repr] attribute on data-carrying enums is intentional (e.g. to control the size of the discriminant. It's just quite rare that it's actually used.

@bors: r+ 608e57c rollup

Thanks!

@bluss
Copy link
Member Author

bluss commented May 26, 2015

Great to have that clear!

@SimonSapin and I discovered that this is quite crucial for arrayvec and smallvec -- see issue servo/rust-smallvec#5

@SimonSapin
Copy link
Contributor

It seems very fragile to relay on on #[repr] to influence something left explicitly undefined… Would it make sense to have a "NonNonZero" that cancels the effects of NonZero? Then arrayvec and smallvec could use something like NonNonZero<ManuallyDrop<[T; 8]>> (rust-lang/rfcs#197)

@bluss
Copy link
Member Author

bluss commented May 26, 2015

The idea of this PR was to make it non-fragile: The needed guarantee is written into the reference and a test to prove it added to the testsuite.

@SimonSapin
Copy link
Contributor

I made a PR into your PR to add another test for the behavior that arrayvec relies on: bluss#1

@bluss
Copy link
Member Author

bluss commented May 26, 2015

I never knew you could submit PRs to PRs, that's neat.

arrayvec uses mem::uninitialized but same effect. I don't know, I was satisfied with the tests to ensure the option optimization did not occur by just looking at size_of. it does ensure a discriminant is present, doesn't it?

@SimonSapin
Copy link
Contributor

I think it tests a slightly different case, but feel free to take it or not.

Manishearth added a commit to Manishearth/rust that referenced this pull request May 27, 2015
Document #[repr] on non-C-like enums

rustc accepts the following today:

    #[repr(u8)]
    enum Flag<T> {
        Dropped,
        Alive(T),
    }

and it has a good use (it appears to me): this inhibits the non-nullable
pointer optimization that the regular Option<T> and similar enums allow.

Document this in the reference, and add tests to make sure it compiles.

This means that we guarantee with `repr` that the discriminant will be
present and with that size, but not sure if we want to guarantee
anything more (no guarantee on placement in struct).
@bluss
Copy link
Member Author

bluss commented May 27, 2015

Oh, my tests are actually completely wrong

@bluss
Copy link
Member Author

bluss commented May 27, 2015

Updated PR. Included @SimonSapin's testcase (thanks!), and fixed mine, which were embarrassingly completely incorrect the first time.

bors added a commit that referenced this pull request May 27, 2015
assert!(size_of::<EC<&i32>>() > ptrsize);

// test that a zero payload does not influence the discriminant
let zero: &i32 = unsafe { zeroed() };
Copy link
Member

Choose a reason for hiding this comment

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

Is... this not undefined behavior? We explicitly tell LLVM that all &T references are not null, and this is making a null one?

Copy link
Member Author

Choose a reason for hiding this comment

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

Right. It should never load the value in the zero variable or in its location inside the enum though, can it be safe that way?

Copy link
Member

Choose a reason for hiding this comment

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

Instead of inspecting the payload, could this just assert that the size of the enum is twice the size of a word?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well that's basically what the tests above already do. They are less specific maybe.

So we should remove this?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think it's fine to remove.

rustc accepts the following today:

    #[repr(u8)]
    enum Flag<T> {
        Dropped,
        Alive(T),
    }

and it has a good use (it appears to me): this inhibits the non-nullable
pointer optimization that the regular Option<T> and similar enums allow.

Document this in the reference, and add tests to make sure it compiles.

This means that we guarantee with `repr` that the discriminant will be
present and with that size, but not sure if we want to guarantee
anything more (no guarantee on placement in struct).
@bluss
Copy link
Member Author

bluss commented May 28, 2015

Thanks, PR updated. Only tests sizes now.

@alexcrichton
Copy link
Member

@bors: r+ d0e6396

@bluss
Copy link
Member Author

bluss commented May 28, 2015

Thanks!

steveklabnik added a commit to steveklabnik/rust that referenced this pull request May 29, 2015
Document #[repr] on non-C-like enums

rustc accepts the following today:

    #[repr(u8)]
    enum Flag<T> {
        Dropped,
        Alive(T),
    }

and it has a good use (it appears to me): this inhibits the non-nullable
pointer optimization that the regular Option<T> and similar enums allow.

Document this in the reference, and add tests to make sure it compiles.

This means that we guarantee with `repr` that the discriminant will be
present and with that size, but not sure if we want to guarantee
anything more (no guarantee on placement in struct).
steveklabnik added a commit to steveklabnik/rust that referenced this pull request May 29, 2015
Document #[repr] on non-C-like enums

rustc accepts the following today:

    #[repr(u8)]
    enum Flag<T> {
        Dropped,
        Alive(T),
    }

and it has a good use (it appears to me): this inhibits the non-nullable
pointer optimization that the regular Option<T> and similar enums allow.

Document this in the reference, and add tests to make sure it compiles.

This means that we guarantee with `repr` that the discriminant will be
present and with that size, but not sure if we want to guarantee
anything more (no guarantee on placement in struct).
@bors
Copy link
Contributor

bors commented May 29, 2015

⌛ Testing commit d0e6396 with merge c449a9f...

@bors
Copy link
Contributor

bors commented May 29, 2015

💔 Test failed - auto-mac-32-opt

assert_eq!(size_of::<Eu16<()>>(), 2);
assert_eq!(size_of::<Ei32<()>>(), 4);
assert_eq!(size_of::<Eu32<()>>(), 4);
assert_eq!(size_of::<Ei64<()>>(), 8);
Copy link
Member Author

Choose a reason for hiding this comment

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

thread '<main>' panicked at 'assertion failed:(left == right)(left:4, right:8)', /Users/rustbuild/src/rust-buildbot/slave/auto-mac-32-opt/build/src/test/run-pass/enum-discrim-manual-sizing-2.rs:89

on 32-bit. Looks like there is a bug in #[repr]?

Copy link
Member

Choose a reason for hiding this comment

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

Hm yes, that sounds like a bug in #[repr]

steveklabnik added a commit to steveklabnik/rust that referenced this pull request May 29, 2015
Document #[repr] on non-C-like enums

rustc accepts the following today:

    #[repr(u8)]
    enum Flag<T> {
        Dropped,
        Alive(T),
    }

and it has a good use (it appears to me): this inhibits the non-nullable
pointer optimization that the regular Option<T> and similar enums allow.

Document this in the reference, and add tests to make sure it compiles.

This means that we guarantee with `repr` that the discriminant will be
present and with that size, but not sure if we want to guarantee
anything more (no guarantee on placement in struct).
@bluss
Copy link
Member Author

bluss commented May 29, 2015

We need to scrub this PR until the bug is fixed. I'll see what I can do though

@steveklabnik
Copy link
Member

@bors: r-

@bluss
Copy link
Member Author

bluss commented Jun 8, 2015

Closing for now, reported a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants