From 1e3ecec4e5e6b90eb0979228d06392e0a035ad4d Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Wed, 25 Jan 2023 12:30:05 -0500 Subject: [PATCH 1/9] add Reflect and Typed bounds when reflecting on a struct that has generic types --- .../bevy_reflect_derive/src/derive_data.rs | 6 +++-- .../bevy_reflect_derive/src/impls/enums.rs | 1 + .../bevy_reflect_derive/src/impls/structs.rs | 25 +++++++++++++++--- .../src/impls/tuple_structs.rs | 26 ++++++++++++++++--- .../bevy_reflect_derive/src/impls/typed.rs | 17 ++++++++++-- .../bevy_reflect_derive/src/impls/values.rs | 2 ++ .../bevy_reflect_derive/src/registration.rs | 17 ++++++++++-- crates/bevy_reflect/src/lib.rs | 26 +++++++++++++++++++ 8 files changed, 108 insertions(+), 12 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 92b2035e1939d..5e11deafbd5a4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -7,7 +7,7 @@ use quote::quote; use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant}; +use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant}; pub(crate) enum ReflectDerive<'a> { Struct(ReflectStruct<'a>), @@ -322,6 +322,7 @@ impl<'a> ReflectMeta<'a> { &self.bevy_reflect_path, self.traits.idents(), self.generics, + &Vec::default(), None, ) } @@ -350,7 +351,7 @@ impl<'a> ReflectStruct<'a> { /// Returns the `GetTypeRegistration` impl as a `TokenStream`. /// /// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method - pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + pub fn get_type_registration(&self, fields: &Vec) -> proc_macro2::TokenStream { let reflect_path = self.meta.bevy_reflect_path(); crate::registration::impl_get_type_registration( @@ -358,6 +359,7 @@ impl<'a> ReflectStruct<'a> { reflect_path, self.meta.traits().idents(), self.meta.generics(), + fields, Some(&self.serialization_denylist), ) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index ce163092fac5a..a81971801c707 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -76,6 +76,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let typed_impl = impl_typed( enum_name, reflect_enum.meta().generics(), + &Vec::default(), quote! { let variants = [#(#variant_info),*]; let info = #info_generator; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index eefffd3ec0506..3890015bcf336 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -91,6 +91,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { let typed_impl = impl_typed( struct_name, reflect_struct.meta().generics(), + &field_types, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -99,16 +100,34 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { bevy_reflect_path, ); - let get_type_registration_impl = reflect_struct.get_type_registration(); + let get_type_registration_impl = reflect_struct.get_type_registration(&field_types); let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); + // Add Reflect bound for each active field + let mut where_reflect_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if !field_types.is_empty() { + quote! {where} + } else { + quote! {} + }; + where_reflect_clause.extend(quote! { + // TODO: had to add #bevy_reflect_path::Typed to get the test to compile, + // presumably because of this: + // #[inline] + // fn get_type_info(&self) -> &'static bevy_reflect::TypeInfo { + // ::type_info() + // } + #(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)* + }); + TokenStream::from(quote! { #get_type_registration_impl #typed_impl - impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_reflect_clause { fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match name { #(#field_names => #fqoption::Some(&self.#field_idents),)* @@ -160,7 +179,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { } } - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index da187a3ca8a0b..25138508a4560 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -11,7 +11,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); let struct_name = reflect_struct.meta().type_name(); - let get_type_registration_impl = reflect_struct.get_type_registration(); let field_idents = reflect_struct .active_fields() @@ -21,6 +20,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); + let get_type_registration_impl = reflect_struct.get_type_registration(&field_types); + let hash_fn = reflect_struct .meta() .traits() @@ -75,6 +76,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let typed_impl = impl_typed( struct_name, reflect_struct.meta().generics(), + &field_types, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -86,12 +88,30 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); + // Add Reflect bound for each active field + let mut where_reflect_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if !field_types.is_empty() { + quote! {where} + } else { + quote! {} + }; + where_reflect_clause.extend(quote! { + // TODO: had to add #bevy_reflect_path::Typed to get the test to compile, + // presumably because of this: + // #[inline] + // fn get_type_info(&self) -> &'static bevy_reflect::TypeInfo { + // ::type_info() + // } + #(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)* + }); + TokenStream::from(quote! { #get_type_registration_impl #typed_impl - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_reflect_clause { fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match index { #(#field_indices => #fqoption::Some(&self.#field_idents),)* @@ -122,7 +142,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { } } - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index 76d36f5869bb1..559d29bca7c1f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -1,10 +1,11 @@ use proc_macro2::Ident; use quote::quote; -use syn::{Generics, Path}; +use syn::{Generics, Path, Type}; pub(crate) fn impl_typed( type_name: &Ident, generics: &Generics, + field_types: &Vec, generator: proc_macro2::TokenStream, bevy_reflect_path: &Path, ) -> proc_macro2::TokenStream { @@ -28,8 +29,20 @@ pub(crate) fn impl_typed( let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Add Typed bound for each active field + let mut where_typed_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if !field_types.is_empty() { + quote! {where} + } else { + quote! {} + }; + where_typed_clause.extend(quote! { + #(#field_types: #bevy_reflect_path::Typed,)* + }); + quote! { - impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_typed_clause { fn type_info() -> &'static #bevy_reflect_path::TypeInfo { #static_generator } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 5d87027d1a25a..f2cc17ce75447 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -21,9 +21,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #[cfg(not(feature = "documentation"))] let with_docs: Option = None; + let field_types = Vec::default(); let typed_impl = impl_typed( type_name, meta.generics(), + &field_types, quote! { let info = #bevy_reflect_path::ValueInfo::new::() #with_docs; #bevy_reflect_path::TypeInfo::Value(info) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 456e4152ca85f..55ca7f7a2926d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -3,7 +3,7 @@ use bit_set::BitSet; use proc_macro2::Ident; use quote::quote; -use syn::{Generics, Path}; +use syn::{Generics, Path, Type}; /// Creates the `GetTypeRegistration` impl for the given type data. pub(crate) fn impl_get_type_registration( @@ -11,6 +11,7 @@ pub(crate) fn impl_get_type_registration( bevy_reflect_path: &Path, registration_data: &[Ident], generics: &Generics, + field_types: &Vec, serialization_denylist: Option<&BitSet>, ) -> proc_macro2::TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); @@ -22,9 +23,21 @@ pub(crate) fn impl_get_type_registration( } }); + // Add Typed bound for each active field + let mut where_reflect_typed_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if !field_types.is_empty() { + quote! {where} + } else { + quote! {} + }; + where_reflect_typed_clause.extend(quote! { + #(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)* + }); + quote! { #[allow(unused_mut)] - impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_typed_clause { fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type()); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index d0bf81058442e..92c7e5ef7f47d 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -158,6 +158,32 @@ mod tests { assert_eq!(foo.a, 123); } + #[test] + fn reflect_struct_generics() { + #[derive(Reflect)] + struct Foo { + a: T, + } + + let mut foo = Foo::{ + a: 42, + }; + + let a = *foo.get_field::("a").unwrap(); + assert_eq!(a, 42); + + *foo.get_field_mut::("a").unwrap() += 1; + assert_eq!(foo.a, 43); + + // patch Foo with a dynamic struct + let mut dynamic_struct = DynamicStruct::default(); + dynamic_struct.insert("a", 123u32); + dynamic_struct.insert("should_be_ignored", 456); + + foo.apply(&dynamic_struct); + assert_eq!(foo.a, 123); + } + #[test] fn reflect_map() { #[derive(Reflect, Hash)] From b52b07ac284b54b1d04d28e7a34ba4be45608a34 Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Wed, 25 Jan 2023 13:20:22 -0500 Subject: [PATCH 2/9] apply rustfmt --- crates/bevy_reflect/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 92c7e5ef7f47d..8891d331c98ac 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -165,9 +165,7 @@ mod tests { a: T, } - let mut foo = Foo::{ - a: 42, - }; + let mut foo = Foo:: { a: 42 }; let a = *foo.get_field::("a").unwrap(); assert_eq!(a, 42); From 76c1c9300cb88520352cd7281b84588a09a004e7 Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Wed, 25 Jan 2023 17:29:20 -0500 Subject: [PATCH 3/9] create where_clause utility function, handle ignored generics --- .../bevy_reflect_derive/src/derive_data.rs | 31 +++++++++++++++++-- .../bevy_reflect_derive/src/impls/enums.rs | 6 +++- .../bevy_reflect_derive/src/impls/structs.rs | 29 +++++++---------- .../src/impls/tuple_structs.rs | 29 +++++++---------- .../bevy_reflect_derive/src/impls/typed.rs | 21 ++++++------- .../bevy_reflect_derive/src/impls/values.rs | 2 ++ .../bevy_reflect_derive/src/registration.rs | 21 ++++++------- .../bevy_reflect_derive/src/utility.rs | 28 +++++++++++++++-- crates/bevy_reflect/src/lib.rs | 12 +++++-- .../tests/reflect_derive/generics.fail.rs | 15 +++++++++ .../tests/reflect_derive/generics.fail.stderr | 14 +++++++++ .../tests/reflect_derive/lifetimes.fail.rs | 9 ------ .../reflect_derive/lifetimes.fail.stderr | 13 -------- 13 files changed, 145 insertions(+), 85 deletions(-) create mode 100644 crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs create mode 100644 crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr delete mode 100644 crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.rs delete mode 100644 crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.stderr diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 5e11deafbd5a4..a0ab969a7abad 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -81,6 +81,18 @@ pub(crate) struct ReflectEnum<'a> { variants: Vec>, } +impl<'a> ReflectEnum<'a> { + /// Get a collection of types which are exposed to the reflection API + pub fn active_types(&self) -> Vec { + Vec::default() + } + + /// Get a collection of types which are ignored by the reflection API + pub fn ignored_types(&self) -> Vec { + Vec::default() + } +} + /// Represents a field on a struct or tuple struct. pub(crate) struct StructField<'a> { /// The raw field. @@ -323,6 +335,7 @@ impl<'a> ReflectMeta<'a> { self.traits.idents(), self.generics, &Vec::default(), + &Vec::default(), None, ) } @@ -351,7 +364,11 @@ impl<'a> ReflectStruct<'a> { /// Returns the `GetTypeRegistration` impl as a `TokenStream`. /// /// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method - pub fn get_type_registration(&self, fields: &Vec) -> proc_macro2::TokenStream { + pub fn get_type_registration( + &self, + field_types: &Vec, + active_types: &Vec, + ) -> proc_macro2::TokenStream { let reflect_path = self.meta.bevy_reflect_path(); crate::registration::impl_get_type_registration( @@ -359,7 +376,8 @@ impl<'a> ReflectStruct<'a> { reflect_path, self.meta.traits().idents(), self.meta.generics(), - fields, + field_types, + active_types, Some(&self.serialization_denylist), ) } @@ -380,6 +398,15 @@ impl<'a> ReflectStruct<'a> { .filter(move |field| field.attrs.ignore.is_active()) } + /// Get a collection of types which are ignored by the reflection API + pub fn ignored_types(&self) -> Vec { + self.fields + .iter() + .filter(move |field| field.attrs.ignore.is_ignored()) + .map(|field| field.data.ty.clone()) + .collect::>() + } + /// Get an iterator of fields which are ignored by the reflection API pub fn ignored_fields(&self) -> impl Iterator> { self.fields diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index a81971801c707..ca5cf6e581b45 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -15,6 +15,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let ref_index = Ident::new("__index_param", Span::call_site()); let ref_value = Ident::new("__value_param", Span::call_site()); + let ignored_types = reflect_enum.ignored_types(); + let field_types = reflect_enum.active_types(); + let EnumImpls { variant_info, enum_field, @@ -76,7 +79,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let typed_impl = impl_typed( enum_name, reflect_enum.meta().generics(), - &Vec::default(), + &field_types, + &ignored_types, quote! { let variants = [#(#variant_info),*]; let info = #info_generator; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index 3890015bcf336..b68674e61e214 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -1,5 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::impl_typed; +use crate::utility::generic_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::{quote, ToTokens}; @@ -34,6 +35,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) }) .collect::>(); + let ignored_types = reflect_struct.ignored_types(); let field_types = reflect_struct.active_types(); let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); @@ -92,6 +94,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { struct_name, reflect_struct.meta().generics(), &field_types, + &ignored_types, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -100,27 +103,19 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { bevy_reflect_path, ); - let get_type_registration_impl = reflect_struct.get_type_registration(&field_types); + let get_type_registration_impl = + reflect_struct.get_type_registration(&field_types, &ignored_types); let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); // Add Reflect bound for each active field - let mut where_reflect_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if !field_types.is_empty() { - quote! {where} - } else { - quote! {} - }; - where_reflect_clause.extend(quote! { - // TODO: had to add #bevy_reflect_path::Typed to get the test to compile, - // presumably because of this: - // #[inline] - // fn get_type_info(&self) -> &'static bevy_reflect::TypeInfo { - // ::type_info() - // } - #(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)* - }); + let where_reflect_clause = generic_where_clause( + where_clause, + &field_types, + quote! { #bevy_reflect_path::Reflect }, + &ignored_types, + quote! { 'static + std::marker::Send + std::marker::Sync }, + ); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index 25138508a4560..e5ba6dbfe20cb 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -1,5 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::impl_typed; +use crate::utility::generic_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::{quote, ToTokens}; @@ -16,11 +17,13 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { .active_fields() .map(|field| Member::Unnamed(Index::from(field.index))) .collect::>(); + let ignored_types = reflect_struct.ignored_types(); let field_types = reflect_struct.active_types(); let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); - let get_type_registration_impl = reflect_struct.get_type_registration(&field_types); + let get_type_registration_impl = + reflect_struct.get_type_registration(&field_types, &ignored_types); let hash_fn = reflect_struct .meta() @@ -77,6 +80,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { struct_name, reflect_struct.meta().generics(), &field_types, + &ignored_types, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -89,22 +93,13 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { reflect_struct.meta().generics().split_for_impl(); // Add Reflect bound for each active field - let mut where_reflect_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if !field_types.is_empty() { - quote! {where} - } else { - quote! {} - }; - where_reflect_clause.extend(quote! { - // TODO: had to add #bevy_reflect_path::Typed to get the test to compile, - // presumably because of this: - // #[inline] - // fn get_type_info(&self) -> &'static bevy_reflect::TypeInfo { - // ::type_info() - // } - #(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)* - }); + let where_reflect_clause = generic_where_clause( + where_clause, + &field_types, + quote! { #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed }, + &ignored_types, + quote! { 'static + std::marker::Send + std::marker::Sync }, + ); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index 559d29bca7c1f..60e7945249a89 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -1,3 +1,4 @@ +use crate::utility::generic_where_clause; use proc_macro2::Ident; use quote::quote; use syn::{Generics, Path, Type}; @@ -6,6 +7,7 @@ pub(crate) fn impl_typed( type_name: &Ident, generics: &Generics, field_types: &Vec, + ignored_types: &Vec, generator: proc_macro2::TokenStream, bevy_reflect_path: &Path, ) -> proc_macro2::TokenStream { @@ -30,19 +32,16 @@ pub(crate) fn impl_typed( let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); // Add Typed bound for each active field - let mut where_typed_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if !field_types.is_empty() { - quote! {where} - } else { - quote! {} - }; - where_typed_clause.extend(quote! { - #(#field_types: #bevy_reflect_path::Typed,)* - }); + let where_reflect_clause = generic_where_clause( + where_clause, + &field_types, + quote! { #bevy_reflect_path::Reflect }, + &ignored_types, + quote! { 'static + std::marker::Send + std::marker::Sync }, + ); quote! { - impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_typed_clause { + impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_reflect_clause { fn type_info() -> &'static #bevy_reflect_path::TypeInfo { #static_generator } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index f2cc17ce75447..4867b8a110c7b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -22,10 +22,12 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { let with_docs: Option = None; let field_types = Vec::default(); + let ignored_types = Vec::default(); let typed_impl = impl_typed( type_name, meta.generics(), &field_types, + &ignored_types, quote! { let info = #bevy_reflect_path::ValueInfo::new::() #with_docs; #bevy_reflect_path::TypeInfo::Value(info) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 55ca7f7a2926d..1d6cda4bd737a 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,5 +1,6 @@ //! Contains code related specifically to Bevy's type registration. +use crate::utility::generic_where_clause; use bit_set::BitSet; use proc_macro2::Ident; use quote::quote; @@ -12,6 +13,7 @@ pub(crate) fn impl_get_type_registration( registration_data: &[Ident], generics: &Generics, field_types: &Vec, + ignored_types: &Vec, serialization_denylist: Option<&BitSet>, ) -> proc_macro2::TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); @@ -24,20 +26,17 @@ pub(crate) fn impl_get_type_registration( }); // Add Typed bound for each active field - let mut where_reflect_typed_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if !field_types.is_empty() { - quote! {where} - } else { - quote! {} - }; - where_reflect_typed_clause.extend(quote! { - #(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)* - }); + let where_reflect_clause = generic_where_clause( + where_clause, + &field_types, + quote! { #bevy_reflect_path::Reflect }, + &ignored_types, + quote! { 'static + std::marker::Send + std::marker::Sync }, + ); quote! { #[allow(unused_mut)] - impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_typed_clause { + impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_clause { fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type()); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index d9e44754c9f54..771c9d6f1aee9 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -3,8 +3,9 @@ use crate::field_attributes::ReflectIgnoreBehavior; use bevy_macro_utils::BevyManifest; use bit_set::BitSet; -use proc_macro2::{Ident, Span}; -use syn::{Member, Path}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::quote; +use syn::{Member, Path, Type, WhereClause}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -59,6 +60,29 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { ) } +pub(crate) fn generic_where_clause( + where_clause: Option<&WhereClause>, + active_types: &Vec, + active_trait_bounds: TokenStream, + ignored_types: &Vec, + ignored_trait_bounds: TokenStream, +) -> TokenStream { + let mut generic_where_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if !(active_types.is_empty() && ignored_types.is_empty()) { + quote! {where} + } else { + quote! {} + }; + generic_where_clause.extend(quote! { + #(#active_types: #active_trait_bounds,)* + }); + generic_where_clause.extend(quote! { + #(#ignored_types: #ignored_trait_bounds,)* + }); + generic_where_clause +} + impl Default for ResultSifter { fn default() -> Self { Self { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 8891d331c98ac..804a178236396 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -161,11 +161,19 @@ mod tests { #[test] fn reflect_struct_generics() { #[derive(Reflect)] - struct Foo { + struct Foo { a: T, + #[reflect(ignore)] + _b: U, } - let mut foo = Foo:: { a: 42 }; + // Type that doesn't implement Reflect + struct NoReflect(f32); + + let mut foo = Foo:: { + a: 42, + _b: NoReflect(1.0), + }; let a = *foo.get_field::("a").unwrap(); assert_eq!(a, 42); diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs new file mode 100644 index 0000000000000..50b9d331b8af4 --- /dev/null +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs @@ -0,0 +1,15 @@ +use bevy_reflect::Reflect; + +#[derive(Reflect)] +struct Foo { + a: T, +} + +// Type that doesn't implement Reflect +struct NoReflect(f32); + +fn main() { + let mut foo = Foo:: { a: NoReflect(42.0) }; + // foo doesn't implement Reflect because NoReflect doesn't implement Reflect + foo.get_field::("a").unwrap(); +} \ No newline at end of file diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr new file mode 100644 index 0000000000000..6ad1e4639b3b5 --- /dev/null +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr @@ -0,0 +1,14 @@ +error[E0599]: no method named `get_field` found for struct `Foo` in the current scope + --> tests/reflect_derive/generics.fail.rs:14:9 + | +4 | struct Foo { + | ------------- method `get_field` not found for this struct +... +14 | foo.get_field::("a").unwrap(); + | ^^^^^^^^^ method not found in `Foo` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `get_field`, perhaps you need to implement one of them: + candidate #1: `GetField` + candidate #2: `GetTupleField` + candidate #3: `GetTupleStructField` diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.rs b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.rs deleted file mode 100644 index 4a97e5d8278a2..0000000000000 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.rs +++ /dev/null @@ -1,9 +0,0 @@ -use bevy_reflect::Reflect; - -#[derive(Reflect)] -struct Foo<'a> { - #[reflect(ignore)] - value: &'a str, -} - -fn main() {} diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.stderr b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.stderr deleted file mode 100644 index 156fb6cda17d7..0000000000000 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/lifetimes.fail.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0478]: lifetime bound not satisfied - --> tests/reflect_derive/lifetimes.fail.rs:3:10 - | -3 | #[derive(Reflect)] - | ^^^^^^^ - | -note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> tests/reflect_derive/lifetimes.fail.rs:4:12 - | -4 | struct Foo<'a> { - | ^^ - = note: but lifetime parameter must outlive the static lifetime - = note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info) From d59789baf918c0c0634f7d9bc513943d07c72741 Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Wed, 25 Jan 2023 17:37:48 -0500 Subject: [PATCH 4/9] nit --- crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs | 4 ++-- crates/bevy_reflect/bevy_reflect_derive/src/registration.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index 60e7945249a89..bca2c19a39627 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -34,9 +34,9 @@ pub(crate) fn impl_typed( // Add Typed bound for each active field let where_reflect_clause = generic_where_clause( where_clause, - &field_types, + field_types, quote! { #bevy_reflect_path::Reflect }, - &ignored_types, + ignored_types, quote! { 'static + std::marker::Send + std::marker::Sync }, ); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 1d6cda4bd737a..37730f40bd66c 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -28,9 +28,9 @@ pub(crate) fn impl_get_type_registration( // Add Typed bound for each active field let where_reflect_clause = generic_where_clause( where_clause, - &field_types, + field_types, quote! { #bevy_reflect_path::Reflect }, - &ignored_types, + ignored_types, quote! { 'static + std::marker::Send + std::marker::Sync }, ); From b88b04f2688e0ee7163030acc0550fcab6b82c52 Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Thu, 26 Jan 2023 20:09:51 -0500 Subject: [PATCH 5/9] address comments --- .../bevy_reflect_derive/Cargo.toml | 2 +- .../bevy_reflect_derive/src/derive_data.rs | 133 ++++++++++++++---- .../bevy_reflect_derive/src/fq_std.rs | 14 ++ .../bevy_reflect_derive/src/impls/enums.rs | 28 +++- .../bevy_reflect_derive/src/impls/structs.rs | 25 ++-- .../src/impls/tuple_structs.rs | 25 ++-- .../bevy_reflect_derive/src/impls/typed.rs | 22 +-- .../bevy_reflect_derive/src/impls/values.rs | 11 +- .../bevy_reflect_derive/src/registration.rs | 21 +-- .../bevy_reflect_derive/src/utility.rs | 44 +++++- crates/bevy_reflect/src/lib.rs | 32 ----- .../tests/reflect_derive/generics.fail.rs | 2 +- .../tests/reflect_derive/generics.fail.stderr | 37 +++-- .../reflect_derive/generics_structs.pass.rs | 29 ++++ 14 files changed, 299 insertions(+), 126 deletions(-) create mode 100644 crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs diff --git a/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml b/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml index 24f0d0863f922..09f046a77cb96 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml +++ b/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml @@ -19,7 +19,7 @@ documentation = [] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.9.0" } -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0", features = ["full", "extra-traits"] } proc-macro2 = "1.0" quote = "1.0" uuid = { version = "1.1", features = ["v4"] } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index a0ab969a7abad..fa10ce41f1cda 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -2,7 +2,9 @@ use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; use crate::utility::members_to_serialization_denylist; use bit_set::BitSet; +use proc_macro2::TokenStream; use quote::quote; +use std::collections::HashSet; use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; use syn::punctuated::Punctuated; @@ -81,18 +83,6 @@ pub(crate) struct ReflectEnum<'a> { variants: Vec>, } -impl<'a> ReflectEnum<'a> { - /// Get a collection of types which are exposed to the reflection API - pub fn active_types(&self) -> Vec { - Vec::default() - } - - /// Get a collection of types which are ignored by the reflection API - pub fn ignored_types(&self) -> Vec { - Vec::default() - } -} - /// Represents a field on a struct or tuple struct. pub(crate) struct StructField<'a> { /// The raw field. @@ -328,14 +318,27 @@ impl<'a> ReflectMeta<'a> { } /// Returns the `GetTypeRegistration` impl as a `TokenStream`. - pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + /// + /// * `active_types`: types corresponding to active fields in the object (used to add specific trait bounds) + /// * `ignored_types`: types corresponding to ignored fields in the object (used to add specific trait bounds) + /// * `active_trait_bounds`: trait bounds to provide for the active types + /// * `ignored_trait_bounds`: trait bounds to provide for the ignored types + pub fn get_type_registration( + &self, + active_types: &[Type], + ignored_types: &[Type], + active_trait_bounds: &TokenStream, + ignored_trait_bounds: &TokenStream, + ) -> proc_macro2::TokenStream { crate::registration::impl_get_type_registration( self.type_name, &self.bevy_reflect_path, self.traits.idents(), self.generics, - &Vec::default(), - &Vec::default(), + active_types, + ignored_types, + active_trait_bounds, + ignored_trait_bounds, None, ) } @@ -363,11 +366,18 @@ impl<'a> ReflectStruct<'a> { /// Returns the `GetTypeRegistration` impl as a `TokenStream`. /// - /// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method + /// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method + /// + /// * `active_types`: types corresponding to active fields in the struct (used to add specific trait bounds) + /// * `ignored_types`: types corresponding to ignored fields in the struct (used to add specific trait bounds) + /// * `active_trait_bounds`: trait bounds to provide for the active types + /// * `ignored_trait_bounds`: trait bounds to provide for the ignored types pub fn get_type_registration( &self, - field_types: &Vec, - active_types: &Vec, + active_types: &[Type], + ignored_types: &[Type], + active_trait_bounds: &TokenStream, + ignored_trait_bounds: &TokenStream, ) -> proc_macro2::TokenStream { let reflect_path = self.meta.bevy_reflect_path(); @@ -376,42 +386,44 @@ impl<'a> ReflectStruct<'a> { reflect_path, self.meta.traits().idents(), self.meta.generics(), - field_types, active_types, + ignored_types, + active_trait_bounds, + ignored_trait_bounds, Some(&self.serialization_denylist), ) } - /// Get a collection of types which are exposed to the reflection API + /// Get a collection of unique types which are exposed to the reflection API pub fn active_types(&self) -> Vec { - self.fields - .iter() - .filter(move |field| field.attrs.ignore.is_active()) + let dedup: HashSet = self + .active_fields() .map(|field| field.data.ty.clone()) - .collect::>() + .collect(); + dedup.into_iter().collect() } /// Get an iterator of fields which are exposed to the reflection API pub fn active_fields(&self) -> impl Iterator> { self.fields .iter() - .filter(move |field| field.attrs.ignore.is_active()) + .filter(|field| field.attrs.ignore.is_active()) } - /// Get a collection of types which are ignored by the reflection API + /// Get a collection of unique types which are ignored by the reflection API pub fn ignored_types(&self) -> Vec { - self.fields - .iter() - .filter(move |field| field.attrs.ignore.is_ignored()) + let dedup: HashSet = self + .ignored_fields() .map(|field| field.data.ty.clone()) - .collect::>() + .collect(); + dedup.into_iter().collect() } /// Get an iterator of fields which are ignored by the reflection API pub fn ignored_fields(&self) -> impl Iterator> { self.fields .iter() - .filter(move |field| field.attrs.ignore.is_ignored()) + .filter(|field| field.attrs.ignore.is_ignored()) } /// The complete set of fields in this struct. @@ -439,4 +451,63 @@ impl<'a> ReflectEnum<'a> { pub fn variants(&self) -> &[EnumVariant<'a>] { &self.variants } + + /// Get an iterator of fields which are exposed to the reflection API + pub fn active_fields(&self) -> impl Iterator> { + self.variants() + .iter() + .flat_map(|variant| variant.active_fields()) + } + + /// Get a collection of unique types which are exposed to the reflection API + pub fn active_types(&self) -> Vec { + let dedup: HashSet = self + .active_fields() + .map(|field| field.data.ty.clone()) + .collect(); + dedup.into_iter().collect() + } + + /// Get an iterator of fields which are ignored by the reflection API + pub fn ignored_fields(&self) -> impl Iterator> { + self.variants() + .iter() + .flat_map(|variant| variant.ignored_fields()) + } + + /// Get a collection of unique types which are ignored to the reflection API + pub fn ignored_types(&self) -> Vec { + let dedup: HashSet = self + .ignored_fields() + .map(|field| field.data.ty.clone()) + .collect(); + dedup.into_iter().collect() + } +} + +impl<'a> EnumVariant<'a> { + /// Get an iterator of fields which are exposed to the reflection API + #[allow(dead_code)] + pub fn active_fields(&self) -> impl Iterator> { + self.fields() + .iter() + .filter(move |field| field.attrs.ignore.is_active()) + } + + /// Get an iterator of fields which are ignored by the reflection API + #[allow(dead_code)] + pub fn ignored_fields(&self) -> impl Iterator> { + self.fields() + .iter() + .filter(move |field| field.attrs.ignore.is_ignored()) + } + + /// The complete set of fields in this variant. + #[allow(dead_code)] + pub fn fields(&self) -> &[StructField<'a>] { + match &self.fields { + EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields, + EnumVariantFields::Unit => &[], + } + } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs b/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs index 7a4e8e78bdc16..788aa675efd38 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs @@ -39,6 +39,8 @@ pub(crate) struct FQClone; pub(crate) struct FQDefault; pub(crate) struct FQOption; pub(crate) struct FQResult; +pub(crate) struct FQSend; +pub(crate) struct FQSync; impl ToTokens for FQAny { fn to_tokens(&self, tokens: &mut TokenStream) { @@ -75,3 +77,15 @@ impl ToTokens for FQResult { quote!(::core::result::Result).to_tokens(tokens); } } + +impl ToTokens for FQSend { + fn to_tokens(&self, tokens: &mut TokenStream) { + quote!(::core::marker::Send).to_tokens(tokens); + } +} + +impl ToTokens for FQSync { + fn to_tokens(&self, tokens: &mut TokenStream) { + quote!(::core::marker::Sync).to_tokens(tokens); + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index ca5cf6e581b45..443bf5f5e0f5e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,7 +1,8 @@ use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; -use crate::fq_std::{FQAny, FQBox, FQOption, FQResult}; +use crate::fq_std::{FQAny, FQBox, FQOption, FQResult, FQSend, FQSync}; use crate::impls::impl_typed; +use crate::utility::extend_where_clause; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::quote; @@ -15,8 +16,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let ref_index = Ident::new("__index_param", Span::call_site()); let ref_value = Ident::new("__value_param", Span::call_site()); - let ignored_types = reflect_enum.ignored_types(); let field_types = reflect_enum.active_types(); + let ignored_types = reflect_enum.ignored_types(); + let active_trait_bounds = quote! { #bevy_reflect_path::FromReflect }; + let ignored_trait_bounds = quote! { #FQAny + #FQSend + #FQSync + Default }; let EnumImpls { variant_info, @@ -81,6 +84,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { reflect_enum.meta().generics(), &field_types, &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, quote! { let variants = [#(#variant_info),*]; let info = #info_generator; @@ -89,16 +94,29 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { bevy_reflect_path, ); - let get_type_registration_impl = reflect_enum.meta().get_type_registration(); + let get_type_registration_impl = reflect_enum.meta().get_type_registration( + &field_types, + &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, + ); let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().generics().split_for_impl(); + let where_reflect_clause = extend_where_clause( + where_clause, + &field_types, + &active_trait_bounds, + &ignored_types, + &ignored_trait_bounds, + ); + TokenStream::from(quote! { #get_type_registration_impl #typed_impl - impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_reflect_clause { fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match self { #(#enum_field,)* @@ -182,7 +200,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } - impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index b68674e61e214..fa4ec99978720 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -1,6 +1,6 @@ -use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; +use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult, FQSend, FQSync}; use crate::impls::impl_typed; -use crate::utility::generic_where_clause; +use crate::utility::extend_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::{quote, ToTokens}; @@ -35,8 +35,10 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) }) .collect::>(); - let ignored_types = reflect_struct.ignored_types(); let field_types = reflect_struct.active_types(); + let ignored_types = reflect_struct.ignored_types(); + let active_trait_bounds = quote! { #bevy_reflect_path::Reflect }; + let ignored_trait_bounds = quote! { #FQAny + #FQSend + #FQSync }; let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); @@ -95,6 +97,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { reflect_struct.meta().generics(), &field_types, &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -103,18 +107,21 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { bevy_reflect_path, ); - let get_type_registration_impl = - reflect_struct.get_type_registration(&field_types, &ignored_types); + let get_type_registration_impl = reflect_struct.get_type_registration( + &field_types, + &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, + ); let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); - // Add Reflect bound for each active field - let where_reflect_clause = generic_where_clause( + let where_reflect_clause = extend_where_clause( where_clause, &field_types, - quote! { #bevy_reflect_path::Reflect }, + &active_trait_bounds, &ignored_types, - quote! { 'static + std::marker::Send + std::marker::Sync }, + &ignored_trait_bounds, ); TokenStream::from(quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index e5ba6dbfe20cb..b0a25ce6ff117 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -1,6 +1,6 @@ -use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; +use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult, FQSend, FQSync}; use crate::impls::impl_typed; -use crate::utility::generic_where_clause; +use crate::utility::extend_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::{quote, ToTokens}; @@ -17,13 +17,19 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { .active_fields() .map(|field| Member::Unnamed(Index::from(field.index))) .collect::>(); - let ignored_types = reflect_struct.ignored_types(); let field_types = reflect_struct.active_types(); + let ignored_types = reflect_struct.ignored_types(); + let active_trait_bounds = quote! { #bevy_reflect_path::Reflect }; + let ignored_trait_bounds = quote! { #FQAny + #FQSend + #FQSync }; let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); - let get_type_registration_impl = - reflect_struct.get_type_registration(&field_types, &ignored_types); + let get_type_registration_impl = reflect_struct.get_type_registration( + &field_types, + &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, + ); let hash_fn = reflect_struct .meta() @@ -81,6 +87,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { reflect_struct.meta().generics(), &field_types, &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -92,13 +100,12 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); - // Add Reflect bound for each active field - let where_reflect_clause = generic_where_clause( + let where_reflect_clause = extend_where_clause( where_clause, &field_types, - quote! { #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed }, + &active_trait_bounds, &ignored_types, - quote! { 'static + std::marker::Send + std::marker::Sync }, + &ignored_trait_bounds, ); TokenStream::from(quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index bca2c19a39627..f45915512881d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -1,16 +1,18 @@ -use crate::utility::generic_where_clause; -use proc_macro2::Ident; +use crate::utility::extend_where_clause; +use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{Generics, Path, Type}; pub(crate) fn impl_typed( type_name: &Ident, generics: &Generics, - field_types: &Vec, - ignored_types: &Vec, - generator: proc_macro2::TokenStream, + active_types: &[Type], + ignored_types: &[Type], + active_trait_bounds: &TokenStream, + ignored_trait_bounds: &TokenStream, + generator: TokenStream, bevy_reflect_path: &Path, -) -> proc_macro2::TokenStream { +) -> TokenStream { let is_generic = !generics.params.is_empty(); let static_generator = if is_generic { @@ -32,12 +34,12 @@ pub(crate) fn impl_typed( let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); // Add Typed bound for each active field - let where_reflect_clause = generic_where_clause( + let where_reflect_clause = extend_where_clause( where_clause, - field_types, - quote! { #bevy_reflect_path::Reflect }, + active_types, + active_trait_bounds, ignored_types, - quote! { 'static + std::marker::Send + std::marker::Sync }, + ignored_trait_bounds, ); quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 4867b8a110c7b..87b7e27db5620 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -23,11 +23,15 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { let field_types = Vec::default(); let ignored_types = Vec::default(); + let active_trait_bounds = quote! {}; + let ignored_trait_bounds = quote! {}; let typed_impl = impl_typed( type_name, meta.generics(), &field_types, &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, quote! { let info = #bevy_reflect_path::ValueInfo::new::() #with_docs; #bevy_reflect_path::TypeInfo::Value(info) @@ -36,7 +40,12 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { ); let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); - let get_type_registration_impl = meta.get_type_registration(); + let get_type_registration_impl = meta.get_type_registration( + &field_types, + &ignored_types, + &active_trait_bounds, + &ignored_trait_bounds, + ); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 37730f40bd66c..b013399cdadf7 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,8 +1,8 @@ //! Contains code related specifically to Bevy's type registration. -use crate::utility::generic_where_clause; +use crate::utility::extend_where_clause; use bit_set::BitSet; -use proc_macro2::Ident; +use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{Generics, Path, Type}; @@ -12,10 +12,12 @@ pub(crate) fn impl_get_type_registration( bevy_reflect_path: &Path, registration_data: &[Ident], generics: &Generics, - field_types: &Vec, - ignored_types: &Vec, + active_types: &[Type], + ignored_types: &[Type], + active_trait_bounds: &TokenStream, + ignored_trait_bounds: &TokenStream, serialization_denylist: Option<&BitSet>, -) -> proc_macro2::TokenStream { +) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let serialization_data = serialization_denylist.map(|denylist| { let denylist = denylist.into_iter(); @@ -25,13 +27,12 @@ pub(crate) fn impl_get_type_registration( } }); - // Add Typed bound for each active field - let where_reflect_clause = generic_where_clause( + let where_reflect_clause = extend_where_clause( where_clause, - field_types, - quote! { #bevy_reflect_path::Reflect }, + active_types, + active_trait_bounds, ignored_types, - quote! { 'static + std::marker::Send + std::marker::Sync }, + ignored_trait_bounds, ); quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 771c9d6f1aee9..050d414276dd9 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -60,12 +60,44 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { ) } -pub(crate) fn generic_where_clause( +/// Extends the `where` clause in reflection with any additional bounds needed. +/// +/// * `where_clause`: existing `where` clause present on the object to be derived +/// * `active_types`: any types that will be reflected and need an extra trait bound +/// * `active_trait_bounds`: trait bounds to add to the active types +/// * `ignored_types`: any types that won't be reflected and need an extra trait bound +/// * `ignored_trait_bounds`: trait bounds to add to the ignored types +/// +/// +/// This is mostly used to add additional bounds to reflected objects with generic types. +/// For reflection purposes, we usually have: +/// * `active_trait_bounds: Reflect` +/// * `ignored_trait_bounds: Any + Send + Sync` +/// +/// For example, the struct: +/// ```rust +/// #[derive(Reflect)] +/// struct Foo { +/// a: T, +/// #[reflect(ignore)] +/// b: U +/// } +/// ``` +/// will have active types: `[T]` and ignored types: `[U]` +/// +/// will yield the following `where` clause: +/// ```ignore +/// where +/// T: Reflect, // active_trait_bounds +/// U: Any + Send + Sync, // ignored_trait_bounds +/// ``` +/// +pub(crate) fn extend_where_clause( where_clause: Option<&WhereClause>, - active_types: &Vec, - active_trait_bounds: TokenStream, - ignored_types: &Vec, - ignored_trait_bounds: TokenStream, + active_types: &[Type], + active_trait_bounds: &TokenStream, + ignored_types: &[Type], + ignored_trait_bounds: &TokenStream, ) -> TokenStream { let mut generic_where_clause = if where_clause.is_some() { quote! {#where_clause} @@ -76,8 +108,6 @@ pub(crate) fn generic_where_clause( }; generic_where_clause.extend(quote! { #(#active_types: #active_trait_bounds,)* - }); - generic_where_clause.extend(quote! { #(#ignored_types: #ignored_trait_bounds,)* }); generic_where_clause diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 804a178236396..d0bf81058442e 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -158,38 +158,6 @@ mod tests { assert_eq!(foo.a, 123); } - #[test] - fn reflect_struct_generics() { - #[derive(Reflect)] - struct Foo { - a: T, - #[reflect(ignore)] - _b: U, - } - - // Type that doesn't implement Reflect - struct NoReflect(f32); - - let mut foo = Foo:: { - a: 42, - _b: NoReflect(1.0), - }; - - let a = *foo.get_field::("a").unwrap(); - assert_eq!(a, 42); - - *foo.get_field_mut::("a").unwrap() += 1; - assert_eq!(foo.a, 43); - - // patch Foo with a dynamic struct - let mut dynamic_struct = DynamicStruct::default(); - dynamic_struct.insert("a", 123u32); - dynamic_struct.insert("should_be_ignored", 456); - - foo.apply(&dynamic_struct); - assert_eq!(foo.a, 123); - } - #[test] fn reflect_map() { #[derive(Reflect, Hash)] diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs index 50b9d331b8af4..964d784d354d0 100644 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs @@ -9,7 +9,7 @@ struct Foo { struct NoReflect(f32); fn main() { - let mut foo = Foo:: { a: NoReflect(42.0) }; + let mut foo: Box = Box::new(Foo:: { a: NoReflect(42.0) }); // foo doesn't implement Reflect because NoReflect doesn't implement Reflect foo.get_field::("a").unwrap(); } \ No newline at end of file diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr index 6ad1e4639b3b5..f59304dbd4e38 100644 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr @@ -1,14 +1,31 @@ -error[E0599]: no method named `get_field` found for struct `Foo` in the current scope +error[E0599]: no method named `get_field` found for struct `Box<(dyn Reflect + 'static)>` in the current scope --> tests/reflect_derive/generics.fail.rs:14:9 | -4 | struct Foo { - | ------------- method `get_field` not found for this struct -... 14 | foo.get_field::("a").unwrap(); - | ^^^^^^^^^ method not found in `Foo` + | ^^^^^^^^^ method not found in `Box<(dyn Reflect + 'static)>` + +error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied + --> tests/reflect_derive/generics.fail.rs:12:37 + | +12 | let mut foo: Box = Box::new(Foo:: { a: NoReflect(42.0) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect` | - = help: items from traits can only be used if the trait is implemented and in scope - = note: the following traits define an item `get_field`, perhaps you need to implement one of them: - candidate #1: `GetField` - candidate #2: `GetTupleField` - candidate #3: `GetTupleStructField` + = help: the following other types implement trait `Reflect`: + &'static Path + () + (A, B) + (A, B, C) + (A, B, C, D) + (A, B, C, D, E) + (A, B, C, D, E, F) + (A, B, C, D, E, F, G) + and $N others +note: required for `Foo` to implement `Reflect` + --> tests/reflect_derive/generics.fail.rs:3:10 + | +3 | #[derive(Reflect)] + | ^^^^^^^ +4 | struct Foo { + | ^^^^^^ + = note: required for the cast from `Foo` to the object type `dyn Reflect` + = note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs new file mode 100644 index 0000000000000..6416bfb32e5fe --- /dev/null +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs @@ -0,0 +1,29 @@ +use bevy_reflect::{Reflect, GetField}; + +#[derive(Reflect)] +struct Foo { + a: T, + #[reflect(ignore)] + _b: U, + + // check that duplicate types only result in one trait bound + _c: T, + // check that when a type is both an active and inactive type, both trait bounds are used + _d: U, + + #[reflect(ignore)] + _e: S, +} + + +fn main() { + let foo = Foo:: { + a: 1, + _b: 2, + _c: 3, + _d: 4, + _e: 5.0, + }; + + let _ = *foo.get_field::("a").unwrap(); +} \ No newline at end of file From e50062b9fd19c3c43ed3752b8ca3de5d3bc69a1c Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Thu, 26 Jan 2023 20:13:30 -0500 Subject: [PATCH 6/9] fix clippy --- crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs | 1 + crates/bevy_reflect/bevy_reflect_derive/src/registration.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index f45915512881d..41ddd5cd9131f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -3,6 +3,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{Generics, Path, Type}; +#[allow(clippy::too_many_arguments)] pub(crate) fn impl_typed( type_name: &Ident, generics: &Generics, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index b013399cdadf7..b32fa99d0f32e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -7,6 +7,7 @@ use quote::quote; use syn::{Generics, Path, Type}; /// Creates the `GetTypeRegistration` impl for the given type data. +#[allow(clippy::too_many_arguments)] pub(crate) fn impl_get_type_registration( type_name: &Ident, bevy_reflect_path: &Path, From b7c0c8bb1810df5b6dc65942d6e9590b9e0991b8 Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Thu, 26 Jan 2023 20:34:19 -0500 Subject: [PATCH 7/9] remove de-duplication --- .../bevy_reflect_derive/Cargo.toml | 2 +- .../bevy_reflect_derive/src/derive_data.rs | 33 +++++++------------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml b/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml index 09f046a77cb96..24f0d0863f922 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml +++ b/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml @@ -19,7 +19,7 @@ documentation = [] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.9.0" } -syn = { version = "1.0", features = ["full", "extra-traits"] } +syn = { version = "1.0", features = ["full"] } proc-macro2 = "1.0" quote = "1.0" uuid = { version = "1.1", features = ["v4"] } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index fa10ce41f1cda..8ca12218c51c5 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -4,7 +4,6 @@ use crate::utility::members_to_serialization_denylist; use bit_set::BitSet; use proc_macro2::TokenStream; use quote::quote; -use std::collections::HashSet; use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; use syn::punctuated::Punctuated; @@ -394,13 +393,11 @@ impl<'a> ReflectStruct<'a> { ) } - /// Get a collection of unique types which are exposed to the reflection API + /// Get a collection of types which are exposed to the reflection API pub fn active_types(&self) -> Vec { - let dedup: HashSet = self - .active_fields() + self.active_fields() .map(|field| field.data.ty.clone()) - .collect(); - dedup.into_iter().collect() + .collect() } /// Get an iterator of fields which are exposed to the reflection API @@ -410,13 +407,11 @@ impl<'a> ReflectStruct<'a> { .filter(|field| field.attrs.ignore.is_active()) } - /// Get a collection of unique types which are ignored by the reflection API + /// Get a collection of types which are ignored by the reflection API pub fn ignored_types(&self) -> Vec { - let dedup: HashSet = self - .ignored_fields() + self.ignored_fields() .map(|field| field.data.ty.clone()) - .collect(); - dedup.into_iter().collect() + .collect() } /// Get an iterator of fields which are ignored by the reflection API @@ -459,13 +454,11 @@ impl<'a> ReflectEnum<'a> { .flat_map(|variant| variant.active_fields()) } - /// Get a collection of unique types which are exposed to the reflection API + /// Get a collection of types which are exposed to the reflection API pub fn active_types(&self) -> Vec { - let dedup: HashSet = self - .active_fields() + self.active_fields() .map(|field| field.data.ty.clone()) - .collect(); - dedup.into_iter().collect() + .collect() } /// Get an iterator of fields which are ignored by the reflection API @@ -475,13 +468,11 @@ impl<'a> ReflectEnum<'a> { .flat_map(|variant| variant.ignored_fields()) } - /// Get a collection of unique types which are ignored to the reflection API + /// Get a collection of types which are ignored to the reflection API pub fn ignored_types(&self) -> Vec { - let dedup: HashSet = self - .ignored_fields() + self.ignored_fields() .map(|field| field.data.ty.clone()) - .collect(); - dedup.into_iter().collect() + .collect() } } From a953884a172208d885bfdccdd5b08925d84503a3 Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Thu, 26 Jan 2023 20:41:50 -0500 Subject: [PATCH 8/9] fix doc --- crates/bevy_reflect/bevy_reflect_derive/src/utility.rs | 2 +- .../tests/reflect_derive/generics_structs.pass.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 050d414276dd9..92d2462001554 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -75,7 +75,7 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { /// * `ignored_trait_bounds: Any + Send + Sync` /// /// For example, the struct: -/// ```rust +/// ```ignore /// #[derive(Reflect)] /// struct Foo { /// a: T, diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs index 6416bfb32e5fe..505cf6dabd9d5 100644 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics_structs.pass.rs @@ -6,7 +6,7 @@ struct Foo { #[reflect(ignore)] _b: U, - // check that duplicate types only result in one trait bound + // check that duplicate types don't cause any compile errors _c: T, // check that when a type is both an active and inactive type, both trait bounds are used _d: U, From e71b4720e5f138b124e193cfe5476577e5c4508e Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Fri, 27 Jan 2023 16:05:59 -0500 Subject: [PATCH 9/9] address comments --- .../bevy_reflect_derive/src/derive_data.rs | 60 +++++++++---------- .../bevy_reflect_derive/src/impls/enums.rs | 29 +++------ .../bevy_reflect_derive/src/impls/structs.rs | 26 ++------ .../src/impls/tuple_structs.rs | 26 ++------ .../bevy_reflect_derive/src/impls/typed.rs | 23 +++---- .../bevy_reflect_derive/src/impls/values.rs | 18 ++---- .../bevy_reflect_derive/src/registration.rs | 21 ++----- .../bevy_reflect_derive/src/utility.rs | 57 +++++++++++++----- 8 files changed, 104 insertions(+), 156 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 8ca12218c51c5..1b47092dbd872 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,14 +1,14 @@ use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; -use crate::utility::members_to_serialization_denylist; +use crate::fq_std::{FQAny, FQDefault, FQSend, FQSync}; +use crate::utility::{members_to_serialization_denylist, WhereClauseOptions}; use bit_set::BitSet; -use proc_macro2::TokenStream; use quote::quote; use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant}; +use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant}; pub(crate) enum ReflectDerive<'a> { Struct(ReflectStruct<'a>), @@ -317,27 +317,16 @@ impl<'a> ReflectMeta<'a> { } /// Returns the `GetTypeRegistration` impl as a `TokenStream`. - /// - /// * `active_types`: types corresponding to active fields in the object (used to add specific trait bounds) - /// * `ignored_types`: types corresponding to ignored fields in the object (used to add specific trait bounds) - /// * `active_trait_bounds`: trait bounds to provide for the active types - /// * `ignored_trait_bounds`: trait bounds to provide for the ignored types pub fn get_type_registration( &self, - active_types: &[Type], - ignored_types: &[Type], - active_trait_bounds: &TokenStream, - ignored_trait_bounds: &TokenStream, + where_clause_options: &WhereClauseOptions, ) -> proc_macro2::TokenStream { crate::registration::impl_get_type_registration( self.type_name, &self.bevy_reflect_path, self.traits.idents(), self.generics, - active_types, - ignored_types, - active_trait_bounds, - ignored_trait_bounds, + where_clause_options, None, ) } @@ -366,17 +355,9 @@ impl<'a> ReflectStruct<'a> { /// Returns the `GetTypeRegistration` impl as a `TokenStream`. /// /// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method - /// - /// * `active_types`: types corresponding to active fields in the struct (used to add specific trait bounds) - /// * `ignored_types`: types corresponding to ignored fields in the struct (used to add specific trait bounds) - /// * `active_trait_bounds`: trait bounds to provide for the active types - /// * `ignored_trait_bounds`: trait bounds to provide for the ignored types pub fn get_type_registration( &self, - active_types: &[Type], - ignored_types: &[Type], - active_trait_bounds: &TokenStream, - ignored_trait_bounds: &TokenStream, + where_clause_options: &WhereClauseOptions, ) -> proc_macro2::TokenStream { let reflect_path = self.meta.bevy_reflect_path(); @@ -385,10 +366,7 @@ impl<'a> ReflectStruct<'a> { reflect_path, self.meta.traits().idents(), self.meta.generics(), - active_types, - ignored_types, - active_trait_bounds, - ignored_trait_bounds, + where_clause_options, Some(&self.serialization_denylist), ) } @@ -426,6 +404,16 @@ impl<'a> ReflectStruct<'a> { pub fn fields(&self) -> &[StructField<'a>] { &self.fields } + + pub fn where_clause_options(&self) -> WhereClauseOptions { + let bevy_reflect_path = &self.meta().bevy_reflect_path; + WhereClauseOptions { + active_types: self.active_types().into(), + active_trait_bounds: quote! { #bevy_reflect_path::Reflect }, + ignored_types: self.ignored_types().into(), + ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync }, + } + } } impl<'a> ReflectEnum<'a> { @@ -474,6 +462,16 @@ impl<'a> ReflectEnum<'a> { .map(|field| field.data.ty.clone()) .collect() } + + pub fn where_clause_options(&self) -> WhereClauseOptions { + let bevy_reflect_path = &self.meta().bevy_reflect_path; + WhereClauseOptions { + active_types: self.active_types().into(), + active_trait_bounds: quote! { #bevy_reflect_path::FromReflect }, + ignored_types: self.ignored_types().into(), + ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync + #FQDefault }, + } + } } impl<'a> EnumVariant<'a> { @@ -482,7 +480,7 @@ impl<'a> EnumVariant<'a> { pub fn active_fields(&self) -> impl Iterator> { self.fields() .iter() - .filter(move |field| field.attrs.ignore.is_active()) + .filter(|field| field.attrs.ignore.is_active()) } /// Get an iterator of fields which are ignored by the reflection API @@ -490,7 +488,7 @@ impl<'a> EnumVariant<'a> { pub fn ignored_fields(&self) -> impl Iterator> { self.fields() .iter() - .filter(move |field| field.attrs.ignore.is_ignored()) + .filter(|field| field.attrs.ignore.is_ignored()) } /// The complete set of fields in this variant. diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 4aeba07351be2..666283615d985 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,6 +1,6 @@ use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; -use crate::fq_std::{FQAny, FQBox, FQOption, FQResult, FQSend, FQSync}; +use crate::fq_std::{FQAny, FQBox, FQOption, FQResult}; use crate::impls::impl_typed; use crate::utility::extend_where_clause; use proc_macro::TokenStream; @@ -16,10 +16,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let ref_index = Ident::new("__index_param", Span::call_site()); let ref_value = Ident::new("__value_param", Span::call_site()); - let field_types = reflect_enum.active_types(); - let ignored_types = reflect_enum.ignored_types(); - let active_trait_bounds = quote! { #bevy_reflect_path::FromReflect }; - let ignored_trait_bounds = quote! { #FQAny + #FQSend + #FQSync + Default }; + let where_clause_options = reflect_enum.where_clause_options(); let EnumImpls { variant_info, @@ -82,10 +79,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let typed_impl = impl_typed( enum_name, reflect_enum.meta().generics(), - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, + &where_clause_options, quote! { let variants = [#(#variant_info),*]; let info = #info_generator; @@ -94,22 +88,13 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { bevy_reflect_path, ); - let get_type_registration_impl = reflect_enum.meta().get_type_registration( - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, - ); + let get_type_registration_impl = reflect_enum + .meta() + .get_type_registration(&where_clause_options); let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().generics().split_for_impl(); - let where_reflect_clause = extend_where_clause( - where_clause, - &field_types, - &active_trait_bounds, - &ignored_types, - &ignored_trait_bounds, - ); + let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index fa4ec99978720..6dd4e983c93c6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -1,4 +1,4 @@ -use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult, FQSend, FQSync}; +use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::impl_typed; use crate::utility::extend_where_clause; use crate::ReflectStruct; @@ -36,9 +36,6 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { }) .collect::>(); let field_types = reflect_struct.active_types(); - let ignored_types = reflect_struct.ignored_types(); - let active_trait_bounds = quote! { #bevy_reflect_path::Reflect }; - let ignored_trait_bounds = quote! { #FQAny + #FQSend + #FQSync }; let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); @@ -92,13 +89,11 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { } }; + let where_clause_options = reflect_struct.where_clause_options(); let typed_impl = impl_typed( struct_name, reflect_struct.meta().generics(), - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, + &where_clause_options, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -107,22 +102,11 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { bevy_reflect_path, ); - let get_type_registration_impl = reflect_struct.get_type_registration( - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, - ); + let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options); let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); - let where_reflect_clause = extend_where_clause( - where_clause, - &field_types, - &active_trait_bounds, - &ignored_types, - &ignored_trait_bounds, - ); + let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index b0a25ce6ff117..c41c53b86ef55 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -1,4 +1,4 @@ -use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult, FQSend, FQSync}; +use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::impl_typed; use crate::utility::extend_where_clause; use crate::ReflectStruct; @@ -18,18 +18,11 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { .map(|field| Member::Unnamed(Index::from(field.index))) .collect::>(); let field_types = reflect_struct.active_types(); - let ignored_types = reflect_struct.ignored_types(); - let active_trait_bounds = quote! { #bevy_reflect_path::Reflect }; - let ignored_trait_bounds = quote! { #FQAny + #FQSend + #FQSync }; let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); - let get_type_registration_impl = reflect_struct.get_type_registration( - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, - ); + let where_clause_options = reflect_struct.where_clause_options(); + let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options); let hash_fn = reflect_struct .meta() @@ -85,10 +78,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let typed_impl = impl_typed( struct_name, reflect_struct.meta().generics(), - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, + &where_clause_options, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -100,13 +90,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); - let where_reflect_clause = extend_where_clause( - where_clause, - &field_types, - &active_trait_bounds, - &ignored_types, - &ignored_trait_bounds, - ); + let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index 41ddd5cd9131f..e0b88c92e44d4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -1,19 +1,16 @@ -use crate::utility::extend_where_clause; -use proc_macro2::{Ident, TokenStream}; +use crate::utility::{extend_where_clause, WhereClauseOptions}; +use proc_macro2::Ident; use quote::quote; -use syn::{Generics, Path, Type}; +use syn::{Generics, Path}; #[allow(clippy::too_many_arguments)] pub(crate) fn impl_typed( type_name: &Ident, generics: &Generics, - active_types: &[Type], - ignored_types: &[Type], - active_trait_bounds: &TokenStream, - ignored_trait_bounds: &TokenStream, - generator: TokenStream, + where_clause_options: &WhereClauseOptions, + generator: proc_macro2::TokenStream, bevy_reflect_path: &Path, -) -> TokenStream { +) -> proc_macro2::TokenStream { let is_generic = !generics.params.is_empty(); let static_generator = if is_generic { @@ -35,13 +32,7 @@ pub(crate) fn impl_typed( let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); // Add Typed bound for each active field - let where_reflect_clause = extend_where_clause( - where_clause, - active_types, - active_trait_bounds, - ignored_types, - ignored_trait_bounds, - ); + let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); quote! { impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_reflect_clause { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 87b7e27db5620..4aa3aed418fb6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -1,5 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult}; use crate::impls::impl_typed; +use crate::utility::WhereClauseOptions; use crate::ReflectMeta; use proc_macro::TokenStream; use quote::quote; @@ -21,17 +22,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #[cfg(not(feature = "documentation"))] let with_docs: Option = None; - let field_types = Vec::default(); - let ignored_types = Vec::default(); - let active_trait_bounds = quote! {}; - let ignored_trait_bounds = quote! {}; + let where_clause_options = WhereClauseOptions::default(); let typed_impl = impl_typed( type_name, meta.generics(), - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, + &where_clause_options, quote! { let info = #bevy_reflect_path::ValueInfo::new::() #with_docs; #bevy_reflect_path::TypeInfo::Value(info) @@ -40,12 +35,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { ); let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); - let get_type_registration_impl = meta.get_type_registration( - &field_types, - &ignored_types, - &active_trait_bounds, - &ignored_trait_bounds, - ); + let get_type_registration_impl = meta.get_type_registration(&where_clause_options); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index b32fa99d0f32e..256fd82c55886 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,10 +1,10 @@ //! Contains code related specifically to Bevy's type registration. -use crate::utility::extend_where_clause; +use crate::utility::{extend_where_clause, WhereClauseOptions}; use bit_set::BitSet; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::Ident; use quote::quote; -use syn::{Generics, Path, Type}; +use syn::{Generics, Path}; /// Creates the `GetTypeRegistration` impl for the given type data. #[allow(clippy::too_many_arguments)] @@ -13,12 +13,9 @@ pub(crate) fn impl_get_type_registration( bevy_reflect_path: &Path, registration_data: &[Ident], generics: &Generics, - active_types: &[Type], - ignored_types: &[Type], - active_trait_bounds: &TokenStream, - ignored_trait_bounds: &TokenStream, + where_clause_options: &WhereClauseOptions, serialization_denylist: Option<&BitSet>, -) -> TokenStream { +) -> proc_macro2::TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let serialization_data = serialization_denylist.map(|denylist| { let denylist = denylist.into_iter(); @@ -28,13 +25,7 @@ pub(crate) fn impl_get_type_registration( } }); - let where_reflect_clause = extend_where_clause( - where_clause, - active_types, - active_trait_bounds, - ignored_types, - ignored_trait_bounds, - ); + let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); quote! { #[allow(unused_mut)] diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 92d2462001554..abe0806a64853 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -3,7 +3,7 @@ use crate::field_attributes::ReflectIgnoreBehavior; use bevy_macro_utils::BevyManifest; use bit_set::BitSet; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, Span}; use quote::quote; use syn::{Member, Path, Type, WhereClause}; @@ -60,21 +60,45 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { ) } +/// Options defining how to extend the `where` clause in reflection with any additional bounds needed. +pub(crate) struct WhereClauseOptions { + /// Any types that will be reflected and need an extra trait bound + pub(crate) active_types: Box<[Type]>, + /// Trait bounds to add to the active types + pub(crate) active_trait_bounds: proc_macro2::TokenStream, + /// Any types that won't be reflected and need an extra trait bound + pub(crate) ignored_types: Box<[Type]>, + /// Trait bounds to add to the ignored types + pub(crate) ignored_trait_bounds: proc_macro2::TokenStream, +} + +impl Default for WhereClauseOptions { + /// By default, don't add any additional bounds to the `where` clause + fn default() -> Self { + Self { + active_types: Box::new([]), + ignored_types: Box::new([]), + active_trait_bounds: quote! {}, + ignored_trait_bounds: quote! {}, + } + } +} + /// Extends the `where` clause in reflection with any additional bounds needed. /// -/// * `where_clause`: existing `where` clause present on the object to be derived -/// * `active_types`: any types that will be reflected and need an extra trait bound -/// * `active_trait_bounds`: trait bounds to add to the active types -/// * `ignored_types`: any types that won't be reflected and need an extra trait bound -/// * `ignored_trait_bounds`: trait bounds to add to the ignored types -/// -/// /// This is mostly used to add additional bounds to reflected objects with generic types. /// For reflection purposes, we usually have: /// * `active_trait_bounds: Reflect` /// * `ignored_trait_bounds: Any + Send + Sync` /// -/// For example, the struct: +/// # Arguments +/// +/// * `where_clause`: existing `where` clause present on the object to be derived +/// * `where_clause_options`: additional paramters defining which trait bounds to add to the `where` clause +/// +/// # Example +/// +/// The struct: /// ```ignore /// #[derive(Reflect)] /// struct Foo { @@ -85,20 +109,21 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { /// ``` /// will have active types: `[T]` and ignored types: `[U]` /// -/// will yield the following `where` clause: +/// The `extend_where_clause` function will yield the following `where` clause: /// ```ignore /// where /// T: Reflect, // active_trait_bounds /// U: Any + Send + Sync, // ignored_trait_bounds /// ``` -/// pub(crate) fn extend_where_clause( where_clause: Option<&WhereClause>, - active_types: &[Type], - active_trait_bounds: &TokenStream, - ignored_types: &[Type], - ignored_trait_bounds: &TokenStream, -) -> TokenStream { + where_clause_options: &WhereClauseOptions, +) -> proc_macro2::TokenStream { + let active_types = &where_clause_options.active_types; + let ignored_types = &where_clause_options.ignored_types; + let active_trait_bounds = &where_clause_options.active_trait_bounds; + let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds; + let mut generic_where_clause = if where_clause.is_some() { quote! {#where_clause} } else if !(active_types.is_empty() && ignored_types.is_empty()) {