add support for deriving NoUninit on enums with fields#292
add support for deriving NoUninit on enums with fields#292Lokathor merged 8 commits intoLokathor:mainfrom
Conversation
We check for padding like we do for structs except that we also consider the enum discriminant when calculating the unpadded size.
ecb270e to
5a6d5a0
Compare
|
any updates on this? |
|
good question. I'd totally forgotten about this PR before putting out the derive update yesterday. I'll try to sit down and review it soon. |
|
Would love to get this in! One thing that might be missing here is the docs for the Line 51 in 028ff3b |
|
I believe that comment is based on the fact that, in the general case, if there is an enum variant with a field, the tag byte will always be initialized but the field bytes might not be initialized if it's currently been tagged as some other variant consider: the byte the u8 sits in is only sometimes initialized. |
|
That makes sense. This wouldn't be the case if all variants have the same size and there's no padding though, right? |
|
I think so. Actually I don't know how the tag bytes are considered to work if there's more than one tag byte for alignment. pub enum ColorUsize {
Red(usize),
Green(usize),
}In an enum like this, assuming 64-bit for sake of argument, the tag will be 8 bytes so that the |
As written, this enum is (implicitly) If the enum was |
|
Alright, then we can absolutely adjust the wording on the trait to allow in additional types of enum that we do get the guarantees for. |
|
Finally have some bandwidth towards open source again recently, so I'm gonna try to review this in the next few days :) |
|
As discussed, I believe the full requirements are:
|
The implementation provided in this PR checks all of these requirements by checking that a packed struct of
Hmm, when I originally wrote the code for this PR, I made |
I added some code that computes the integer type of the correct size for the discriminant of |
|
i think we should error out and require a specific integer type instead of guessing at the probable integer type. In general, the derive should always error out if we're not 100% sure. Any user can "override" our hesitation by just writing the manual impl, if they're that confident we've been too conservative. |
|
The alternative would be to not assume the discriminant size and derive it by duplicating the type, removing the fields, but keeping any explicit discriminant values, and then looking at the resulting enum's size. That's literally the representation for |
|
Yeah that would be good. If we're following a spec it's fine. |
Unfortunately, the integer discriminant used for #[repr(C)] doesn't have a name (it's not always core::ffi::c_int), but we can use some compile-time tricks to get the integer type. Use that instead of hard- coding core::ffi::c_int and add support for deriving NoUninit for #[repr(C)] enums.
145284d to
2b41d97
Compare
|
I updated the desugaring to transform enums with fields into fieldless enums (as described in the reference) and use that to get the size of the discriminant. |
|
@fu5ha do you want to do a final check on this before a merge? |
|
@Lokathor yes, reviewing it now |
fu5ha
left a comment
There was a problem hiding this comment.
A couple of minor comments. In addition to the suggestions/comments in the code, the docs for both the core NoUninit trait as well as the NoUninit derive macro need updating. They should enumerate exactly the requirements for when field-ful(?) enums are supported, and link to reference for further info.
Co-authored-by: Gray Olson <gray@grayolson.com>
|
Thanks for the review @fu5ha ! |
There was a problem hiding this comment.
Thanks, pushed a tiny update to the NoUninit docs, otherwise looks good.
My only hesitation left is the fact we're generating a non-hygenic BlahDiscriminant type for all #[repr(C)] enums now, but I don't immediately see a way to prevent that. We already do this with a BlahBits type for the CheckedBitPattern derive macro, so it's not un-heardof... @Lokathor what do you think?
The |
|
Oh! For some reason I thought the LGTM! |
|
Wait, hm. For the |
This makes the generated code a bit harder to read, but also has the advantage of not unnecessarily adding a type to the global namespace for CheckedBitPattern.
Yup, that works, good idea! |
|
Awesome :) Thanks for working on this and applying feedback! Think we can merge whenever @Lokathor would like |
|
Hey @Lokathor would it be possible to publish a new version with this change? |
|
Ah sorry, I didn't realize it'd been a month already. Let me see what I can do from my phone. |
|
Should be all good now, 1.10 |
|
Could we get a new #[derive(Clone, Copy, bytemuck::CheckedBitPattern)]
#[repr(C)]
pub enum Foo {
X,
}
// expands to
unsafe impl ::bytemuck::CheckedBitPattern for Foo {
type Bits = <[::core::primitive::u8; ::core::mem::size_of::<
Foo,
>()] as ::bytemuck::derive::EnumTagIntegerBytes>::Integer; // here's where the error's from
#[inline]
#[allow(clippy::double_comparisons)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits >= 0 && *bits <= 0
}
}error[E0433]: failed to resolve: could not find `derive` in `bytemuck`
--> src/lib.rs:1:23
|
1 | #[derive(Clone, Copy, bytemuck::CheckedBitPattern)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not find `derive` in `bytemuck`
|
= note: this error originates in the derive macro `bytemuck::CheckedBitPattern` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0433`.
error: could not compile `ashldkjfsa` (lib) due to 1 previous error$ cargo tree
ashldkjfsa v0.1.0 (/tmp/ashldkjfsa)
└── bytemuck v1.23.1
└── bytemuck_derive v1.10.1 (proc-macro)
├── proc-macro2 v1.0.95
│ └── unicode-ident v1.0.18
├── quote v1.0.40
│ └── proc-macro2 v1.0.95 (*)
└── syn v2.0.104
├── proc-macro2 v1.0.95 (*)
├── quote v1.0.40 (*)
└── unicode-ident v1.0.18 |
|
Oh my, okay! |
|
Should be all up to date in bytemuck 1.23.2 |
We check for padding like we do for structs except that we also consider the enum discriminant when calculating the unpadded size.