-
Notifications
You must be signed in to change notification settings - Fork 1
reject named-field enums in SoAble derive to avoid unsound layout #45
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather see the named field cases fixed than to remove the support; right now they work and are generally fine in normal cases when the user has laid them out in systematic order.
Also thank you for the contribution and hello! :)
|
hello :) and thank you for the clarification i'll take a look at implementing a correct field-name-based mapping for enums with named fields (so that fields are paired by name rather than position) and update the PR accordingly. |
|
i’ve updated the derive to implement a field-name–based SoA layout for enums with named fields, pairing fields by name rather than by position. |
aapoalas
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for putting in the work to generate the "correct" SoA code, and nice work!
Some issues remain: most pressing are the failing formatting / linting, and the amount of removed tests. Beyond that there were a few other issues that I commented in, mostly small stuff.
| let result = expand_data_enum(input).unwrap().to_string(); | ||
|
|
||
| // Should have unions named after fields, not positions | ||
| assert!(result.contains("union FooFieldx")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: The field name's first character should be in upper case, so FooFieldX and FooFieldY.
|
|
||
| assert!(result.contains("union TestEnumField0")); | ||
| // For unnamed enums, unions should be named Fieldfield0, Fieldfield1, etc. | ||
| assert!(result.contains("union TestEnumFieldfield0")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: There's an extra "Field" here. Field0, not FieldField0.
| } | ||
|
|
||
| #[test] | ||
| fn test_expand_derive_enum_explicit_discriminant() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: It doesn't seem like a good idea to remove all of these tests.
| } | ||
|
|
||
| // Test enum with named fields | ||
| #[derive(SoAble)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: These integration tests should not be removed.
| )); | ||
| } | ||
|
|
||
| // CHANGE: Determine if we have named fields |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick (blocking): The "CHANGE" etc comments are helpful for reviewing, but they'll be merely confusing when left in the code.
I recommend self-reviewing your PR and leaving comments in place of these comment lines in the future,
We'll need to remove the "CHANGE" / "ADD" parts at the very least.
| fn collect_all_field_names( | ||
| variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>, | ||
| ) -> Vec<String> { | ||
| let mut field_names = BTreeMap::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: I think we should use a Vec here instead: we do not want to sort the fields by their field name's hash value. The best sorting is probably going to be what the user provides for us, or as close to that as we can.
Effectively in this sort of case:
enum Foo {
A { x: u32, y: u32 },
B { x: u32, z: u32 },
}we'd end up with three fields for each of x, y, and z, and they'd be in that order: vec!["x", "y", "z"].
Instead of using BTreeMap insertion, just use a Vec with contains and push: it might technically be slower, but we expect the number of fields to be pretty low so it is unlikely to really matter.
| }) | ||
| .collect(); | ||
| // CHANGE: Build union fields differently for named vs unnamed | ||
| let (field_unions, all_field_identifiers): (Vec<Vec<_>>, Vec<String>) = if has_named_fields { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: The if and else branches here could benefit from more comments.
|
This PR will need a new title now that it does something entirely different. |
enums with named fields are currently laid out positionally, which can
pair unrelated fields across variants and cause unsound behavior.
this PR explicitly rejects named-field enums in the SoAble derive macro,
emitting a clear compile-time error explaining that a field-name–based layout is required.
this avoids unsoundness while leaving room for a correct implementation in the future.
fixes #43 by rejecting the problematic case.