diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index fa9f3ee2dc..d006d85ed6 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -122,7 +122,6 @@ impl std::fmt::Debug for StrongHandle { /// [`Handle::Strong`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists). #[derive(Component, Reflect)] #[reflect(Component)] -#[reflect(where A: Asset)] pub enum Handle { /// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept /// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata. diff --git a/crates/bevy_asset/src/id.rs b/crates/bevy_asset/src/id.rs index a11fcad774..1efc61bacc 100644 --- a/crates/bevy_asset/src/id.rs +++ b/crates/bevy_asset/src/id.rs @@ -17,7 +17,6 @@ use thiserror::Error; /// /// For an "untyped" / "generic-less" id, see [`UntypedAssetId`]. #[derive(Reflect)] -#[reflect(where A: Asset)] pub enum AssetId { /// A small / efficient runtime identifier that can be used to efficiently look up an asset stored in [`Assets`]. This is /// the "default" identifier used for assets. The alternative(s) (ex: [`AssetId::Uuid`]) will only be used if assets are diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index b8dc979cb7..2b2dce64f5 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -25,6 +25,9 @@ const HASH_ATTR: &str = "Hash"; // but useful to know exist nonetheless pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault"; +// Attributes for `Reflect` implementation +const NO_FIELD_BOUNDS_ATTR: &str = "no_field_bounds"; + // Attributes for `FromReflect` implementation const FROM_REFLECT_ATTR: &str = "from_reflect"; @@ -212,6 +215,7 @@ pub(crate) struct ReflectTraits { from_reflect_attrs: FromReflectAttrs, type_path_attrs: TypePathAttrs, custom_where: Option, + no_field_bounds: bool, idents: Vec, } @@ -260,6 +264,9 @@ impl ReflectTraits { HASH_ATTR => { traits.hash.merge(TraitImpl::Implemented(span))?; } + NO_FIELD_BOUNDS_ATTR => { + traits.no_field_bounds = true; + } // We only track reflected idents for traits not considered special _ => { // Create the reflect ident @@ -422,6 +429,10 @@ impl ReflectTraits { self.custom_where.as_ref() } + pub fn no_field_bounds(&self) -> bool { + self.no_field_bounds + } + /// Merges the trait implementations of this [`ReflectTraits`] with another one. /// /// An error is returned if the two [`ReflectTraits`] have conflicting implementations. @@ -434,6 +445,8 @@ impl ReflectTraits { self.merge_custom_where(other.custom_where); + self.no_field_bounds |= other.no_field_bounds; + for ident in other.idents { add_unique_ident(&mut self.idents, ident)?; } 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 cfdf641714..22330691e5 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,4 +1,4 @@ -use crate::container_attributes::{FromReflectAttrs, ReflectTraits}; +use crate::container_attributes::{FromReflectAttrs, ReflectTraits, TypePathAttrs}; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; use crate::type_path::parse_path_no_leading_colon; use crate::utility::{StringExpr, WhereClauseOptions}; @@ -402,6 +402,11 @@ impl<'a> ReflectMeta<'a> { self.traits.from_reflect_attrs() } + /// The `TypePath` attributes on this type. + pub fn type_path_attrs(&self) -> &TypePathAttrs { + self.traits.type_path_attrs() + } + /// The path to this type. pub fn type_path(&self) -> &ReflectTypePath<'a> { &self.type_path @@ -480,7 +485,7 @@ impl<'a> ReflectStruct<'a> { } pub fn where_clause_options(&self) -> WhereClauseOptions { - WhereClauseOptions::new(self.meta()) + WhereClauseOptions::new_with_fields(self.meta(), self.active_types().into_boxed_slice()) } } @@ -503,28 +508,33 @@ impl<'a> ReflectEnum<'a> { &self.variants } + /// 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 exposed to the reflection API + pub fn active_fields(&self) -> impl Iterator> { + self.variants + .iter() + .flat_map(|variant| variant.active_fields()) + } + pub fn where_clause_options(&self) -> WhereClauseOptions { - WhereClauseOptions::new(self.meta()) + WhereClauseOptions::new_with_fields(self.meta(), self.active_types().into_boxed_slice()) } } 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>] { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index c55267d374..7650a162bb 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -23,8 +23,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { let type_path = meta.type_path(); let bevy_reflect_path = meta.bevy_reflect_path(); let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); - let where_from_reflect_clause = - WhereClauseOptions::new_type_path(meta).extend_where_clause(where_clause); + let where_from_reflect_clause = WhereClauseOptions::new(meta).extend_where_clause(where_clause); quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_from_reflect_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption { @@ -50,8 +49,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl(); // Add FromReflect bound for each active field - let where_from_reflect_clause = - WhereClauseOptions::new(reflect_enum.meta()).extend_where_clause(where_clause); + let where_from_reflect_clause = reflect_enum + .where_clause_options() + .extend_where_clause(where_clause); quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause { @@ -130,8 +130,9 @@ fn impl_struct_internal( .split_for_impl(); // Add FromReflect bound for each active field - let where_from_reflect_clause = - WhereClauseOptions::new(reflect_struct.meta()).extend_where_clause(where_clause); + let where_from_reflect_clause = reflect_struct + .where_clause_options() + .extend_where_clause(where_clause); quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause { 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 294a8cce83..4019bffa3d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -49,7 +49,7 @@ pub(crate) enum TypedProperty { } pub(crate) fn impl_type_path(meta: &ReflectMeta) -> proc_macro2::TokenStream { - let where_clause_options = WhereClauseOptions::new_type_path(meta); + let where_clause_options = WhereClauseOptions::new(meta); if !meta.traits().type_path_attrs().should_auto_derive() { return proc_macro2::TokenStream::new(); 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 db1082236f..96725cf882 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -21,7 +21,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { #[cfg(not(feature = "documentation"))] let with_docs: Option = None; - let where_clause_options = WhereClauseOptions::new_type_path(meta); + let where_clause_options = WhereClauseOptions::new(meta); let typed_impl = impl_typed( meta, &where_clause_options, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 55d1fc845c..4584356a7a 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -131,50 +131,85 @@ pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name"; /// This is useful for when a type can't or shouldn't implement `TypePath`, /// or if a manual implementation is desired. /// -/// ## `#[reflect(where T: Trait, U::Assoc: Trait, ...)]` +/// ## `#[reflect(no_field_bounds)]` /// -/// By default, the derive macro will automatically add certain trait bounds to all generic type parameters -/// in order to make them compatible with reflection without the user needing to add them manually. -/// This includes traits like `Reflect` and `FromReflect`. -/// However, this may not always be desired, and some type paramaters can't or shouldn't require those bounds -/// (i.e. their usages in fields are ignored or they're only used for their associated types). +/// This attribute will opt-out of the default trait bounds added to all field types +/// for the generated reflection trait impls. /// -/// With this attribute, you can specify a custom `where` clause to be used instead of the default. -/// If this attribute is present, none of the type parameters will receive the default bounds. -/// Only the bounds specified by the type itself and by this attribute will be used. -/// The only exceptions to this are the `Any`, `Send`, `Sync`, and `TypePath` bounds, -/// which will always be added regardless of this attribute due to their necessity for reflection -/// in general. -/// -/// This means that if you want to opt-out of the default bounds for _all_ type parameters, -/// you can add `#[reflect(where)]` to the container item to indicate -/// that an empty `where` clause should be used. +/// Normally, all fields will have the bounds `TypePath`, and either `FromReflect` or `Reflect` +/// depending on if `#[reflect(from_reflect = false)]` is used. +/// However, this might not always be desirable, and so this attribute may be used to remove those bounds. /// /// ### Example /// +/// If a type is recursive the default bounds will cause an overflow error when building: +/// +/// ```ignore (bevy_reflect is not accessible from this crate) +/// #[derive(Reflect)] // ERROR: overflow evaluating the requirement `Foo: FromReflect` +/// struct Foo { +/// foo: Vec, +/// } +/// +/// // Generates a where clause like: +/// // impl bevy_reflect::Reflect for Foo +/// // where +/// // Self: Any + Send + Sync, +/// // Vec: FromReflect + TypePath, +/// ``` +/// +/// In this case, `Foo` is given the bounds `Vec: FromReflect + TypePath`, +/// which requires that `Foo` implements `FromReflect`, +/// which requires that `Vec` implements `FromReflect`, +/// and so on, resulting in the error. +/// +/// To fix this, we can add `#[reflect(no_field_bounds)]` to `Foo` to remove the bounds on `Vec`: +/// +/// ```ignore (bevy_reflect is not accessible from this crate) +/// #[derive(Reflect)] +/// #[reflect(no_field_bounds)] +/// struct Foo { +/// foo: Vec, +/// } +/// +/// // Generates a where clause like: +/// // impl bevy_reflect::Reflect for Foo +/// // where +/// // Self: Any + Send + Sync, +/// ``` +/// +/// ## `#[reflect(where T: Trait, U::Assoc: Trait, ...)]` +/// +/// This attribute can be used to add additional bounds to the generated reflection trait impls. +/// +/// This is useful for when a type needs certain bounds only applied to the reflection impls +/// that are not otherwise automatically added by the derive macro. +/// +/// ### Example +/// +/// In the example below, we want to enforce that `T::Assoc: List` is required in order for +/// `Foo` to be reflectable, but we don't want it to prevent `Foo` from being used +/// in places where `T::Assoc: List` is not required. +/// /// ```ignore /// trait Trait { /// type Assoc; /// } /// /// #[derive(Reflect)] -/// #[reflect(where T::Assoc: FromReflect)] +/// #[reflect(where T::Assoc: List)] /// struct Foo where T::Assoc: Default { /// value: T::Assoc, /// } /// -/// // Generates a where clause like the following -/// // (notice that `T` does not have any `Reflect` or `FromReflect` bounds): +/// // Generates a where clause like: /// // /// // impl bevy_reflect::Reflect for Foo /// // where -/// // Self: 'static, +/// // Self: Any + Send + Sync, /// // T::Assoc: Default, -/// // T: bevy_reflect::TypePath -/// // + ::core::any::Any -/// // + ::core::marker::Send -/// // + ::core::marker::Sync, -/// // T::Assoc: FromReflect, +/// // T: TypePath, +/// // T::Assoc: FromReflect + TypePath, +/// // T::Assoc: List, /// // {/* ... */} /// ``` /// @@ -191,10 +226,6 @@ pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name"; /// which may be useful for maintaining invariants, keeping certain data private, /// or allowing the use of types that do not implement `Reflect` within the container. /// -/// If the field contains a generic type parameter, you will likely need to add a -/// [`#[reflect(where)]`](#reflectwheret-trait-uassoc-trait-) -/// attribute to the container in order to avoid the default bounds being applied to the type parameter. -/// /// ## `#[reflect(skip_serializing)]` /// /// This works similar to `#[reflect(ignore)]`, but rather than opting out of _all_ of reflection, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index b50b0b92eb..148b694f10 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -5,9 +5,10 @@ use bevy_macro_utils::{ fq_std::{FQAny, FQOption, FQSend, FQSync}, BevyManifest, }; -use proc_macro2::{Ident, Span}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; -use syn::{spanned::Spanned, LitStr, Member, Path, WhereClause}; +use syn::punctuated::Punctuated; +use syn::{spanned::Spanned, LitStr, Member, Path, Token, Type, WhereClause}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -69,64 +70,36 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { /// Options defining how to extend the `where` clause for reflection. pub(crate) struct WhereClauseOptions<'a, 'b> { meta: &'a ReflectMeta<'b>, - additional_bounds: proc_macro2::TokenStream, - required_bounds: proc_macro2::TokenStream, + active_fields: Box<[Type]>, } impl<'a, 'b> WhereClauseOptions<'a, 'b> { - /// Create [`WhereClauseOptions`] for a reflected struct or enum type. pub fn new(meta: &'a ReflectMeta<'b>) -> Self { - let bevy_reflect_path = meta.bevy_reflect_path(); - - let active_bound = if meta.from_reflect().should_auto_derive() { - quote!(#bevy_reflect_path::FromReflect) - } else { - quote!(#bevy_reflect_path::Reflect) - }; - - let type_path_bound = if meta.traits().type_path_attrs().should_auto_derive() { - Some(quote!(#bevy_reflect_path::TypePath +)) - } else { - None - }; - Self { meta, - additional_bounds: quote!(#type_path_bound #active_bound), - required_bounds: quote!(#type_path_bound #FQAny + #FQSend + #FQSync), + active_fields: Box::new([]), } } - /// Create [`WhereClauseOptions`] with the minimum bounds needed to fulfill `TypePath`. - pub fn new_type_path(meta: &'a ReflectMeta<'b>) -> Self { - let bevy_reflect_path = meta.bevy_reflect_path(); - + pub fn new_with_fields(meta: &'a ReflectMeta<'b>, active_fields: Box<[Type]>) -> Self { Self { meta, - additional_bounds: quote!(#bevy_reflect_path::TypePath), - required_bounds: quote!(#bevy_reflect_path::TypePath + #FQAny + #FQSend + #FQSync), + active_fields, } } - /// Extends the `where` clause in reflection with additional bounds needed for reflection. + /// Extends the `where` clause for a type with additional bounds needed for the reflection impls. /// - /// This will only add bounds for generic type parameters. + /// The default bounds added are as follows: + /// - `Self` has the bounds `Any + Send + Sync` + /// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present + /// - Active fields have the bounds `TypePath` and either `Reflect` if `#[reflect(from_reflect = false)]` is present + /// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present) /// - /// If the container has a `#[reflect(where)]` attribute, - /// this method will extend the type parameters with the _required_ bounds. - /// If the attribute is not present, it will extend the type parameters with the _additional_ bounds. - /// - /// The required bounds are the minimum bounds needed for a type to be reflected. - /// These include `TypePath`, `Any`, `Send`, and `Sync`. - /// - /// The additional bounds are added bounds used to enforce that a generic type parameter - /// is itself reflectable. - /// These include `Reflect` and `FromReflect`, as well as `TypePath`. + /// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well. /// /// # Example /// - /// Take the following struct: - /// /// ```ignore (bevy_reflect is not accessible from this crate) /// #[derive(Reflect)] /// struct Foo { @@ -136,82 +109,145 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> { /// } /// ``` /// - /// It has type parameters `T` and `U`. - /// - /// Since there is no `#[reflect(where)]` attribute, this method will extend the type parameters - /// with the additional bounds: + /// Generates the following where clause: /// /// ```ignore (bevy_reflect is not accessible from this crate) /// where - /// T: FromReflect + TypePath, // additional bounds - /// U: FromReflect + TypePath, // additional bounds + /// // `Self` bounds: + /// Self: Any + Send + Sync, + /// // Type parameter bounds: + /// T: TypePath, + /// U: TypePath, + /// // Field bounds + /// T: FromReflect + TypePath, /// ``` /// - /// If we had this struct: - /// ```ignore (bevy_reflect is not accessible from this crate) - /// #[derive(Reflect)] - /// #[reflect(where T: FromReflect + Default)] - /// struct Foo { - /// a: T, - /// #[reflect(ignore)] - /// b: U - /// } - /// ``` - /// - /// Since there is a `#[reflect(where)]` attribute, this method will extend the type parameters - /// with _just_ the required bounds along with the predicates specified in the attribute: + /// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate: /// /// ```ignore (bevy_reflect is not accessible from this crate) /// where - /// T: FromReflect + Default, // predicates from attribute - /// T: TypePath + Any + Send + Sync, // required bounds - /// U: TypePath + Any + Send + Sync, // required bounds + /// // `Self` bounds: + /// Self: Any + Send + Sync, + /// // Type parameter bounds: + /// T: TypePath, + /// U: TypePath, + /// // Field bounds + /// T: FromReflect + TypePath, + /// // Custom bounds + /// T: MyTrait, + /// ``` + /// + /// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate: + /// + /// ```ignore (bevy_reflect is not accessible from this crate) + /// where + /// // `Self` bounds: + /// Self: Any + Send + Sync, + /// // Type parameter bounds: + /// T: TypePath, + /// U: TypePath, + /// // Custom bounds + /// T: MyTrait, /// ``` pub fn extend_where_clause( &self, where_clause: Option<&WhereClause>, ) -> proc_macro2::TokenStream { + let required_bounds = self.required_bounds(); // Maintain existing where clause, if any. let mut generic_where_clause = if let Some(where_clause) = where_clause { let predicates = where_clause.predicates.iter(); - quote! {where Self: 'static, #(#predicates,)*} + quote! {where Self: #required_bounds, #(#predicates,)*} } else { - quote!(where Self: 'static,) + quote!(where Self: #required_bounds,) }; // Add additional reflection trait bounds - let types = self.type_param_idents(); - let custom_where = self - .meta - .traits() - .custom_where() - .map(|clause| &clause.predicates); - let trait_bounds = self.trait_bounds(); - + let predicates = self.predicates(); generic_where_clause.extend(quote! { - #(#types: #trait_bounds,)* - #custom_where + #predicates }); generic_where_clause } - /// Returns the trait bounds to use for all type parameters. - fn trait_bounds(&self) -> &proc_macro2::TokenStream { - if self.meta.traits().custom_where().is_some() { - &self.required_bounds + /// Returns an iterator the where clause predicates to extended the where clause with. + fn predicates(&self) -> Punctuated { + let mut predicates = Punctuated::new(); + + if let Some(type_param_predicates) = self.type_param_predicates() { + predicates.extend(type_param_predicates); + } + + if let Some(field_predicates) = self.active_field_predicates() { + predicates.extend(field_predicates); + } + + if let Some(custom_where) = self.meta.traits().custom_where() { + predicates.push(custom_where.predicates.to_token_stream()); + } + + predicates + } + + /// Returns an iterator over the where clause predicates for the type parameters + /// if they require one. + fn type_param_predicates(&self) -> Option + '_> { + self.type_path_bound().map(|type_path_bound| { + self.meta + .type_path() + .generics() + .type_params() + .map(move |param| { + let ident = ¶m.ident; + + quote!(#ident : #type_path_bound) + }) + }) + } + + /// Returns an iterator over the where clause predicates for the active fields. + fn active_field_predicates(&self) -> Option + '_> { + if self.meta.traits().no_field_bounds() { + None } else { - &self.additional_bounds + let bevy_reflect_path = self.meta.bevy_reflect_path(); + let reflect_bound = self.reflect_bound(); + + // `TypePath` is always required for active fields since they are used to + // construct `NamedField` and `UnnamedField` instances for the `Typed` impl. + Some( + self.active_fields + .iter() + .map(move |ty| quote!(#ty : #reflect_bound + #bevy_reflect_path::TypePath)), + ) } } - /// Returns an iterator of the type parameter idents for the reflected type. - fn type_param_idents(&self) -> impl Iterator { - self.meta - .type_path() - .generics() - .type_params() - .map(|param| ¶m.ident) + /// The `Reflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`. + fn reflect_bound(&self) -> TokenStream { + let bevy_reflect_path = self.meta.bevy_reflect_path(); + + if self.meta.from_reflect().should_auto_derive() { + quote!(#bevy_reflect_path::FromReflect) + } else { + quote!(#bevy_reflect_path::Reflect) + } + } + + /// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`. + fn type_path_bound(&self) -> Option { + if self.meta.type_path_attrs().should_auto_derive() { + let bevy_reflect_path = self.meta.bevy_reflect_path(); + Some(quote!(#bevy_reflect_path::TypePath)) + } else { + None + } + } + + /// The minimum required bounds for a type to be reflected. + fn required_bounds(&self) -> proc_macro2::TokenStream { + quote!(#FQAny + #FQSend + #FQSync) } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 59a69ca6c7..4de1f1cb57 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1899,8 +1899,8 @@ bevy_reflect::tests::Test { #[test] fn should_allow_multiple_custom_where() { #[derive(Reflect)] - #[reflect(where T: Default + FromReflect)] - #[reflect(where U: std::ops::Add + FromReflect)] + #[reflect(where T: Default)] + #[reflect(where U: std::ops::Add)] struct Foo(T, U); #[derive(Reflect)] @@ -1916,12 +1916,12 @@ bevy_reflect::tests::Test { #[test] fn should_allow_custom_where_wtih_assoc_type() { trait Trait { - type Assoc: FromReflect + TypePath; + type Assoc; } // We don't need `T` to be `Reflect` since we only care about `T::Assoc` #[derive(Reflect)] - #[reflect(where T::Assoc: FromReflect)] + #[reflect(where T::Assoc: core::fmt::Display)] struct Foo(T::Assoc); #[derive(TypePath)] @@ -1931,7 +1931,15 @@ bevy_reflect::tests::Test { type Assoc = usize; } + #[derive(TypePath)] + struct Baz; + + impl Trait for Baz { + type Assoc = (f32, f32); + } + assert_impl_all!(Foo: Reflect); + assert_not_impl_all!(Foo: Reflect); } #[test] @@ -1943,6 +1951,7 @@ bevy_reflect::tests::Test { let _ = > as TypePath>::type_path(); #[derive(Reflect)] + #[reflect(no_field_bounds)] struct SelfRecurse { recurse: Vec, } @@ -1951,11 +1960,13 @@ bevy_reflect::tests::Test { let _ = ::type_path(); #[derive(Reflect)] + #[reflect(no_field_bounds)] enum RecurseA { Recurse(RecurseB), } #[derive(Reflect)] + // `#[reflect(no_field_bounds)]` not needed since already added to `RecurseA` struct RecurseB { vector: Vec, } @@ -1970,7 +1981,6 @@ bevy_reflect::tests::Test { fn can_opt_out_type_path() { #[derive(Reflect)] #[reflect(type_path = false)] - #[reflect(where)] struct Foo { #[reflect(ignore)] _marker: PhantomData,