Skip to content

Commit e720eb8

Browse files
committed
rust: implement Reloc macro
1 parent 01988ed commit e720eb8

File tree

7 files changed

+289
-5
lines changed

7 files changed

+289
-5
lines changed

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
9898
bazel_dep(name = "google_benchmark", version = "1.9.4")
9999
bazel_dep(name = "rules_rust", version = "0.61.0")
100100
bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2")
101-
bazel_dep(name = "score_crates", version = "0.0.5", repo_name = "crate_index")
101+
bazel_dep(name = "score_crates", version = "0.0.6", repo_name = "crate_index")
102102

103103
bazel_dep(name = "boost.program_options", version = "1.87.0")
104104
bazel_dep(name = "download_utils", version = "1.0.1")

score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ use com_api::{
1515
Consumer, Interface, OfferedProducer, Producer, Publisher, Reloc, Runtime, Subscriber,
1616
};
1717

18-
#[derive(Debug)]
18+
#[derive(Debug, Reloc)]
19+
#[repr(C)]
1920
pub struct Tire {}
20-
unsafe impl Reloc for Tire {}
2121

22-
#[derive(Debug)]
22+
#[derive(Debug, Reloc)]
23+
#[repr(C)]
2324
pub struct Exhaust {}
24-
unsafe impl Reloc for Exhaust {}
2525

2626
pub struct VehicleInterface {}
2727

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
13+
load("@rules_rust//rust:defs.bzl", "rust_proc_macro", "rust_doc_test")
14+
15+
rust_proc_macro(
16+
name = "com-api-concept-macros",
17+
srcs = ["lib.rs"],
18+
crate_name = "com_api_concept_macros",
19+
visibility = [
20+
"//visibility:public",
21+
],
22+
deps = [
23+
"@crate_index//:syn",
24+
"@crate_index//:quote",
25+
],
26+
)
27+
28+
rust_doc_test(
29+
name = "com-api-concept-macros-tests",
30+
crate = ":com-api-concept-macros",
31+
)
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
use proc_macro::TokenStream;
2+
use quote::quote;
3+
use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, Generics, Meta, Type};
4+
5+
///
6+
/// Derive macro for the `Reloc` trait. This macro automatically implements the `Reloc` trait
7+
/// for structs, ensuring that all field types also implement `Reloc`. This also requires that the
8+
/// struct is marked with `#[repr(C)]`.
9+
///
10+
#[proc_macro_derive(Reloc)]
11+
pub fn derive_reloc(input: TokenStream) -> TokenStream {
12+
let input_args = parse_macro_input!(input as DeriveInput);
13+
let ident_name = &input_args.ident;
14+
15+
// Ensure #[repr(C)] on the struct itself
16+
if !has_repr_c(&input_args.attrs) {
17+
return syn::Error::new_spanned(
18+
&ident_name,
19+
"The #[derive(Reloc)] macro requires #[repr(C)] on the type",
20+
)
21+
.to_compile_error()
22+
.into();
23+
}
24+
25+
let mut generics = create_bounds_with_reloc(input_args.generics.clone());
26+
27+
// Collect field types. Since we cannot check if given field type has implemented given trait we will use compiler
28+
// to check that generating trait bounds on fields like below:
29+
//
30+
// struct Example {
31+
// field1: SomeType,
32+
// field2: AnotherType,
33+
// }
34+
// unsafe impl Reloc for Example where SomeType: Reloc, AnotherType: Reloc {}
35+
//
36+
let field_types = match collect_field_types(&input_args.data){
37+
Ok(types) => types,
38+
Err(()) => return syn::Error::new_spanned(
39+
&ident_name,
40+
"The #[derive(Reloc)] macro is supported only for enums(C like), structs and tuple structs",
41+
)
42+
.to_compile_error()
43+
.into(),
44+
};
45+
46+
{
47+
let where_clause_ref = generics.make_where_clause();
48+
for ty in field_types {
49+
where_clause_ref.predicates.push(parse_quote!(#ty: Reloc));
50+
}
51+
}
52+
53+
// Create pieces for final implementation
54+
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
55+
56+
let expanded = quote! {
57+
unsafe impl #impl_generics Reloc for #ident_name #type_generics #where_clause {}
58+
};
59+
60+
expanded.into()
61+
}
62+
63+
/// Add `T: Reloc` bounds to all generic parameters
64+
fn create_bounds_with_reloc(mut generics: Generics) -> Generics {
65+
for param in generics.type_params_mut() {
66+
param.bounds.push(parse_quote!(Reloc));
67+
}
68+
generics
69+
}
70+
71+
/// Check for #[repr(C)] existence
72+
fn has_repr_c(attrs: &[syn::Attribute]) -> bool {
73+
for attr in attrs {
74+
if attr.path().is_ident("repr") {
75+
if let Meta::List(list) = &attr.meta {
76+
let tokens = list.tokens.to_string();
77+
if tokens.split(',').any(|t| t.trim() == "C") {
78+
return true;
79+
}
80+
}
81+
}
82+
}
83+
false
84+
}
85+
86+
/// Collect field types, skipping generic parameters and primitives
87+
fn collect_field_types<'a>(data: &'a Data) -> Result<Vec<&'a Type>, ()> {
88+
let mut out = Vec::new();
89+
90+
match data {
91+
Data::Struct(data_struct) => {
92+
out = match &data_struct.fields {
93+
Fields::Named(fields) => fields.named.iter().map(|f| &f.ty).collect(),
94+
Fields::Unnamed(fields) => fields.unnamed.iter().map(|f| &f.ty).collect(),
95+
Fields::Unit => Vec::new(),
96+
};
97+
}
98+
Data::Enum(data_enum) => {
99+
if !data_enum
100+
.variants
101+
.iter()
102+
.all(|variant| matches!(variant.fields, Fields::Unit))
103+
{
104+
return Err(());
105+
}
106+
}
107+
Data::Union(_) => return Err(()),
108+
};
109+
110+
Ok(out)
111+
}
112+
113+
// Use doctest to test failed compilations and successful ones
114+
115+
/// ```
116+
/// pub unsafe trait Reloc {}
117+
/// use com_api_concept_macros::Reloc;
118+
///
119+
/// #[derive(Reloc)]
120+
/// #[repr(C)]
121+
/// pub struct RelocType<T> { t : T}
122+
///
123+
/// #[derive(Reloc)]
124+
/// #[repr(C)]
125+
/// pub struct RelocTypeTwoGen<T, D> { t : T, d: D}
126+
/// ```
127+
#[cfg(doctest)]
128+
fn reloc_works_on_generic_types() {}
129+
130+
/// ```
131+
/// pub unsafe trait Reloc {}
132+
/// use com_api_concept_macros::Reloc;
133+
///
134+
/// #[derive(Reloc)]
135+
/// #[repr(C)]
136+
/// enum EnumPlanType {
137+
/// A,
138+
/// B,
139+
/// C
140+
/// }
141+
/// ```
142+
#[cfg(doctest)]
143+
fn reloc_works_on_c_like_enums() {}
144+
145+
/// ```
146+
/// pub unsafe trait Reloc {}
147+
/// use com_api_concept_macros::Reloc;
148+
///
149+
/// #[derive(Reloc)]
150+
/// #[repr(C)]
151+
/// struct StructTag{}
152+
/// ```
153+
#[cfg(doctest)]
154+
fn reloc_works_on_unit() {}
155+
156+
/// ```
157+
/// pub unsafe trait Reloc {}
158+
/// unsafe impl Reloc for u32{}
159+
/// unsafe impl Reloc for u64{}
160+
///
161+
/// use com_api_concept_macros::Reloc;
162+
///
163+
/// #[derive(Reloc)]
164+
/// #[repr(C)]
165+
/// pub struct RelocType<T> { t : T}
166+
///
167+
/// #[derive(Reloc)]
168+
/// #[repr(C)]
169+
/// pub struct StructType {
170+
/// a: u32,
171+
/// b: u64,
172+
/// f: RelocType<u64>
173+
/// }
174+
/// ```
175+
#[cfg(doctest)]
176+
fn reloc_works_on_structs() {}
177+
178+
/// ```
179+
/// pub unsafe trait Reloc {}
180+
/// unsafe impl Reloc for u32{}
181+
/// unsafe impl Reloc for u64{}
182+
///
183+
/// use com_api_concept_macros::Reloc;
184+
///
185+
/// #[derive(Reloc)]
186+
/// #[repr(C)]
187+
/// pub struct RelocType<T> { t : T}
188+
///
189+
/// #[derive(Reloc)]
190+
/// #[repr(C)]
191+
/// pub struct TupleStructType( u32, u64, RelocType<u64> );
192+
/// ```
193+
#[cfg(doctest)]
194+
fn reloc_works_on_tuples() {}
195+
196+
// Failure cases
197+
198+
/// ```compile_fail
199+
/// pub unsafe trait Reloc {}
200+
/// use com_api_concept_macros::Reloc;
201+
///
202+
/// #[derive(Reloc)]
203+
/// #[repr(C)]
204+
/// enum EnumPlanType {
205+
/// A(u32),
206+
/// B,
207+
/// C
208+
/// }
209+
/// ```
210+
#[cfg(doctest)]
211+
fn reloc_fail_on_non_c_like_enums() {}
212+
213+
/// ```compile_fail
214+
/// pub unsafe trait Reloc {}
215+
/// unsafe impl Reloc for u32{}
216+
///
217+
/// use com_api_concept_macros::Reloc;
218+
///
219+
/// #[derive(Reloc)]
220+
/// #[repr(C)]
221+
/// pub struct RelocType<T> { t : T}
222+
///
223+
/// #[derive(Reloc)]
224+
/// #[repr(C)]
225+
/// pub struct StructType {
226+
/// a: u32,
227+
/// b: u64,
228+
/// f: RelocType<u64>
229+
/// }
230+
/// ```
231+
#[cfg(doctest)]
232+
fn reloc_fails_if_member_is_not_reloc_on_struct() {}
233+
234+
/// ```compile_fail
235+
/// pub unsafe trait Reloc {}
236+
/// use com_api_concept_macros::Reloc;
237+
///
238+
/// #[derive(Reloc)]
239+
/// #[repr(C)]
240+
/// pub struct RelocType<T> { t : T}
241+
///
242+
/// #[derive(Reloc)]
243+
/// #[repr(C)]
244+
/// pub struct ContainsNonRelocType(RelocType<u64>);
245+
/// ```
246+
#[cfg(doctest)]
247+
fn reloc_fails_on_generic_types_that_do_not_impl_reloc() {}

score/mw/com/impl/rust/com-api/com-api-concept/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ rust_library(
2323
"//visibility:public", # platform_only
2424
],
2525
deps = [],
26+
proc_macro_deps = [
27+
"//score/mw/com/impl/rust/com-api/com-api-concept-macros",
28+
],
2629
)
2730

2831
rust_test(

score/mw/com/impl/rust/com-api/com-api-concept/reloc.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
/// is unsafe for now. The expectation is that very few users ever need to implement this manually.
2828
pub unsafe trait Reloc {}
2929

30+
pub use com_api_concept_macros::Reloc;
31+
3032
unsafe impl Reloc for () {}
3133
unsafe impl Reloc for u8 {}
3234
unsafe impl Reloc for u16 {}

score/mw/com/impl/rust/com-api/com-api/com_api.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ pub use com_api_concept::{
2222
Publisher, Reloc, Result, Runtime, SampleContainer, SampleMaybeUninit, SampleMut,
2323
ServiceDiscovery, Subscriber, Subscription,
2424
};
25+

0 commit comments

Comments
 (0)