-
Notifications
You must be signed in to change notification settings - Fork 40
rust: implement Reloc macro #126
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
Closed
pawelrutkaq
wants to merge
1
commit into
eclipse-score:main
from
qorix-group:pawelrutkaq_reloc_macro
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # ******************************************************************************* | ||
| # Copyright (c) 2025 Contributors to the Eclipse Foundation | ||
| # | ||
| # See the NOTICE file(s) distributed with this work for additional | ||
| # information regarding copyright ownership. | ||
| # | ||
| # This program and the accompanying materials are made available under the | ||
| # terms of the Apache License Version 2.0 which is available at | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # ******************************************************************************* | ||
| load("@rules_rust//rust:defs.bzl", "rust_proc_macro", "rust_doc_test") | ||
|
|
||
| rust_proc_macro( | ||
| name = "com-api-concept-macros", | ||
| srcs = ["lib.rs"], | ||
| crate_name = "com_api_concept_macros", | ||
| visibility = [ | ||
| "//visibility:public", | ||
| ], | ||
| deps = [ | ||
| "@crate_index//:syn", | ||
| "@crate_index//:quote", | ||
| ], | ||
| ) | ||
|
|
||
| rust_doc_test( | ||
| name = "com-api-concept-macros-tests", | ||
| crate = ":com-api-concept-macros", | ||
| ) |
247 changes: 247 additions & 0 deletions
247
score/mw/com/impl/rust/com-api/com-api-concept-macros/lib.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,247 @@ | ||
| use proc_macro::TokenStream; | ||
| use quote::quote; | ||
| use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, Generics, Meta, Type}; | ||
|
|
||
| /// | ||
| /// Derive macro for the `Reloc` trait. This macro automatically implements the `Reloc` trait | ||
| /// for structs, ensuring that all field types also implement `Reloc`. This also requires that the | ||
| /// struct is marked with `#[repr(C)]`. | ||
| /// | ||
| #[proc_macro_derive(Reloc)] | ||
| pub fn derive_reloc(input: TokenStream) -> TokenStream { | ||
| let input_args = parse_macro_input!(input as DeriveInput); | ||
| let ident_name = &input_args.ident; | ||
|
|
||
| // Ensure #[repr(C)] on the struct itself | ||
| if !has_repr_c(&input_args.attrs) { | ||
| return syn::Error::new_spanned( | ||
| &ident_name, | ||
| "The #[derive(Reloc)] macro requires #[repr(C)] on the type", | ||
| ) | ||
| .to_compile_error() | ||
| .into(); | ||
| } | ||
|
|
||
| let mut generics = create_bounds_with_reloc(input_args.generics.clone()); | ||
|
|
||
| // Collect field types. Since we cannot check if given field type has implemented given trait we will use compiler | ||
| // to check that generating trait bounds on fields like below: | ||
| // | ||
| // struct Example { | ||
| // field1: SomeType, | ||
| // field2: AnotherType, | ||
| // } | ||
| // unsafe impl Reloc for Example where SomeType: Reloc, AnotherType: Reloc {} | ||
| // | ||
| let field_types = match collect_field_types(&input_args.data){ | ||
| Ok(types) => types, | ||
| Err(()) => return syn::Error::new_spanned( | ||
| &ident_name, | ||
| "The #[derive(Reloc)] macro is supported only for enums(C like), structs and tuple structs", | ||
| ) | ||
| .to_compile_error() | ||
| .into(), | ||
| }; | ||
|
|
||
| { | ||
| let where_clause_ref = generics.make_where_clause(); | ||
| for ty in field_types { | ||
| where_clause_ref.predicates.push(parse_quote!(#ty: Reloc)); | ||
| } | ||
| } | ||
|
|
||
| // Create pieces for final implementation | ||
| let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); | ||
|
|
||
| let expanded = quote! { | ||
| unsafe impl #impl_generics Reloc for #ident_name #type_generics #where_clause {} | ||
| }; | ||
|
|
||
| expanded.into() | ||
| } | ||
|
|
||
| /// Add `T: Reloc` bounds to all generic parameters | ||
| fn create_bounds_with_reloc(mut generics: Generics) -> Generics { | ||
| for param in generics.type_params_mut() { | ||
| param.bounds.push(parse_quote!(Reloc)); | ||
| } | ||
| generics | ||
| } | ||
|
|
||
| /// Check for #[repr(C)] existence | ||
| fn has_repr_c(attrs: &[syn::Attribute]) -> bool { | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| false | ||
| } | ||
|
|
||
| /// Collect field types, skipping generic parameters and primitives | ||
| fn collect_field_types<'a>(data: &'a Data) -> Result<Vec<&'a Type>, ()> { | ||
| let mut out = Vec::new(); | ||
|
|
||
| match data { | ||
| Data::Struct(data_struct) => { | ||
| out = match &data_struct.fields { | ||
| Fields::Named(fields) => fields.named.iter().map(|f| &f.ty).collect(), | ||
| Fields::Unnamed(fields) => fields.unnamed.iter().map(|f| &f.ty).collect(), | ||
| Fields::Unit => Vec::new(), | ||
| }; | ||
| } | ||
| Data::Enum(data_enum) => { | ||
| if !data_enum | ||
| .variants | ||
| .iter() | ||
| .all(|variant| matches!(variant.fields, Fields::Unit)) | ||
| { | ||
| return Err(()); | ||
| } | ||
| } | ||
| Data::Union(_) => return Err(()), | ||
| }; | ||
|
|
||
| Ok(out) | ||
| } | ||
|
|
||
| // Use doctest to test failed compilations and successful ones | ||
|
|
||
| /// ``` | ||
| /// pub unsafe trait Reloc {} | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct RelocType<T> { t : T} | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct RelocTypeTwoGen<T, D> { t : T, d: D} | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_works_on_generic_types() {} | ||
|
|
||
| /// ``` | ||
| /// pub unsafe trait Reloc {} | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// enum EnumPlanType { | ||
| /// A, | ||
| /// B, | ||
| /// C | ||
| /// } | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_works_on_c_like_enums() {} | ||
|
|
||
| /// ``` | ||
| /// pub unsafe trait Reloc {} | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// struct StructTag{} | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_works_on_unit() {} | ||
|
|
||
| /// ``` | ||
| /// pub unsafe trait Reloc {} | ||
| /// unsafe impl Reloc for u32{} | ||
| /// unsafe impl Reloc for u64{} | ||
| /// | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct RelocType<T> { t : T} | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct StructType { | ||
| /// a: u32, | ||
| /// b: u64, | ||
| /// f: RelocType<u64> | ||
| /// } | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_works_on_structs() {} | ||
|
|
||
| /// ``` | ||
| /// pub unsafe trait Reloc {} | ||
| /// unsafe impl Reloc for u32{} | ||
| /// unsafe impl Reloc for u64{} | ||
| /// | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct RelocType<T> { t : T} | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct TupleStructType( u32, u64, RelocType<u64> ); | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_works_on_tuples() {} | ||
|
|
||
| // Failure cases | ||
|
|
||
| /// ```compile_fail | ||
| /// pub unsafe trait Reloc {} | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// enum EnumPlanType { | ||
| /// A(u32), | ||
| /// B, | ||
| /// C | ||
| /// } | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_fail_on_non_c_like_enums() {} | ||
|
|
||
| /// ```compile_fail | ||
| /// pub unsafe trait Reloc {} | ||
| /// unsafe impl Reloc for u32{} | ||
| /// | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct RelocType<T> { t : T} | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct StructType { | ||
| /// a: u32, | ||
| /// b: u64, | ||
| /// f: RelocType<u64> | ||
| /// } | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_fails_if_member_is_not_reloc_on_struct() {} | ||
|
|
||
| /// ```compile_fail | ||
| /// pub unsafe trait Reloc {} | ||
| /// use com_api_concept_macros::Reloc; | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct RelocType<T> { t : T} | ||
| /// | ||
| /// #[derive(Reloc)] | ||
| /// #[repr(C)] | ||
| /// pub struct ContainsNonRelocType(RelocType<u64>); | ||
| /// ``` | ||
| #[cfg(doctest)] | ||
| fn reloc_fails_on_generic_types_that_do_not_impl_reloc() {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
Can we have different name of this file, as we are not using lib.rs naming in com-api lib.
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.
Isnt
lib.rsa general Rust convention for lib crates ? Bit confused.Uh oh!
There was an error while loading. Please reload this page.
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.
We can use lib.rs, i just mentioned because our all the files naming is based on module name in com-api , so to make file naming consistent i have suggested.
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.
as we talked, I keep
lib.rsas all other codebase in score uses lib.rs