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 ce379b8cd20ed..36f74ec43a5ae 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -3,7 +3,7 @@ use crate::derive_data::ReflectEnum; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::field_attributes::DefaultBehavior; use crate::fq_std::{FQAny, FQClone, FQDefault, FQOption}; -use crate::utility::{extend_where_clause, ident_or_index, WhereClauseOptions}; +use crate::utility::{ident_or_index, WhereClauseOptions}; use crate::{ReflectMeta, ReflectStruct}; use proc_macro2::Span; use quote::{quote, ToTokens}; @@ -23,8 +23,8 @@ 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 = - extend_where_clause(where_clause, &WhereClauseOptions::new(meta)); + 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 { @@ -51,7 +51,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream // Add FromReflect bound for each active field let where_from_reflect_clause = - extend_where_clause(where_clause, &WhereClauseOptions::new(reflect_enum.meta())); + WhereClauseOptions::new(reflect_enum.meta()).extend_where_clause(where_clause); quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause { @@ -130,10 +130,8 @@ fn impl_struct_internal( .split_for_impl(); // Add FromReflect bound for each active field - let where_from_reflect_clause = extend_where_clause( - where_clause, - &WhereClauseOptions::new(reflect_struct.meta()), - ); + let where_from_reflect_clause = + WhereClauseOptions::new(reflect_struct.meta()).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/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 41a47c2552c15..573e4c717cf13 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -2,7 +2,6 @@ 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_type_path, impl_typed}; -use crate::utility::extend_where_clause; use proc_macro2::{Ident, Span}; use quote::quote; use syn::Fields; @@ -94,7 +93,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().type_path().generics().split_for_impl(); - let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); 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 40ca5e3bd503b..49fe1ddadb4ca 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::impls::{impl_type_path, impl_typed}; -use crate::utility::{extend_where_clause, ident_or_index}; +use crate::utility::ident_or_index; use crate::ReflectStruct; use quote::{quote, ToTokens}; @@ -101,7 +101,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); - let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); 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 37984fc1f08d7..7a54d109758d3 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,5 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use crate::impls::{impl_type_path, impl_typed}; -use crate::utility::extend_where_clause; use crate::ReflectStruct; use quote::{quote, ToTokens}; use syn::{Index, Member}; @@ -92,7 +91,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: .generics() .split_for_impl(); - let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); 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 290154692c496..1c21218d70290 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -1,4 +1,4 @@ -use crate::utility::{extend_where_clause, StringExpr, WhereClauseOptions}; +use crate::utility::{StringExpr, WhereClauseOptions}; use quote::{quote, ToTokens}; use crate::{ @@ -97,7 +97,7 @@ pub(crate) fn impl_type_path( let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); // Add Typed bound for each active field - let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); quote! { #primitive_assert @@ -138,7 +138,7 @@ pub(crate) fn impl_typed( let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); - let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); quote! { impl #impl_generics #bevy_reflect_path::Typed for #type_path #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 ca8fa4595be7a..743e97f610412 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -1,6 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult}; use crate::impls::{impl_type_path, impl_typed}; -use crate::utility::{extend_where_clause, WhereClauseOptions}; +use crate::utility::WhereClauseOptions; use crate::ReflectMeta; use quote::quote; @@ -34,7 +34,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { let type_path_impl = impl_type_path(meta, &where_clause_options); let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); - let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 0b0a31e0a38fd..b1b3ce27c4e4d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,7 +1,7 @@ //! Contains code related specifically to Bevy's type registration. use crate::derive_data::ReflectMeta; -use crate::utility::{extend_where_clause, WhereClauseOptions}; +use crate::utility::WhereClauseOptions; use bit_set::BitSet; use quote::quote; @@ -16,7 +16,7 @@ pub(crate) fn impl_get_type_registration( let bevy_reflect_path = meta.bevy_reflect_path(); let registration_data = meta.traits().idents(); let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); - let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let from_reflect_data = if meta.from_reflect().should_auto_derive() { Some(quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 54f60f3298815..7ce8bd3d364d4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -7,8 +7,7 @@ use bevy_macro_utils::BevyManifest; use bit_set::BitSet; use proc_macro2::{Ident, Span}; use quote::{quote, ToTokens}; -use syn::punctuated::Punctuated; -use syn::{spanned::Spanned, LitStr, Member, Path, Token, TypeParam, WhereClause, WherePredicate}; +use syn::{spanned::Spanned, LitStr, Member, Path, WhereClause}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -61,151 +60,142 @@ 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 - active_types: Vec, - /// Trait bounds to add to the active types - active_trait_bounds: Vec, - /// Any types that won't be reflected and need an extra trait bound - ignored_types: Vec, - /// Trait bounds to add to the ignored types - ignored_trait_bounds: Vec, - custom_where: Option>, +/// 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, } -impl Default for WhereClauseOptions { - /// By default, don't add any additional bounds to the `where` clause - fn default() -> Self { - Self { - active_types: Vec::new(), - ignored_types: Vec::new(), - active_trait_bounds: Vec::new(), - ignored_trait_bounds: Vec::new(), - custom_where: None, - } - } -} - -impl WhereClauseOptions { +impl<'a, 'b> WhereClauseOptions<'a, 'b> { /// Create [`WhereClauseOptions`] for a reflected struct or enum type. - pub fn new(meta: &ReflectMeta) -> Self { + pub fn new(meta: &'a ReflectMeta<'b>) -> Self { let bevy_reflect_path = meta.bevy_reflect_path(); let is_from_reflect = meta.from_reflect().should_auto_derive(); - Self::new_with_bounds( + Self { meta, - |_| { - if is_from_reflect { - Some(quote!(#bevy_reflect_path::FromReflect + #bevy_reflect_path::TypePath)) - } else { - Some(quote!(#bevy_reflect_path::Reflect + #bevy_reflect_path::TypePath)) - } + additional_bounds: if is_from_reflect { + quote!(#bevy_reflect_path::FromReflect + #bevy_reflect_path::TypePath) + } else { + quote!(#bevy_reflect_path::Reflect + #bevy_reflect_path::TypePath) }, - |_| Some(quote!(#bevy_reflect_path::TypePath + #FQAny + #FQSend + #FQSync)), - ) + required_bounds: quote!(#bevy_reflect_path::TypePath + #FQAny + #FQSend + #FQSync), + } } /// Create [`WhereClauseOptions`] with the minimum bounds needed to fulfill `TypePath`. - pub fn new_type_path(meta: &ReflectMeta) -> Self { + pub fn new_type_path(meta: &'a ReflectMeta<'b>) -> Self { let bevy_reflect_path = meta.bevy_reflect_path(); - Self::new_with_bounds( + Self { meta, - |_| Some(quote!(#bevy_reflect_path::TypePath)), - |_| Some(quote!(#bevy_reflect_path::TypePath + #FQAny + #FQSend + #FQSync)), - ) + additional_bounds: quote!(#bevy_reflect_path::TypePath), + required_bounds: quote!(#bevy_reflect_path::TypePath + #FQAny + #FQSend + #FQSync), + } } - /// Create [`WhereClauseOptions`] for a struct or enum type. + /// Extends the `where` clause in reflection with additional bounds needed for reflection. /// - /// Compared to [`WhereClauseOptions::new`], this version allows you to specify - /// custom trait bounds for each field. - fn new_with_bounds( - meta: &ReflectMeta, - active_bounds: impl Fn(&TypeParam) -> Option, - ignored_bounds: impl Fn(&TypeParam) -> Option, - ) -> Self { - let mut options = WhereClauseOptions::default(); - - for param in meta.type_path().generics().type_params() { - let ident = param.ident.clone(); - let override_bounds = meta.traits().custom_where().is_some(); - - if override_bounds { - let bounds = ignored_bounds(param).unwrap_or_default(); - - options.ignored_types.push(ident); - options.ignored_trait_bounds.push(bounds); - } else { - let bounds = active_bounds(param).unwrap_or_default(); + /// This will only add bounds for generic type parameters. + /// + /// If the container has a `#[reflect(custom_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`. + /// + /// # Example + /// + /// Take the following struct: + /// + /// ```ignore + /// #[derive(Reflect)] + /// struct Foo { + /// a: T, + /// #[reflect(ignore)] + /// b: U + /// } + /// ``` + /// + /// It has type parameters `T` and `U`. + /// + /// Since there is no `#[reflect(custom_where(...))]` attribute, this method will extend the type parameters + /// with the additional bounds: + /// + /// ```ignore + /// where + /// T: FromReflect + TypePath, // additional bounds + /// U: FromReflect + TypePath, // additional bounds + /// ``` + /// + /// If we had this struct: + /// ```ignore + /// #[derive(Reflect)] + /// #[reflect(custom_where(T: FromReflect + Default))] + /// struct Foo { + /// a: T, + /// #[reflect(ignore)] + /// b: U + /// } + /// ``` + /// + /// Since there is a `#[reflect(custom_where(...))]` attribute, this method will extend the type parameters + /// with _just_ the required bounds along with the predicates specified in the attribute: + /// + /// ```ignore + /// where + /// T: FromReflect + Default, // predicates from attribute + /// T: TypePath + Any + Send + Sync, // required bounds + /// U: TypePath + Any + Send + Sync, // required bounds + /// ``` + pub fn extend_where_clause( + &self, + where_clause: Option<&WhereClause>, + ) -> proc_macro2::TokenStream { + // 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,)*} + } else { + quote!(where Self: 'static,) + }; - options.active_types.push(ident); - options.active_trait_bounds.push(bounds); - } - } + // Add additional reflection trait bounds + let types = self.type_param_idents(); + let custom_where = self.meta.traits().custom_where(); + let trait_bounds = self.trait_bounds(); - options.custom_where = meta.traits().custom_where().cloned(); + generic_where_clause.extend(quote! { + #(#types: #trait_bounds,)* + #custom_where + }); - options + generic_where_clause } -} -/// 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 + TypePath` or `FromReflect + TypePath` -/// * `ignored_trait_bounds`: `TypePath + Any + Send + Sync` -/// -/// # Arguments -/// -/// * `where_clause`: existing `where` clause present on the object to be derived -/// * `where_clause_options`: additional parameters 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 + TypePath, // active_trait_bounds -/// U: TypePath + 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 let Some(where_clause) = where_clause { - let predicates = where_clause.predicates.iter(); - quote! {where Self: 'static, #(#predicates,)*} - } else { - quote!(where Self: 'static,) - }; - - let custom_where = &where_clause_options.custom_where; - - generic_where_clause.extend(quote! { - #(#active_types: #active_trait_bounds,)* - #(#ignored_types: #ignored_trait_bounds,)* - #custom_where - }); - 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 + } else { + &self.additional_bounds + } + } + + /// 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) + } } impl Default for ResultSifter {