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 79595b988113b..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,6 +1,7 @@ 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 quote::quote; @@ -316,12 +317,16 @@ impl<'a> ReflectMeta<'a> { } /// Returns the `GetTypeRegistration` impl as a `TokenStream`. - pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + pub fn get_type_registration( + &self, + 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, + where_clause_options, None, ) } @@ -350,7 +355,10 @@ 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 - pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + pub fn get_type_registration( + &self, + where_clause_options: &WhereClauseOptions, + ) -> proc_macro2::TokenStream { let reflect_path = self.meta.bevy_reflect_path(); crate::registration::impl_get_type_registration( @@ -358,31 +366,37 @@ impl<'a> ReflectStruct<'a> { reflect_path, self.meta.traits().idents(), self.meta.generics(), + where_clause_options, Some(&self.serialization_denylist), ) } /// Get a collection of 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()) + self.active_fields() .map(|field| field.data.ty.clone()) - .collect::>() + .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 + pub fn ignored_types(&self) -> Vec { + self.ignored_fields() + .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 .iter() - .filter(move |field| field.attrs.ignore.is_ignored()) + .filter(|field| field.attrs.ignore.is_ignored()) } /// The complete set of fields in this struct. @@ -390,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> { @@ -410,4 +434,69 @@ 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 types which are exposed to the reflection API + pub fn active_types(&self) -> Vec { + self.active_fields() + .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.variants() + .iter() + .flat_map(|variant| variant.ignored_fields()) + } + + /// Get a collection of types which are ignored to the reflection API + pub fn ignored_types(&self) -> Vec { + self.ignored_fields() + .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> { + /// 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(|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(|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 a439e8b468969..666283615d985 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -2,6 +2,7 @@ use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructFiel use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::fq_std::{FQAny, FQBox, FQOption, FQResult}; use crate::impls::impl_typed; +use crate::utility::extend_where_clause; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::quote; @@ -15,6 +16,8 @@ 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 where_clause_options = reflect_enum.where_clause_options(); + let EnumImpls { variant_info, enum_field, @@ -76,6 +79,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let typed_impl = impl_typed( enum_name, reflect_enum.meta().generics(), + &where_clause_options, quote! { let variants = [#(#variant_info),*]; let info = #info_generator; @@ -84,16 +88,20 @@ 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(&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, &where_clause_options); + 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,)* @@ -177,7 +185,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 eefffd3ec0506..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,5 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::impl_typed; +use crate::utility::extend_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::{quote, ToTokens}; @@ -88,9 +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(), + &where_clause_options, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -99,16 +102,18 @@ 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(&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, &where_clause_options); + 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 +165,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..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,5 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::impl_typed; +use crate::utility::extend_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::{quote, ToTokens}; @@ -11,7 +12,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 +21,9 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); + 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() .traits() @@ -75,6 +78,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let typed_impl = impl_typed( struct_name, reflect_struct.meta().generics(), + &where_clause_options, quote! { let fields = [#field_generator]; let info = #info_generator; @@ -86,12 +90,14 @@ 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, &where_clause_options); + 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 +128,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..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,10 +1,13 @@ +use crate::utility::{extend_where_clause, WhereClauseOptions}; use proc_macro2::Ident; use quote::quote; use syn::{Generics, Path}; +#[allow(clippy::too_many_arguments)] pub(crate) fn impl_typed( type_name: &Ident, generics: &Generics, + where_clause_options: &WhereClauseOptions, generator: proc_macro2::TokenStream, bevy_reflect_path: &Path, ) -> proc_macro2::TokenStream { @@ -28,8 +31,11 @@ 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, where_clause_options); + 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_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 5d87027d1a25a..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,9 +22,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #[cfg(not(feature = "documentation"))] let with_docs: Option = None; + let where_clause_options = WhereClauseOptions::default(); let typed_impl = impl_typed( type_name, meta.generics(), + &where_clause_options, quote! { let info = #bevy_reflect_path::ValueInfo::new::() #with_docs; #bevy_reflect_path::TypeInfo::Value(info) @@ -32,7 +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(); + 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 456e4152ca85f..256fd82c55886 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,16 +1,19 @@ //! Contains code related specifically to Bevy's type registration. +use crate::utility::{extend_where_clause, WhereClauseOptions}; use bit_set::BitSet; use proc_macro2::Ident; use quote::quote; use syn::{Generics, Path}; /// 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, registration_data: &[Ident], generics: &Generics, + where_clause_options: &WhereClauseOptions, serialization_denylist: Option<&BitSet>, ) -> proc_macro2::TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); @@ -22,9 +25,11 @@ pub(crate) fn impl_get_type_registration( } }); + let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); + 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_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..abe0806a64853 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -4,7 +4,8 @@ use crate::field_attributes::ReflectIgnoreBehavior; use bevy_macro_utils::BevyManifest; use bit_set::BitSet; use proc_macro2::{Ident, Span}; -use syn::{Member, Path}; +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,84 @@ 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. +/// +/// 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` +/// +/// # 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 { +/// a: T, +/// #[reflect(ignore)] +/// b: U +/// } +/// ``` +/// will have active types: `[T]` and ignored types: `[U]` +/// +/// 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>, + 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()) { + quote! {where} + } else { + quote! {} + }; + generic_where_clause.extend(quote! { + #(#active_types: #active_trait_bounds,)* + #(#ignored_types: #ignored_trait_bounds,)* + }); + generic_where_clause +} + impl Default for ResultSifter { fn default() -> Self { Self { 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..964d784d354d0 --- /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: 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 new file mode 100644 index 0000000000000..f59304dbd4e38 --- /dev/null +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr @@ -0,0 +1,31 @@ +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 + | +14 | foo.get_field::("a").unwrap(); + | ^^^^^^^^^ 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: 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..505cf6dabd9d5 --- /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 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, + + #[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 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)