From 8848cd0732a7656a2e0913e63d224618082f21df Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 17 Jun 2026 19:41:11 +0200 Subject: feat(engine-macros): improve flexibility of Reflection macro on generic types --- engine-macros/src/lib.rs | 2 +- engine-macros/src/reflection.rs | 61 +++- engine-macros/src/reflection/default_value.rs | 4 +- engine-macros/src/reflection/enum_impl.rs | 477 +++++++++++++++----------- engine-macros/src/reflection/field.rs | 24 +- engine-macros/src/reflection/options_attr.rs | 37 ++ engine-macros/src/reflection/struct_impl.rs | 105 +++--- engine-macros/src/util.rs | 24 +- 8 files changed, 439 insertions(+), 295 deletions(-) create mode 100644 engine-macros/src/reflection/options_attr.rs (limited to 'engine-macros/src') diff --git a/engine-macros/src/lib.rs b/engine-macros/src/lib.rs index 5d16ad2..deddd15 100644 --- a/engine-macros/src/lib.rs +++ b/engine-macros/src/lib.rs @@ -3,7 +3,7 @@ mod reflection; mod util; -#[proc_macro_derive(Reflection)] +#[proc_macro_derive(Reflection, attributes(reflection))] pub fn reflection_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { reflection::derive(input.into()).into() diff --git a/engine-macros/src/reflection.rs b/engine-macros/src/reflection.rs index 5d40e0c..2180084 100644 --- a/engine-macros/src/reflection.rs +++ b/engine-macros/src/reflection.rs @@ -1,6 +1,9 @@ +use crate::reflection::options_attr::OptionsAttr; + mod default_value; mod enum_impl; mod field; +mod options_attr; mod struct_impl; mod visibility; @@ -8,9 +11,63 @@ pub fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { let input = syn::parse2::(input).unwrap(); + let options = get_options(&input); + match input { - syn::Item::Struct(input) => struct_impl::generate(input), - syn::Item::Enum(input) => enum_impl::generate(input), + syn::Item::Struct(input) => struct_impl::generate(input, options), + syn::Item::Enum(input) => enum_impl::generate(input, options), _ => panic!("Invalid input"), } } + +pub fn get_options(input: &syn::Item) -> OptionsAttr +{ + let (attrs, generic_params) = match &input { + syn::Item::Struct(input) => (input.attrs.as_slice(), &input.generics.params), + syn::Item::Enum(input) => (input.attrs.as_slice(), &input.generics.params), + _ => ( + const { &[] }.as_slice(), + &syn::punctuated::Punctuated::::new(), + ), + }; + + if attrs + .iter() + .filter(|attr| attr.path().is_ident("reflection")) + .count() + > 1 + { + panic!("Multiple 'reflection' attributes are not allowed"); + } + + let options = match attrs + .iter() + .find_map(|attr| { + if attr.path().is_ident("reflection") { + return Some(attr.parse_args::()); + } + + None + }) + .transpose() + { + Ok(attr) => attr.unwrap_or_default(), + Err(err) => { + panic!("{err}"); + } + }; + + if options.impl_with_generics.is_empty() && !generic_params.is_empty() { + panic!(concat!( + "Generic types deriving Reflection must specify which ", + "generics to use in the generated impl(s) using the 'reflection' attribute\n", + "For example: #[reflection(impl_with_generics(, ))]" + )); + } + + if !options.impl_with_generics.is_empty() && generic_params.is_empty() { + panic!("Specifying 'impl_with_generics' for non-generic types is not allowed"); + } + + options +} diff --git a/engine-macros/src/reflection/default_value.rs b/engine-macros/src/reflection/default_value.rs index 567c3b1..6784129 100644 --- a/engine-macros/src/reflection/default_value.rs +++ b/engine-macros/src/reflection/default_value.rs @@ -2,7 +2,7 @@ use quote::quote; pub fn gen_get_default_value_fn( type_ident: &proc_macro2::Ident, - type_generics: &syn::TypeGenerics, + generic_args: Option<&syn::AngleBracketedGenericArguments>, ) -> proc_macro2::TokenStream { quote! { @@ -36,7 +36,7 @@ pub fn gen_get_default_value_fn( } } - (&SpecializationTarget::<#type_ident #type_generics>(std::marker::PhantomData)) + (&SpecializationTarget::<#type_ident #generic_args>(std::marker::PhantomData)) .default_value_fn() } } diff --git a/engine-macros/src/reflection/enum_impl.rs b/engine-macros/src/reflection/enum_impl.rs index 5dbb176..0ce2562 100644 --- a/engine-macros/src/reflection/enum_impl.rs +++ b/engine-macros/src/reflection/enum_impl.rs @@ -1,37 +1,20 @@ use quote::{format_ident, quote, ToTokens}; +use syn::ItemEnum; use crate::reflection::default_value::gen_get_default_value_fn; use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions}; -use crate::util::{find_engine_crate_path, syn_path, SynPathExt}; +use crate::reflection::options_attr::OptionsAttr; +use crate::util::find_engine_crate_path; -const PRIMITIVE_REPRS: &[&'static str] = &[ - "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", - "isize", -]; - -pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream +pub fn generate(input: syn::ItemEnum, options: OptionsAttr) -> proc_macro2::TokenStream { let engine_crate_path = find_engine_crate_path().unwrap(); - let input_ident = &input.ident; - - let mut generics = input.generics; - - for type_param in generics.type_params_mut() { - type_param - .bounds - .push(syn::TypeParamBound::Trait(syn::TraitBound { - paren_token: None, - modifier: syn::TraitBoundModifier::None, - lifetimes: None, - path: engine_crate_path.join(syn_path!(reflection::Reflection)), - })); - } - - let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); - - let variant_lookup_match_arms = - input.variants.iter().enumerate().map(|(index, variant)| { + let variant_lookup_match_arms = input + .variants + .iter() + .enumerate() + .map(|(index, variant)| { let variant_ident = &variant.ident; let pattern = match variant.fields { @@ -43,178 +26,74 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream quote! { #pattern => &enum_reflection.variants[#index] } - }); + }) + .collect::>(); let is_unit_only = input .variants .iter() .all(|variant| matches!(variant.fields, syn::Fields::Unit)); - let mod_name = format_ident!("__engine_private_{input_ident}"); - - let mut has_c_repr = false; - - let mut primitive_repr: Option = None; - - for attr in &input.attrs { - let syn::Meta::List(attr_meta) = &attr.meta else { - continue; - }; - - if !attr_meta.path.is_ident("repr") { - continue; - } - - attr_meta - .parse_nested_meta(|nested_meta| { - let Some(meta_ident) = nested_meta.path.get_ident() else { - return Ok(()); - }; - - if meta_ident == "C" { - has_c_repr = true; - } else if PRIMITIVE_REPRS.contains(&meta_ident.to_string().as_str()) { - primitive_repr = Some(meta_ident.clone()); - } else { - return Err(nested_meta.error(concat!( - "Unsupported representation. ", - "Only C and primitive representations are allowed" - ))); - } + let mod_name = format_ident!("__engine_private_{}", input.ident); - Ok(()) - }) - .unwrap(); - } + let reprs = get_reprs(&input); - if !has_c_repr && primitive_repr.is_none() { + if !reprs.has_c_repr && reprs.primitive_repr.is_none() { panic!("Enums must have a C or primitive representation to derive Reflection"); } - let mod_content = if is_unit_only { - quote! {} - } else { - let variant_fields_structs = - input.variants.iter().map(|variant| { - let fields = variant.fields.iter().map(|field| { - let field_type = &field.ty; - - if let Some(field_ident) = &field.ident { - quote! { pub #field_ident: #field_type } - } else { - quote! { pub #field_type } - } - }); - - let ident = format_ident!("VariantFields{}", variant.ident); - - let generics_phantom_data_elems = generics.params.iter().filter_map( - |generic_param| match generic_param { - syn::GenericParam::Type(type_param) => { - Some(type_param.ident.clone().into_token_stream()) - } - syn::GenericParam::Lifetime(_) => { - unimplemented!(); - } - syn::GenericParam::Const(_const_param) => None, - }, - ); - - let generics_phantom_data = quote! { - std::marker::PhantomData<(#(#generics_phantom_data_elems),*)> - }; - - match variant.fields { - syn::Fields::Named(_) => quote! { - #[repr(C)] - pub struct #ident #impl_generics - #where_clause - { - #(#fields,)* - _pd: #generics_phantom_data - } - }, - syn::Fields::Unnamed(_) => quote! { - #[repr(C)] - pub struct #ident #impl_generics ( - #(#fields,)* - #generics_phantom_data - ) - #where_clause; - }, - syn::Fields::Unit => quote! { - #[repr(C)] - pub struct #ident #impl_generics (#generics_phantom_data) - #where_clause; - }, - } - }); - - let fields_union_fields = input.variants.iter().map(|variant| { - let variant_ident = &variant.ident; - - let variant_fields_struct_ident = - format_ident!("VariantFields{}", variant.ident); - - quote! { - pub #variant_ident: - std::mem::ManuallyDrop<#variant_fields_struct_ident #type_generics> - } - }); - - let discriminant_enum_variants = input.variants.iter().map(|variant| { - let variant_ident = &variant.ident; - - if let Some((_, discriminant)) = &variant.discriminant { - quote! { #variant_ident = #discriminant } - } else { - quote! { #variant_ident } - } - }); - - // If the enum has both a C & primitive repr, the primitive repr must be used - let discriminant_enum_repr = if let Some(primitive_repr) = primitive_repr { - quote! { #primitive_repr } - } else if has_c_repr { - quote! { C } + let mod_content = generate_mod_content(&input, is_unit_only, &reprs); + + let impls: &mut dyn Iterator = + if input.generics.params.is_empty() { + &mut [generate_impls( + &input, + None, + is_unit_only, + &variant_lookup_match_arms, + &mod_name, + &engine_crate_path, + )] + .into_iter() } else { - unreachable!(); + &mut options.impl_with_generics.iter().map(|generic_args| { + generate_impls( + &input, + Some(generic_args), + is_unit_only, + &variant_lookup_match_arms, + &mod_name, + &engine_crate_path, + ) + }) }; - quote! { - #![allow(non_snake_case, dead_code)] - - use super::*; - - #[repr(C)] - pub struct Equivalent #impl_generics - #where_clause - { - pub tag: Discriminant, - pub payload: Fields #type_generics - } - - #[repr(#discriminant_enum_repr)] - pub enum Discriminant - { - #(#discriminant_enum_variants),* - } + quote! { + #[doc(hidden)] + #[allow(non_snake_case)] + mod #mod_name { + #mod_content + } - #[repr(C)] - pub union Fields #impl_generics - #where_clause - { - #(#fields_union_fields),* - } + #(#impls)* + } +} - #(#variant_fields_structs)* - } - }; +fn generate_impls( + input: &syn::ItemEnum, + generic_args: Option<&syn::AngleBracketedGenericArguments>, + is_unit_only: bool, + variant_lookup_match_arms: &[proc_macro2::TokenStream], + mod_name: &proc_macro2::Ident, + engine_crate_path: &syn::Path, +) -> proc_macro2::TokenStream +{ + let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args); let variants = generate_variants( - input_ident, + &input.ident, &input.variants, - &generics, + generic_args, is_unit_only, &engine_crate_path, ); @@ -228,28 +107,46 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream Some(#engine_crate_path::reflection::EnumTaggedUnion { discriminant_layout: std::alloc::Layout::new::<#mod_name::Discriminant>(), discriminant_byte_offset: - std::mem::offset_of!(#mod_name::Equivalent #type_generics, tag), + std::mem::offset_of!(#mod_name::Equivalent #generic_args, tag), fields_layout: - std::alloc::Layout::new::<#mod_name::Fields #type_generics>(), + std::alloc::Layout::new::<#mod_name::Fields #generic_args>(), fields_byte_offset: - std::mem::offset_of!(#mod_name::Equivalent #type_generics, payload), + std::mem::offset_of!(#mod_name::Equivalent #generic_args, payload), }) } }; - let get_default_value_fn = gen_get_default_value_fn(&input_ident, &type_generics); + let generics_type_aliases = input + .generics + .type_params() + .zip( + generic_args + .iter() + .map(|generic_args| &generic_args.args) + .flatten() + .flat_map(|generic_arg| match generic_arg { + syn::GenericArgument::Type(ty) => Some(ty), + _ => None, + }), + ) + .map(|(type_param, generic_arg_type)| { + let type_param_ident = &type_param.ident; - quote! { - #[doc(hidden)] - mod #mod_name { - #mod_content - } + quote! { + type #type_param_ident = #generic_arg_type; + } + }); + + let input_ident = &input.ident; - unsafe impl #impl_generics #engine_crate_path::reflection::Reflection for - #input_ident #type_generics #where_clause + quote! { + unsafe impl #engine_crate_path::reflection::Reflection for + #input_ident #generic_args { const TYPE_REFLECTION: &#engine_crate_path::reflection::Type = &const { + #(#generics_type_aliases)* + #engine_crate_path::reflection::Type::Enum( #engine_crate_path::reflection::Enum { variants: &[#(#variants),*], @@ -263,8 +160,8 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream }; } - unsafe impl #impl_generics #engine_crate_path::reflection::EnumReflectionExt for - #input_ident #type_generics #where_clause + unsafe impl #engine_crate_path::reflection::EnumReflectionExt for + #input_ident #generic_args { fn get_variant_reflection(&self) -> &'static #engine_crate_path::reflection::EnumVariant @@ -286,15 +183,13 @@ pub fn generate(input: syn::ItemEnum) -> proc_macro2::TokenStream fn generate_variants<'a>( input_ident: &proc_macro2::Ident, input_variants: &'a syn::punctuated::Punctuated, - input_generics: &'a syn::Generics, + generic_args: Option<&'a syn::AngleBracketedGenericArguments>, is_unit_only: bool, engine_crate_path: &'a syn::Path, ) -> impl Iterator + use<'a> { let mod_name = format_ident!("__engine_private_{input_ident}"); - let (_, type_generics, _) = input_generics.split_for_impl(); - input_variants.iter().map(move |variant| { let variant_name = syn::LitStr::new(&variant.ident.to_string(), variant.ident.span()); @@ -321,12 +216,12 @@ fn generate_variants<'a>( if let Some(field_ident) = &field.ident { quote! { std::mem::offset_of!( - #mod_name::Equivalent #type_generics, + #mod_name::Equivalent #generic_args, payload.#variant_ident ) + std::mem::offset_of!( #mod_name::#variant_fields_struct_ident - #type_generics, + #generic_args, #field_ident ) } @@ -334,9 +229,6 @@ fn generate_variants<'a>( unreachable!(); } }, - type_reflection_optional: input_generics - .params - .is_empty(), }, ) }, @@ -371,20 +263,17 @@ fn generate_variants<'a>( quote! { std::mem::offset_of!( - #mod_name::Equivalent #type_generics, + #mod_name::Equivalent #generic_args, payload.#variant_ident ) + std::mem::offset_of!( #mod_name::#variant_fields_struct_ident - #type_generics, + #generic_args, #field_index ) } } }, - type_reflection_optional: input_generics - .params - .is_empty(), }, ) }, @@ -449,3 +338,181 @@ fn generate_variants<'a>( } }) } + +fn generate_mod_content( + input: &syn::ItemEnum, + is_unit_only: bool, + reprs: &Reprs, +) -> proc_macro2::TokenStream +{ + if is_unit_only { + return quote! {}; + } + + let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); + + let variant_fields_structs = input.variants.iter().map(|variant| { + let fields = variant.fields.iter().map(|field| { + let field_type = &field.ty; + + if let Some(field_ident) = &field.ident { + quote! { pub #field_ident: #field_type } + } else { + quote! { pub #field_type } + } + }); + + let ident = format_ident!("VariantFields{}", variant.ident); + + let generics_phantom_data_elems = input.generics.params.iter().filter_map( + |generic_param| match generic_param { + syn::GenericParam::Type(type_param) => { + Some(type_param.ident.clone().into_token_stream()) + } + syn::GenericParam::Lifetime(_) => { + unimplemented!(); + } + syn::GenericParam::Const(_const_param) => None, + }, + ); + + let generics_phantom_data = quote! { + std::marker::PhantomData<(#(#generics_phantom_data_elems),*)> + }; + + match variant.fields { + syn::Fields::Named(_) => quote! { + #[repr(C)] + pub struct #ident #impl_generics + #where_clause + { + #(#fields,)* + _pd: #generics_phantom_data + } + }, + syn::Fields::Unnamed(_) => quote! { + #[repr(C)] + pub struct #ident #impl_generics ( + #(#fields,)* + #generics_phantom_data + ) + #where_clause; + }, + syn::Fields::Unit => quote! { + #[repr(C)] + pub struct #ident #impl_generics (#generics_phantom_data) + #where_clause; + }, + } + }); + + let fields_union_fields = input.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + + let variant_fields_struct_ident = format_ident!("VariantFields{}", variant.ident); + + quote! { + pub #variant_ident: + std::mem::ManuallyDrop<#variant_fields_struct_ident #type_generics> + } + }); + + let discriminant_enum_variants = input.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + + if let Some((_, discriminant)) = &variant.discriminant { + quote! { #variant_ident = #discriminant } + } else { + quote! { #variant_ident } + } + }); + + // If the enum has both a C & primitive repr, the primitive repr must be used + let discriminant_enum_repr = if let Some(primitive_repr) = &reprs.primitive_repr { + quote! { #primitive_repr } + } else if reprs.has_c_repr { + quote! { C } + } else { + unreachable!(); + }; + + quote! { + #![allow(non_snake_case, dead_code)] + + use super::*; + + #[repr(C)] + pub struct Equivalent #impl_generics + #where_clause + { + pub tag: Discriminant, + pub payload: Fields #type_generics + } + + #[repr(#discriminant_enum_repr)] + pub enum Discriminant + { + #(#discriminant_enum_variants),* + } + + #[repr(C)] + pub union Fields #impl_generics + #where_clause + { + #(#fields_union_fields),* + } + + #(#variant_fields_structs)* + } +} + +fn get_reprs(input: &ItemEnum) -> Reprs +{ + let mut has_c_repr = false; + let mut primitive_repr: Option = None; + + for attr in &input.attrs { + let syn::Meta::List(attr_meta) = &attr.meta else { + continue; + }; + + if !attr_meta.path.is_ident("repr") { + continue; + } + + attr_meta + .parse_nested_meta(|nested_meta| { + let Some(meta_ident) = nested_meta.path.get_ident() else { + return Ok(()); + }; + + if meta_ident == "C" { + has_c_repr = true; + } else if PRIMITIVE_REPRS.contains(&meta_ident.to_string().as_str()) { + primitive_repr = Some(meta_ident.clone()); + } else { + return Err(nested_meta.error(concat!( + "Unsupported representation. ", + "Only C and primitive representations are allowed" + ))); + } + + Ok(()) + }) + .unwrap(); + } + + Reprs { has_c_repr, primitive_repr } +} + +#[derive(Debug)] +struct Reprs +{ + has_c_repr: bool, + primitive_repr: Option, +} + +const PRIMITIVE_REPRS: &[&'static str] = &[ + "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", + "isize", +]; diff --git a/engine-macros/src/reflection/field.rs b/engine-macros/src/reflection/field.rs index 95671c3..7c50f47 100644 --- a/engine-macros/src/reflection/field.rs +++ b/engine-macros/src/reflection/field.rs @@ -6,7 +6,6 @@ pub struct ReflectionFieldGenOptions<'a> { pub field_vis_override: Option, pub gen_get_byte_offset: &'a dyn Fn(&syn::Field) -> proc_macro2::TokenStream, - pub type_reflection_optional: bool, } pub fn generate( @@ -34,11 +33,8 @@ pub fn generate( let field_reflection_vis = generate_visibility(field_vis, &engine_crate_path); - let get_type_fn_body = if options.type_reflection_optional { - gen_get_optional_type_reflection(field_type, engine_crate_path) - } else { - gen_get_mandatory_type_reflection(field_type, engine_crate_path) - }; + let get_type_fn_body = + { gen_get_optional_type_reflection(field_type, engine_crate_path) }; quote! { #engine_crate_path::reflection::Field { @@ -102,19 +98,3 @@ fn gen_get_optional_type_reflection( .field_type_reflection() } } - -fn gen_get_mandatory_type_reflection( - field_type: &syn::Type, - engine_crate_path: &syn::Path, -) -> proc_macro2::TokenStream -{ - quote! { - fn field_type_reflection() - -> &'static #engine_crate_path::reflection::Type - { - T::type_reflection() - } - - Some(field_type_reflection::<#field_type>()) - } -} diff --git a/engine-macros/src/reflection/options_attr.rs b/engine-macros/src/reflection/options_attr.rs new file mode 100644 index 0000000..231b969 --- /dev/null +++ b/engine-macros/src/reflection/options_attr.rs @@ -0,0 +1,37 @@ +#[derive(Debug, Default)] +pub struct OptionsAttr +{ + pub impl_with_generics: + syn::punctuated::Punctuated, +} + +impl syn::parse::Parse for OptionsAttr +{ + fn parse(input: syn::parse::ParseStream) -> syn::Result + { + let params = + input.parse_terminated(|param| param.parse::(), syn::Token![,])?; + + let mut impl_with_generics = syn::punctuated::Punctuated::new(); + + for param in params { + let param = param.require_list()?; + + if !param.path.is_ident("impl_with_generics") { + return Err(syn::Error::new_spanned( + param, + "Unrecognized attribute parameter", + )); + } + + impl_with_generics = param.parse_args_with( + syn::punctuated::Punctuated::< + syn::AngleBracketedGenericArguments, + syn::Token![,], + >::parse_terminated, + )?; + } + + Ok(Self { impl_with_generics }) + } +} diff --git a/engine-macros/src/reflection/struct_impl.rs b/engine-macros/src/reflection/struct_impl.rs index ce1086b..58046fe 100644 --- a/engine-macros/src/reflection/struct_impl.rs +++ b/engine-macros/src/reflection/struct_impl.rs @@ -2,62 +2,85 @@ use quote::quote; use crate::reflection::default_value::gen_get_default_value_fn; use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions}; -use crate::util::{find_engine_crate_path, syn_path, SynPathExt}; +use crate::reflection::options_attr::OptionsAttr; +use crate::util::find_engine_crate_path; -pub fn generate(input: syn::ItemStruct) -> proc_macro2::TokenStream +pub fn generate(input: syn::ItemStruct, options: OptionsAttr) + -> proc_macro2::TokenStream { let engine_crate_path = find_engine_crate_path().unwrap(); - let input_ident = input.ident; - - let type_reflection_optional = input.generics.params.is_empty(); + if input.generics.params.is_empty() { + return gen_impl(&input, None, &engine_crate_path); + } - let mut generics = input.generics; + let impls = options + .impl_with_generics + .into_iter() + .map(|generic_args| gen_impl(&input, Some(&generic_args), &engine_crate_path)); - for type_param in generics.type_params_mut() { - type_param - .bounds - .push(syn::TypeParamBound::Trait(syn::TraitBound { - paren_token: None, - modifier: syn::TraitBoundModifier::None, - lifetimes: None, - path: engine_crate_path.join(syn_path!(reflection::Reflection)), - })); + quote! { + #(#impls)* } +} - let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); - - let fields = input - .fields - .into_iter() - .enumerate() - .map(|(field_index, field)| { - generate_field( - &field, - field_index, - &engine_crate_path, - ReflectionFieldGenOptions { - field_vis_override: None, - gen_get_byte_offset: &|field| { - if let Some(field_ident) = &field.ident { - quote! { std::mem::offset_of!(Self, #field_ident) } - } else { - quote! { std::mem::offset_of!(Self, #field_index) } - } - }, - type_reflection_optional, +fn gen_impl( + input: &syn::ItemStruct, + generic_args: Option<&syn::AngleBracketedGenericArguments>, + engine_crate_path: &syn::Path, +) -> proc_macro2::TokenStream +{ + let fields = input.fields.iter().enumerate().map(|(field_index, field)| { + generate_field( + &field, + field_index, + &engine_crate_path, + ReflectionFieldGenOptions { + field_vis_override: None, + gen_get_byte_offset: &|field| { + if let Some(field_ident) = &field.ident { + quote! { std::mem::offset_of!(Self, #field_ident) } + } else { + quote! { std::mem::offset_of!(Self, #field_index) } + } }, - ) - }); + }, + ) + }); + + let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args); + + let input_ident = &input.ident; - let get_default_value_fn = gen_get_default_value_fn(&input_ident, &type_generics); + let generics_type_aliases = input + .generics + .type_params() + .zip( + generic_args + .iter() + .map(|generic_args| &generic_args.args) + .flatten() + .flat_map(|generic_arg| match generic_arg { + syn::GenericArgument::Type(ty) => Some(ty), + _ => None, + }), + ) + .map(|(type_param, generic_arg_type)| { + let type_param_ident = &type_param.ident; + + quote! { + type #type_param_ident = #generic_arg_type; + } + }); quote! { - unsafe impl #impl_generics #engine_crate_path::reflection::Reflection for - #input_ident #type_generics #where_clause + unsafe impl #engine_crate_path::reflection::Reflection for + #input_ident #generic_args { const TYPE_REFLECTION: &#engine_crate_path::reflection::Type = &const { + #(#generics_type_aliases)* + #engine_crate_path::reflection::Type::Struct( #engine_crate_path::reflection::Struct { fields: &[ diff --git a/engine-macros/src/util.rs b/engine-macros/src/util.rs index 327c517..515b066 100644 --- a/engine-macros/src/util.rs +++ b/engine-macros/src/util.rs @@ -7,8 +7,8 @@ macro_rules! syn_path { ::syn::Path { leading_colon: None, segments: ::syn::punctuated::Punctuated::from_iter([ - $crate::util::syn_path_segment!($first_segment), - $($crate::util::syn_path_segment!($segment),)* + syn_path_segment!($first_segment), + $(syn_path_segment!($segment),)* ]) } }; @@ -26,9 +26,6 @@ macro_rules! syn_path_segment { }; } -pub(crate) use syn_path; -pub(crate) use syn_path_segment; - pub fn find_engine_crate_path() -> Option { let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?; @@ -66,20 +63,3 @@ pub fn syn_path_to_string(path: &syn::Path) -> String output } - -pub trait SynPathExt -{ - fn join(&self, tail: Self) -> Self; -} - -impl SynPathExt for syn::Path -{ - fn join(&self, tail: Self) -> Self - { - let mut new = self.clone(); - - new.segments.extend(tail.segments); - - new - } -} -- cgit v1.2.3-18-g5258