extern crate proc_macro;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{
parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Fields, GenericParam,
Generics, Ident, Type,
};
#[proc_macro_derive(SharedMemCast)]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
add_generic_bounds(&mut input.generics);
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let type_assertions = field_type_assertions(&name, &input.data);
let expanded = quote! {
unsafe impl #impl_generics shared_memory::SharedMemCast for #name #ty_generics #where_clause {
fn assert_receiver_is_shared_mem_cast(&self) {
#type_assertions
}
}
};
expanded.into()
}
fn add_generic_bounds(generics: &mut Generics) {
for param in &mut generics.params {
if let GenericParam::Type(type_param) = param {
type_param
.bounds
.push(parse_quote!(shared_memory::SharedMemCast));
}
}
}
fn field_type_assertions(name: &Ident, data: &Data) -> TokenStream {
match data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) if !fields.named.is_empty() => {
type_assertions_from_types(fields.named.iter().map(|f| &f.ty))
}
Fields::Unnamed(fields) if !fields.unnamed.is_empty() => {
type_assertions_from_types(fields.unnamed.iter().map(|f| &f.ty))
}
_ => {
error(
name.span(),
"Zero-sized types cannot be casted from shared memory",
)
}
},
Data::Enum(data) => {
if data.variants.is_empty() {
return error(
name.span(),
"Empty enums types cannot be casted from shared memory",
);
}
type_assertions_from_types(
data.variants
.iter()
.flat_map(|var| var.fields.iter().map(|f| &f.ty)),
)
}
Data::Union(data) => error(
data.union_token.span(),
"Untagged unions are not supported by SharedMemCast",
),
}
}
fn type_assertions_from_types<'a, I: Iterator<Item = &'a Type>>(types: I) -> TokenStream {
let type_assertions = types.map(|ty| {
quote_spanned! {ty.span() =>
let _: shared_memory::AssertIsSharedMemCast<#ty>;
}
});
quote! {
#(#type_assertions)*
}
}
fn error<T: std::fmt::Display>(span: Span, message: T) -> TokenStream {
syn::Error::new(span, message).to_compile_error()
}