summaryrefslogtreecommitdiff
path: root/engine-macros/src/reflection
diff options
context:
space:
mode:
Diffstat (limited to 'engine-macros/src/reflection')
-rw-r--r--engine-macros/src/reflection/enum_impl.rs369
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",
-];