Skip to content

Conversation

@paradoxicalguy
Copy link

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.

Copy link
Member

@aapoalas aapoalas left a 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! :)

@paradoxicalguy
Copy link
Author

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.

@paradoxicalguy
Copy link
Author

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.
all tests passed; would appreciate another look!

Copy link
Member

@aapoalas aapoalas left a 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"));
Copy link
Member

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"));
Copy link
Member

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() {
Copy link
Member

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)]
Copy link
Member

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
Copy link
Member

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();
Copy link
Member

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 {
Copy link
Member

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.

@workingjubilee
Copy link

This PR will need a new title now that it does something entirely different.

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.

Named enum fields are not named

3 participants