-
Notifications
You must be signed in to change notification settings - Fork 93
fix: Allow sized C-like enums in Rust #905
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -223,7 +223,8 @@ fn check_attrs(fields: &Fields) -> Result<(), syn::Error> { | |||||
| /// | ||||||
| /// # Validation performed by this macro | ||||||
| /// | ||||||
| /// - **`#[repr(C)]`** must be present on the type. | ||||||
| /// - **`#[repr(C)]`** must be present on the type. For C-like enums, a primitive | ||||||
| /// integer repr (e.g. `#[repr(u8)]`) is accepted as well. | ||||||
| /// - It must be a struct, tuple struct, or C-like enum (no unions or non-C-like enums). | ||||||
| /// | ||||||
| /// These checks are intentionally kept in `Reloc` rather than `CommData` so that each macro | ||||||
|
|
@@ -237,7 +238,10 @@ pub fn derive_reloc(input: TokenStream) -> TokenStream { | |||||
| if !has_repr_c(&input_args.attrs) { | ||||||
| return syn::Error::new_spanned( | ||||||
| ident_name, | ||||||
| "The #[derive(Reloc)] macro requires #[repr(C)] on the type", | ||||||
| concat!( | ||||||
| "The #[derive(Reloc)] macro requires #[repr(C)] or a primitive integer repr ", | ||||||
| "(e.g. #[repr(u8)]) on the type" | ||||||
| ), | ||||||
| ) | ||||||
| .to_compile_error() | ||||||
| .into(); | ||||||
|
|
@@ -260,8 +264,10 @@ pub fn derive_reloc(input: TokenStream) -> TokenStream { | |||||
| Err(()) => { | ||||||
| return syn::Error::new_spanned( | ||||||
| ident_name, | ||||||
| "The #[derive(Reloc)] macro is supported only for enums(C like), structs and \ | ||||||
| tuple structs", | ||||||
| concat!( | ||||||
| "The #[derive(Reloc)] macro is supported only for enums(C like), structs ", | ||||||
| "and tuple structs" | ||||||
| ), | ||||||
| ) | ||||||
| .to_compile_error() | ||||||
| .into() | ||||||
|
|
@@ -293,16 +299,45 @@ fn create_bounds_with_reloc(mut generics: Generics) -> Generics { | |||||
| generics | ||||||
| } | ||||||
|
|
||||||
| /// Check for #[repr(C)] existence | ||||||
| /// Check whether the type is representable as C struct or enum. | ||||||
| fn has_repr_c(attrs: &[syn::Attribute]) -> bool { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const INT_REPRS: [&str; 10] = [ | ||||||
| "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize", | ||||||
| ]; | ||||||
|
|
||||||
| for attr in attrs { | ||||||
| if attr.path().is_ident("repr") { | ||||||
| if let Meta::List(list) = &attr.meta { | ||||||
| let tokens = list.tokens.to_string(); | ||||||
| if tokens.split(',').any(|t| t.trim() == "C") { | ||||||
| return true; | ||||||
| } | ||||||
| if !attr.path().is_ident("repr") { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| let mut found = false; | ||||||
| let _ = attr.parse_nested_meta(|meta| { | ||||||
| if meta.path.is_ident("C") { | ||||||
| found = true; | ||||||
| return Ok(()); | ||||||
| } | ||||||
|
|
||||||
| if meta | ||||||
| .path | ||||||
| .get_ident() | ||||||
| .is_some_and(|id| INT_REPRS.contains(&id.to_string().as_str())) | ||||||
| { | ||||||
| found = true; | ||||||
| return Ok(()); | ||||||
| } | ||||||
|
|
||||||
| // Unrecognized modifier (e.g. align(4), packed(2)); consume any | ||||||
| // parenthesized arguments so parsing succeeds, then ignore it. | ||||||
| if meta.input.peek(syn::token::Paren) { | ||||||
| let content; | ||||||
| syn::parenthesized!(content in meta.input); | ||||||
| let _ = content; | ||||||
| } | ||||||
| Ok(()) | ||||||
|
Comment on lines
+331
to
+336
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think padding should be handled by ABI, possibly through some kind of code generation tool. If we allow padding to be managed on the Rust side, but other applications or components do not apply the exact same rules, it could lead to compatibility issues. I would recommend making #
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have same opinion about
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I can also only allow
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My concern is that alignment and padding should be managed from a single source of truth for both C++ and Rust. If you look at the Rust examples, we write the corresponding C++ structure as well for FFI layer translation. If a user applies packing or custom alignment only on the Rust side and forgets to apply the same settings in C++, it can easily result in layout mismatches and misalignment issues. So, if we want to support this feature, we need to ensure that both the Rust and C++ definitions are generated or configured with identical alignment requirements. We can enable this feature, but we need a mechanism to guarantee consistency across both sides. A Rust macro can validate attributes present on the Rust type, but currently we do not have a way to verify that the equivalent C++ structure has been defined with the same alignment characteristics. Without such validation, we risk introducing subtle interoperability bugs.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the second thought, I think when an |
||||||
| }); | ||||||
|
|
||||||
| if found { | ||||||
| return true; | ||||||
| } | ||||||
| } | ||||||
| false | ||||||
|
|
@@ -367,6 +402,70 @@ fn reloc_works_on_generic_types() {} | |||||
| #[cfg(doctest)] | ||||||
| fn reloc_works_on_c_like_enums() {} | ||||||
|
|
||||||
| /// ``` | ||||||
| /// pub unsafe trait Reloc: Send + Unpin + 'static {} | ||||||
| /// use score_com_macros::Reloc; | ||||||
| /// | ||||||
| /// #[derive(Reloc)] | ||||||
| /// #[repr(u8)] | ||||||
| /// enum EnumU8 { | ||||||
| /// A, | ||||||
| /// B, | ||||||
| /// C | ||||||
| /// } | ||||||
| /// | ||||||
| /// #[derive(Reloc)] | ||||||
| /// #[repr(u32)] | ||||||
| /// enum EnumU32 { | ||||||
| /// A, | ||||||
| /// B, | ||||||
| /// C | ||||||
| /// } | ||||||
| /// ``` | ||||||
| #[cfg(doctest)] | ||||||
| fn reloc_works_on_c_like_enums_with_int_repr() {} | ||||||
|
|
||||||
| /// ``` | ||||||
| /// pub unsafe trait Reloc: Send + Unpin + 'static {} | ||||||
| /// unsafe impl Reloc for u8 {} | ||||||
| /// use score_com_macros::Reloc; | ||||||
| /// | ||||||
| /// #[derive(Reloc)] | ||||||
| /// #[repr(C, align(4))] | ||||||
| /// pub struct StructWithAlign { | ||||||
| /// a: u8, | ||||||
| /// } | ||||||
| /// | ||||||
| /// #[derive(Reloc)] | ||||||
| /// #[repr(u8, align(4))] | ||||||
| /// enum EnumWithAlign { | ||||||
| /// A, | ||||||
| /// B, | ||||||
| /// C | ||||||
| /// } | ||||||
| /// ``` | ||||||
| #[cfg(doctest)] | ||||||
| fn reloc_works_with_repr_c_and_align() {} | ||||||
|
|
||||||
| /// ``` | ||||||
| /// pub unsafe trait Reloc: Send + Unpin + 'static {} | ||||||
| /// pub trait CommData: Reloc { | ||||||
| /// const ID: &'static str; | ||||||
| /// } | ||||||
| /// use score_com_macros::{CommData, Reloc}; | ||||||
| /// | ||||||
| /// #[derive(Reloc, CommData)] | ||||||
| /// #[repr(u8)] | ||||||
| /// pub enum MyEnum { | ||||||
| /// A, | ||||||
| /// B, | ||||||
| /// C, | ||||||
| /// } | ||||||
| /// // ID will be auto-generated as "module_path::MyEnum" | ||||||
| /// ``` | ||||||
| #[cfg(doctest)] | ||||||
| fn comm_data_and_reloc_work_on_enum_with_int_repr() {} | ||||||
|
|
||||||
| /// ``` | ||||||
| /// pub unsafe trait Reloc: Send + Unpin + 'static {} | ||||||
| /// use score_com_macros::Reloc; | ||||||
|
|
@@ -435,6 +534,21 @@ fn reloc_works_on_tuples() {} | |||||
| #[cfg(doctest)] | ||||||
| fn reloc_fail_on_non_c_like_enums() {} | ||||||
|
|
||||||
| /// ```compile_fail | ||||||
| /// pub unsafe trait Reloc: Send + Unpin + 'static {} | ||||||
| /// use score_com_macros::Reloc; | ||||||
| /// | ||||||
| /// #[derive(Reloc)] | ||||||
| /// #[repr(u8)] | ||||||
| /// pub struct StructType { | ||||||
| /// a: u32, | ||||||
| /// } | ||||||
| /// ``` | ||||||
| /// This will fail because rustc itself rejects a primitive integer repr on structs | ||||||
| /// (it is only legal on enums). | ||||||
| #[cfg(doctest)] | ||||||
| fn reloc_fails_on_struct_with_int_repr() {} | ||||||
|
|
||||||
| /// ```compile_fail | ||||||
| /// pub unsafe trait Reloc: Send + Unpin + 'static {} | ||||||
| /// unsafe impl Reloc for u32{} | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.