diff options
Diffstat (limited to 'engine-macros')
| -rw-r--r-- | engine-macros/src/reflection/enum_impl.rs | 369 |
1 files changed, 60 insertions, 309 deletions
diff --git a/engine-macros/src/reflection/enum_impl.rs b/engine-macros/src/reflection/enum_impl.rs index f46b977..1cc8f0a 100644 --- a/engine-macros/src/reflection/enum_impl.rs +++ b/engine-macros/src/reflection/enum_impl.rs @@ -1,8 +1,6 @@ -use quote::{format_ident, quote, ToTokens}; -use syn::ItemEnum; +use quote::{format_ident, quote}; use crate::reflection::default_value::gen_get_default_value_fn; -use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions}; use crate::reflection::options_attr::OptionsAttr; use crate::util::find_engine_crate_path; @@ -34,16 +32,6 @@ pub fn generate(input: syn::ItemEnum, options: OptionsAttr) -> proc_macro2::Toke .iter() .all(|variant| matches!(variant.fields, syn::Fields::Unit)); - let mod_name = format_ident!("__engine_private_{}", input.ident); - - let reprs = get_reprs(&input); - - 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 = generate_mod_content(&input, is_unit_only, &reprs); - let impls: &mut dyn Iterator<Item = proc_macro2::TokenStream> = if input.generics.params.is_empty() { &mut [generate_impls( @@ -51,7 +39,6 @@ pub fn generate(input: syn::ItemEnum, options: OptionsAttr) -> proc_macro2::Toke None, is_unit_only, &variant_lookup_match_arms, - &mod_name, &engine_crate_path, )] .into_iter() @@ -62,19 +49,12 @@ pub fn generate(input: syn::ItemEnum, options: OptionsAttr) -> proc_macro2::Toke Some(generic_args), is_unit_only, &variant_lookup_match_arms, - &mod_name, &engine_crate_path, ) }) }; quote! { - #[doc(hidden)] - #[allow(non_snake_case)] - mod #mod_name { - #mod_content - } - #(#impls)* } } @@ -84,37 +64,12 @@ fn generate_impls( 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.variants, - generic_args, - is_unit_only, - &engine_crate_path, - ); - - let tagged_union = if is_unit_only { - quote! { - None - } - } else { - quote! { - 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 #generic_args, tag), - fields_layout: - std::alloc::Layout::new::<#mod_name::Fields #generic_args>(), - fields_byte_offset: - std::mem::offset_of!(#mod_name::Equivalent #generic_args, payload), - }) - } - }; + let variants = generate_variants(&input.variants, &engine_crate_path); let generics_type_aliases = input .generics @@ -139,16 +94,6 @@ fn generate_impls( let input_ident = &input.ident; - let discriminant_size = if is_unit_only { - quote! { - std::mem::size_of::<Self>() - } - } else { - quote! { - std::mem::size_of::<#mod_name::Discriminant>() - } - }; - quote! { unsafe impl #engine_crate_path::reflection::Reflection for #input_ident #generic_args @@ -161,8 +106,6 @@ fn generate_impls( #engine_crate_path::reflection::Enum { variants: &[#(#variants),*], is_unit_only: #is_unit_only, - discriminant_size: #discriminant_size, - tagged_union: #tagged_union, get_default_value: || { #get_default_value_fn }, @@ -203,51 +146,23 @@ fn generate_impls( } fn generate_variants<'a>( - input_ident: &proc_macro2::Ident, input_variants: &'a syn::punctuated::Punctuated<syn::Variant, syn::Token![,]>, - generic_args: Option<&'a syn::AngleBracketedGenericArguments>, - is_unit_only: bool, engine_crate_path: &'a syn::Path, ) -> impl Iterator<Item = proc_macro2::TokenStream> + use<'a> { - let mod_name = format_ident!("__engine_private_{input_ident}"); - input_variants.iter().map(move |variant| { let variant_name = syn::LitStr::new(&variant.ident.to_string(), variant.ident.span()); - let variant_ident = &variant.ident; - - let variant_fields_struct_ident = format_ident!("VariantFields{}", variant.ident); - let fields = match &variant.fields { syn::Fields::Unit => quote! { None }, syn::Fields::Named(named_fields) => { let fields = named_fields.named.iter().enumerate().map( |(variant_field_index, variant_field)| { - generate_field( + generate_enum_variant_field( variant_field, variant_field_index, &engine_crate_path, - ReflectionFieldGenOptions { - // enum variant fields are always public - field_vis_override: Some(syn::Visibility::Public( - <syn::Token![pub]>::default(), - )), - gen_get_byte_offset: &|field| { - if let Some(field_ident) = &field.ident { - quote! { - std::mem::offset_of!( - #mod_name::#variant_fields_struct_ident - #generic_args, - #field_ident - ) - } - } else { - unreachable!(); - } - }, - }, ) }, ); @@ -261,34 +176,10 @@ fn generate_variants<'a>( syn::Fields::Unnamed(unnamed_fields) => { let fields = unnamed_fields.unnamed.iter().enumerate().map( |(variant_field_index, variant_field)| { - generate_field( + generate_enum_variant_field( variant_field, variant_field_index, &engine_crate_path, - ReflectionFieldGenOptions { - // enum variant fields are always public - field_vis_override: Some(syn::Visibility::Public( - <syn::Token![pub]>::default(), - )), - gen_get_byte_offset: &|field| { - if let Some(_) = &field.ident { - unreachable!() - } else { - let field_index = - proc_macro2::Literal::usize_unsuffixed( - variant_field_index, - ); - - quote! { - std::mem::offset_of!( - #mod_name::#variant_fields_struct_ident - #generic_args, - #field_index - ) - } - } - }, - }, ) }, ); @@ -301,46 +192,6 @@ fn generate_variants<'a>( } }; - let discriminant = match variant.fields { - syn::Fields::Unit if is_unit_only => { - quote! { - let mut buf = [0u8; std::mem::size_of::<i128>()]; - - // Self or any of it's type parameters might have a Drop impl and - // since that can not be const evaluated, the discriminant value has - // to be wrapped in ManuallyDrop - let discriminant = std::mem::ManuallyDrop::new(Self::#variant_ident); - - unsafe { - std::ptr::copy_nonoverlapping( - (&raw const discriminant).cast::<u8>(), - buf.as_mut_ptr(), - std::mem::size_of::<Self>(), - ); - } - - buf - } - } - _ => { - quote! { - let mut buf = [0u8; std::mem::size_of::<i128>()]; - - let discriminant = #mod_name::Discriminant::#variant_ident; - - unsafe { - std::ptr::copy_nonoverlapping( - (&raw const discriminant).cast::<u8>(), - buf.as_mut_ptr(), - std::mem::size_of::<#mod_name::Discriminant>(), - ); - } - - buf - } - } - }; - let variant_field_vars = variant.fields.iter().enumerate().map( |(variant_field_index, variant_field)| { let var_ident = match variant_field.ident.as_ref() { @@ -415,9 +266,6 @@ fn generate_variants<'a>( #engine_crate_path::reflection::EnumVariant { name: #variant_name, fields: #fields, - discriminant: #engine_crate_path::reflection::EnumDiscriminant { - buf: { #discriminant } - }, try_write_new_to: |dst, fields| { use std::any::Any; use #engine_crate_path::reflection::EnumVariantWriteNewToError; @@ -443,180 +291,83 @@ fn generate_variants<'a>( }) } -fn generate_mod_content( - input: &syn::ItemEnum, - is_unit_only: bool, - reprs: &Reprs, +pub fn generate_enum_variant_field( + field: &syn::Field, + field_index: usize, + engine_crate_path: &syn::Path, ) -> proc_macro2::TokenStream { - if is_unit_only { - return quote! {}; - } + let field_ident = &field.ident; - let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); + let field_type = &field.ty; - let variant_fields_structs = input.variants.iter().map(|variant| { - let fields = variant.fields.iter().map(|field| { - let field_type = &field.ty; + let field_name = if let Some(field_ident) = field_ident { + let field_name = syn::LitStr::new(&field_ident.to_string(), field_ident.span()); - 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 } + quote! { Some(#field_name) } } else { - unreachable!(); + quote! { None } }; + let get_type_fn_body = + { gen_get_optional_type_reflection(field_type, engine_crate_path) }; + quote! { - #![allow(non_snake_case, dead_code)] + #engine_crate_path::reflection::EnumVariantField { + name: #field_name, + index: #field_index, + type_id: std::any::TypeId::of::<#field_type>(), + get_type_name: #engine_crate_path::reflection::FnWithDebug::new(|| { + std::any::type_name::<#field_type>() + }), + get_type: #engine_crate_path::reflection::FnWithDebug::new(|| { + #get_type_fn_body + }), + } + } +} - use super::*; +fn gen_get_optional_type_reflection( + field_type: &syn::Type, + engine_crate_path: &syn::Path, +) -> proc_macro2::TokenStream +{ + quote! { + struct SpecializationTarget<Field>(std::marker::PhantomData<Field>); - #[repr(C)] - pub struct Equivalent #impl_generics - #where_clause + trait FieldHasReflection { - pub tag: Discriminant, - pub payload: Fields #type_generics + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type>; } - #[repr(#discriminant_enum_repr)] - pub enum Discriminant + trait FieldDoesNotHaveReflection { - #(#discriminant_enum_variants),* + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type>; } - #[repr(C)] - pub union Fields #impl_generics - #where_clause + impl<Field> FieldDoesNotHaveReflection for &SpecializationTarget<Field> { - #(#fields_union_fields),* + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type> + { + None + } } - #(#variant_fields_structs)* - } -} - -fn get_reprs(input: &ItemEnum) -> Reprs -{ - let mut has_c_repr = false; - let mut primitive_repr: Option<proc_macro2::Ident> = None; - - for attr in &input.attrs { - let syn::Meta::List(attr_meta) = &attr.meta else { - continue; - }; - - if !attr_meta.path.is_ident("repr") { - continue; + impl<Field> FieldHasReflection for SpecializationTarget<Field> + where + Field: #engine_crate_path::reflection::Reflection + { + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type> + { + Some(Field::type_reflection()) + } } - 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(); + (&SpecializationTarget::<#field_type>(std::marker::PhantomData)) + .field_type_reflection() } - - Reprs { has_c_repr, primitive_repr } -} - -#[derive(Debug)] -struct Reprs -{ - has_c_repr: bool, - primitive_repr: Option<proc_macro2::Ident>, } - -const PRIMITIVE_REPRS: &[&'static str] = &[ - "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", - "isize", -]; |
