Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "google_benchmark", version = "1.9.4")
bazel_dep(name = "rules_rust", version = "0.61.0")
bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2")
bazel_dep(name = "score_crates", version = "0.0.5", repo_name = "crate_index")
bazel_dep(name = "score_crates", version = "0.0.6", repo_name = "crate_index")

bazel_dep(name = "boost.program_options", version = "1.87.0")
bazel_dep(name = "download_utils", version = "1.0.1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ use com_api::{
Consumer, Interface, OfferedProducer, Producer, Publisher, Reloc, Runtime, Subscriber,
};

#[derive(Debug)]
#[derive(Debug, Reloc)]
#[repr(C)]
pub struct Tire {}
unsafe impl Reloc for Tire {}

#[derive(Debug)]
#[derive(Debug, Reloc)]
#[repr(C)]
pub struct Exhaust {}
unsafe impl Reloc for Exhaust {}

pub struct VehicleInterface {}

Expand Down
31 changes: 31 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD
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 score/mw/com/impl/rust/com-api/com-api-concept-macros/lib.rs
Copy link
Contributor

@bharatGoswami8 bharatGoswami8 Jan 8, 2026

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isnt lib.rs a general Rust convention for lib crates ? Bit confused.

Copy link
Contributor

@bharatGoswami8 bharatGoswami8 Jan 12, 2026

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.

Copy link
Contributor Author

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.rs as all other codebase in score uses lib.rs

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() {}
3 changes: 3 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api-concept/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ rust_library(
"//visibility:public", # platform_only
],
deps = [],
proc_macro_deps = [
"//score/mw/com/impl/rust/com-api/com-api-concept-macros",
],
)

rust_test(
Expand Down
2 changes: 2 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api-concept/reloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
/// is unsafe for now. The expectation is that very few users ever need to implement this manually.
pub unsafe trait Reloc {}

pub use com_api_concept_macros::Reloc;

unsafe impl Reloc for () {}
unsafe impl Reloc for u8 {}
unsafe impl Reloc for u16 {}
Expand Down
1 change: 1 addition & 0 deletions score/mw/com/impl/rust/com-api/com-api/com_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ pub use com_api_concept::{
Publisher, Reloc, Result, Runtime, SampleContainer, SampleMaybeUninit, SampleMut,
ServiceDiscovery, Subscriber, Subscription,
};

Loading