diff --git a/crates/bevy_reflect/derive/src/derive_data.rs b/crates/bevy_reflect/derive/src/derive_data.rs index 44e06658fc..4122ae5007 100644 --- a/crates/bevy_reflect/derive/src/derive_data.rs +++ b/crates/bevy_reflect/derive/src/derive_data.rs @@ -15,6 +15,7 @@ use crate::{ use quote::{quote, ToTokens}; use syn::token::Comma; +use crate::generics::generate_generics; use syn::{ parse_str, punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Field, Fields, GenericParam, Generics, Ident, LitStr, Meta, Path, PathSegment, Type, TypeParam, Variant, @@ -627,13 +628,6 @@ impl<'a> ReflectStruct<'a> { .custom_attributes() .to_tokens(bevy_reflect_path); - #[cfg_attr( - not(feature = "documentation"), - expect( - unused_mut, - reason = "Needs to be mutable if `documentation` feature is enabled.", - ) - )] let mut info = quote! { #bevy_reflect_path::#info_struct::new::(&[ #(#field_infos),* @@ -641,6 +635,12 @@ impl<'a> ReflectStruct<'a> { .with_custom_attributes(#custom_attributes) }; + if let Some(generics) = generate_generics(self.meta()) { + info.extend(quote! { + .with_generics(#generics) + }); + } + #[cfg(feature = "documentation")] { let docs = self.meta().doc(); @@ -730,13 +730,6 @@ impl<'a> ReflectEnum<'a> { .custom_attributes() .to_tokens(bevy_reflect_path); - #[cfg_attr( - not(feature = "documentation"), - expect( - unused_mut, - reason = "Needs to be mutable if `documentation` feature is enabled.", - ) - )] let mut info = quote! { #bevy_reflect_path::EnumInfo::new::(&[ #(#variants),* @@ -744,6 +737,12 @@ impl<'a> ReflectEnum<'a> { .with_custom_attributes(#custom_attributes) }; + if let Some(generics) = generate_generics(self.meta()) { + info.extend(quote! { + .with_generics(#generics) + }); + } + #[cfg(feature = "documentation")] { let docs = self.meta().doc(); diff --git a/crates/bevy_reflect/derive/src/generics.rs b/crates/bevy_reflect/derive/src/generics.rs new file mode 100644 index 0000000000..bf79b75348 --- /dev/null +++ b/crates/bevy_reflect/derive/src/generics.rs @@ -0,0 +1,72 @@ +use crate::derive_data::ReflectMeta; +use proc_macro2::TokenStream; +use quote::quote; +use syn::punctuated::Punctuated; +use syn::{GenericParam, Token}; + +/// Creates a `TokenStream` for generating an expression that creates a `Generics` instance. +/// +/// Returns `None` if `Generics` cannot or should not be generated. +pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option { + if !meta.attrs().type_path_attrs().should_auto_derive() { + // Cannot verify that all generic parameters implement `TypePath` + return None; + } + + let bevy_reflect_path = meta.bevy_reflect_path(); + + let generics = meta + .type_path() + .generics() + .params + .iter() + .filter_map(|param| match param { + GenericParam::Type(ty_param) => { + let ident = &ty_param.ident; + let name = ident.to_string(); + let with_default = ty_param + .default + .as_ref() + .map(|default_ty| quote!(.with_default::<#default_ty>())); + + Some(quote! { + #bevy_reflect_path::GenericInfo::Type( + #bevy_reflect_path::TypeParamInfo::new::<#ident>( + ::std::borrow::Cow::Borrowed(#name), + ) + #with_default + ) + }) + } + GenericParam::Const(const_param) => { + let ty = &const_param.ty; + let name = const_param.ident.to_string(); + let with_default = const_param.default.as_ref().map(|default| { + // We add the `as #ty` to ensure that the correct type is inferred. + quote!(.with_default(#default as #ty)) + }); + + Some(quote! { + #[allow( + clippy::unnecessary_cast, + reason = "reflection requires an explicit type hint for const generics" + )] + #bevy_reflect_path::GenericInfo::Const( + #bevy_reflect_path::ConstParamInfo::new::<#ty>( + ::std::borrow::Cow::Borrowed(#name), + ) + #with_default + ) + }) + } + GenericParam::Lifetime(_) => None, + }) + .collect::>(); + + if generics.is_empty() { + // No generics to generate + return None; + } + + Some(quote!(#bevy_reflect_path::Generics::from_iter([ #generics ]))) +} diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index a54442028d..276371427b 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -25,6 +25,7 @@ mod documentation; mod enum_utility; mod field_attributes; mod from_reflect; +mod generics; mod ident; mod impls; mod meta; diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 87e86d45f8..bdda829d3e 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,7 +1,8 @@ +use crate::generics::impl_generic_info_methods; use crate::{ self as bevy_reflect, type_info::impl_type_methods, utility::reflect_hasher, ApplyError, - MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, - TypeInfo, TypePath, + Generics, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, + ReflectRef, Type, TypeInfo, TypePath, }; use bevy_reflect_derive::impl_type_path; use core::{ @@ -79,6 +80,7 @@ pub trait Array: PartialReflect { #[derive(Clone, Debug)] pub struct ArrayInfo { ty: Type, + generics: Generics, item_info: fn() -> Option<&'static TypeInfo>, item_ty: Type, capacity: usize, @@ -97,6 +99,7 @@ impl ArrayInfo { ) -> Self { Self { ty: Type::of::(), + generics: Generics::new(), item_info: TItem::maybe_type_info, item_ty: Type::of::(), capacity, @@ -138,6 +141,8 @@ impl ArrayInfo { pub fn docs(&self) -> Option<&'static str> { self.docs } + + impl_generic_info_methods!(generics); } /// A fixed-size list of reflected values. diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 086092b520..431b4c3ff3 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,7 +1,8 @@ +use crate::generics::impl_generic_info_methods; use crate::{ attributes::{impl_custom_attribute_methods, CustomAttributes}, type_info::impl_type_methods, - DynamicEnum, PartialReflect, Type, TypePath, VariantInfo, VariantType, + DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType, }; use alloc::sync::Arc; use bevy_utils::HashMap; @@ -138,6 +139,7 @@ pub trait Enum: PartialReflect { #[derive(Clone, Debug)] pub struct EnumInfo { ty: Type, + generics: Generics, variants: Box<[VariantInfo]>, variant_names: Box<[&'static str]>, variant_indices: HashMap<&'static str, usize>, @@ -163,6 +165,7 @@ impl EnumInfo { Self { ty: Type::of::(), + generics: Generics::new(), variants: variants.to_vec().into_boxed_slice(), variant_names, variant_indices, @@ -239,6 +242,8 @@ impl EnumInfo { } impl_custom_attribute_methods!(self.custom_attributes, "enum"); + + impl_generic_info_methods!(generics); } /// An iterator over the fields in the current enum variant. diff --git a/crates/bevy_reflect/src/generics.rs b/crates/bevy_reflect/src/generics.rs new file mode 100644 index 0000000000..4e92e8023a --- /dev/null +++ b/crates/bevy_reflect/src/generics.rs @@ -0,0 +1,338 @@ +use crate::type_info::impl_type_methods; +use crate::{Reflect, Type, TypePath}; +use alloc::borrow::Cow; +use alloc::sync::Arc; +use core::ops::Deref; + +/// The generic parameters of a type. +/// +/// This is automatically generated via the [`Reflect` derive macro] +/// and stored on the [`TypeInfo`] returned by [`Typed::type_info`] +/// for types that have generics. +/// +/// It supports both type parameters and const parameters +/// so long as they implement [`TypePath`]. +/// +/// If the type has no generics, this will be empty. +/// +/// If the type is marked with `#[reflect(type_path = false)]`, +/// the generics will be empty even if the type has generics. +/// +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect +/// [`TypeInfo`]: crate::type_info::TypeInfo +/// [`Typed::type_info`]: crate::Typed::type_info +#[derive(Clone, Default, Debug)] +pub struct Generics(Box<[GenericInfo]>); + +impl Generics { + /// Creates an empty set of generics. + pub fn new() -> Self { + Self(Box::new([])) + } + + /// Finds the generic parameter with the given name. + /// + /// Returns `None` if no such parameter exists. + pub fn get_named(&self, name: &str) -> Option<&GenericInfo> { + // For small sets of generics (the most common case), + // a linear search is often faster using a `HashMap`. + self.0.iter().find(|info| info.name() == name) + } + + /// Adds the given generic parameter to the set. + pub fn with(mut self, info: impl Into) -> Self { + self.0 = IntoIterator::into_iter(self.0) + .chain(core::iter::once(info.into())) + .collect(); + self + } +} + +impl> FromIterator for Generics { + fn from_iter>(iter: I) -> Self { + Self(iter.into_iter().map(Into::into).collect()) + } +} + +impl Deref for Generics { + type Target = [GenericInfo]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// An enum representing a generic parameter. +#[derive(Clone, Debug)] +pub enum GenericInfo { + /// A type parameter. + /// + /// An example would be `T` in `struct Foo`. + Type(TypeParamInfo), + /// A const parameter. + /// + /// An example would be `N` in `struct Foo`. + Const(ConstParamInfo), +} + +impl GenericInfo { + /// The name of the generic parameter. + pub fn name(&self) -> &Cow<'static, str> { + match self { + Self::Type(info) => info.name(), + Self::Const(info) => info.name(), + } + } + + /// Whether the generic parameter is a const parameter. + pub fn is_const(&self) -> bool { + match self { + Self::Type(_) => false, + Self::Const(_) => true, + } + } + + impl_type_methods!(self => { + match self { + Self::Type(info) => info.ty(), + Self::Const(info) => info.ty(), + } + }); +} + +impl From for GenericInfo { + fn from(info: TypeParamInfo) -> Self { + Self::Type(info) + } +} + +impl From for GenericInfo { + fn from(info: ConstParamInfo) -> Self { + Self::Const(info) + } +} + +/// Type information for a generic type parameter. +/// +/// An example of a type parameter would be `T` in `struct Foo`. +#[derive(Clone, Debug)] +pub struct TypeParamInfo { + name: Cow<'static, str>, + ty: Type, + default: Option, +} + +impl TypeParamInfo { + /// Creates a new type parameter with the given name. + pub fn new(name: impl Into>) -> Self { + Self { + name: name.into(), + ty: Type::of::(), + default: None, + } + } + + /// Sets the default type for the parameter. + pub fn with_default(mut self) -> Self { + self.default = Some(Type::of::()); + self + } + + /// The name of the type parameter. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } + + /// The default type for the parameter, if any. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::{GenericInfo, Reflect, Typed}; + /// #[derive(Reflect)] + /// struct Foo(T); + /// + /// let generics = Foo::::type_info().generics(); + /// let GenericInfo::Type(info) = generics.get_named("T").unwrap() else { + /// panic!("expected a type parameter"); + /// }; + /// + /// let default = info.default().unwrap(); + /// + /// assert!(default.is::()); + /// ``` + pub fn default(&self) -> Option<&Type> { + self.default.as_ref() + } + + impl_type_methods!(ty); +} + +/// Type information for a const generic parameter. +/// +/// An example of a const parameter would be `N` in `struct Foo`. +#[derive(Clone, Debug)] +pub struct ConstParamInfo { + name: Cow<'static, str>, + ty: Type, + // Rust currently only allows certain primitive types in const generic position, + // meaning that `Reflect` is guaranteed to be implemented for the default value. + default: Option>, +} + +impl ConstParamInfo { + /// Creates a new const parameter with the given name. + pub fn new(name: impl Into>) -> Self { + Self { + name: name.into(), + ty: Type::of::(), + default: None, + } + } + + /// Sets the default value for the parameter. + pub fn with_default(mut self, default: T) -> Self { + self.default = Some(Arc::new(default)); + self + } + + /// The name of the const parameter. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } + + /// The default value for the parameter, if any. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::{GenericInfo, Reflect, Typed}; + /// #[derive(Reflect)] + /// struct Foo([u8; N]); + /// + /// let generics = Foo::<5>::type_info().generics(); + /// let GenericInfo::Const(info) = generics.get_named("N").unwrap() else { + /// panic!("expected a const parameter"); + /// }; + /// + /// let default = info.default().unwrap(); + /// + /// assert_eq!(default.downcast_ref::().unwrap(), &10); + /// ``` + pub fn default(&self) -> Option<&dyn Reflect> { + self.default.as_deref() + } + + impl_type_methods!(ty); +} + +macro_rules! impl_generic_info_methods { + // Implements both getter and setter methods for the given field. + ($field:ident) => { + $crate::generics::impl_generic_info_methods!(self => &self.$field); + + /// Sets the generic parameters for this type. + pub fn with_generics(mut self, generics: crate::generics::Generics) -> Self { + self.$field = generics; + self + } + }; + // Implements only a getter method for the given expression. + ($self:ident => $expr:expr) => { + /// Gets the generic parameters for this type. + pub fn generics(&$self) -> &crate::generics::Generics { + $expr + } + }; +} + +pub(crate) use impl_generic_info_methods; + +#[cfg(test)] +mod tests { + use super::*; + use crate as bevy_reflect; + use crate::{Reflect, Typed}; + use core::fmt::Debug; + + #[test] + fn should_maintain_order() { + #[derive(Reflect)] + struct Test([(T, U); N]); + + let generics = as Typed>::type_info() + .as_tuple_struct() + .unwrap() + .generics(); + + assert_eq!(generics.len(), 3); + + let mut iter = generics.iter(); + + let t = iter.next().unwrap(); + assert_eq!(t.name(), "T"); + assert!(t.ty().is::()); + assert!(!t.is_const()); + + let u = iter.next().unwrap(); + assert_eq!(u.name(), "U"); + assert!(u.ty().is::()); + assert!(!u.is_const()); + + let n = iter.next().unwrap(); + assert_eq!(n.name(), "N"); + assert!(n.ty().is::()); + assert!(n.is_const()); + + assert!(iter.next().is_none()); + } + + #[test] + fn should_get_by_name() { + #[derive(Reflect)] + enum Test { + Array([(T, U); N]), + } + + let generics = as Typed>::type_info() + .as_enum() + .unwrap() + .generics(); + + let t = generics.get_named("T").unwrap(); + assert_eq!(t.name(), "T"); + assert!(t.ty().is::()); + assert!(!t.is_const()); + + let u = generics.get_named("U").unwrap(); + assert_eq!(u.name(), "U"); + assert!(u.ty().is::()); + assert!(!u.is_const()); + + let n = generics.get_named("N").unwrap(); + assert_eq!(n.name(), "N"); + assert!(n.ty().is::()); + assert!(n.is_const()); + } + + #[test] + fn should_store_defaults() { + #[derive(Reflect)] + struct Test([(T, U); N]); + + let generics = as Typed>::type_info() + .as_tuple_struct() + .unwrap() + .generics(); + + let GenericInfo::Type(u) = generics.get_named("U").unwrap() else { + panic!("expected a type parameter"); + }; + assert_eq!(u.default().unwrap(), &Type::of::()); + + let GenericInfo::Const(n) = generics.get_named("N").unwrap() else { + panic!("expected a const parameter"); + }; + assert_eq!(n.default().unwrap().downcast_ref::().unwrap(), &10); + } +} diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 072eec311d..b10ea1493b 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -5,9 +5,9 @@ use core::any::Any; use crate::{ self as bevy_reflect, utility::GenericTypeInfoCell, ApplyError, FromReflect, FromType, - GetTypeRegistration, List, ListInfo, ListIter, MaybeTyped, PartialReflect, Reflect, - ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, - TypeRegistration, Typed, + Generics, GetTypeRegistration, List, ListInfo, ListIter, MaybeTyped, PartialReflect, Reflect, + ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeParamInfo, + TypePath, TypeRegistration, Typed, }; impl List for SmallVec @@ -183,7 +183,12 @@ where { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| TypeInfo::List(ListInfo::new::())) + CELL.get_or_insert::(|| { + TypeInfo::List( + ListInfo::new::() + .with_generics(Generics::from_iter([TypeParamInfo::new::("T")])), + ) + }) } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 74791e7b22..734486715c 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -8,10 +8,10 @@ use crate::{ set_apply, set_partial_eq, set_try_apply, utility::{reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell}, ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicSet, DynamicTypePath, FromReflect, - FromType, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, MaybeTyped, - OpaqueInfo, PartialReflect, Reflect, ReflectDeserialize, ReflectFromPtr, ReflectFromReflect, - ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set, SetInfo, TypeInfo, - TypePath, TypeRegistration, TypeRegistry, Typed, + FromType, Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, + MaybeTyped, OpaqueInfo, PartialReflect, Reflect, ReflectDeserialize, ReflectFromPtr, + ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set, + SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, TypeRegistry, Typed, }; use alloc::{borrow::Cow, collections::VecDeque}; use bevy_reflect_derive::{impl_reflect, impl_reflect_opaque}; @@ -525,7 +525,13 @@ macro_rules! impl_reflect_for_veclike { impl Typed for $ty { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| TypeInfo::List(ListInfo::new::())) + CELL.get_or_insert::(|| { + TypeInfo::List( + ListInfo::new::().with_generics(Generics::from_iter([ + TypeParamInfo::new::("T") + ])) + ) + }) } } @@ -764,7 +770,14 @@ macro_rules! impl_reflect_for_hashmap { { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| TypeInfo::Map(MapInfo::new::())) + CELL.get_or_insert::(|| { + TypeInfo::Map( + MapInfo::new::().with_generics(Generics::from_iter([ + TypeParamInfo::new::("K"), + TypeParamInfo::new::("V"), + ])), + ) + }) } } @@ -981,7 +994,13 @@ macro_rules! impl_reflect_for_hashset { { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| TypeInfo::Set(SetInfo::new::())) + CELL.get_or_insert::(|| { + TypeInfo::Set( + SetInfo::new::().with_generics(Generics::from_iter([ + TypeParamInfo::new::("V") + ])) + ) + }) } } @@ -1233,7 +1252,14 @@ where { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| TypeInfo::Map(MapInfo::new::())) + CELL.get_or_insert::(|| { + TypeInfo::Map( + MapInfo::new::().with_generics(Generics::from_iter([ + TypeParamInfo::new::("K"), + TypeParamInfo::new::("V"), + ])), + ) + }) } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 033459917f..fcecd5ed8d 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -583,6 +583,7 @@ mod impls { pub mod attributes; mod enums; +mod generics; pub mod serde; pub mod std_traits; #[cfg(feature = "debug_stack")] @@ -610,6 +611,7 @@ pub use array::*; pub use enums::*; pub use fields::*; pub use from_reflect::*; +pub use generics::*; pub use kind::*; pub use list::*; pub use map::*; diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index c4cffc6650..1eea9eaff2 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -6,10 +6,11 @@ use core::{ use bevy_reflect_derive::impl_type_path; +use crate::generics::impl_generic_info_methods; use crate::{ self as bevy_reflect, type_info::impl_type_methods, utility::reflect_hasher, ApplyError, - FromReflect, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, - ReflectRef, Type, TypeInfo, TypePath, + FromReflect, Generics, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, + ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, }; /// A trait used to power [list-like] operations via [reflection]. @@ -114,6 +115,7 @@ pub trait List: PartialReflect { #[derive(Clone, Debug)] pub struct ListInfo { ty: Type, + generics: Generics, item_info: fn() -> Option<&'static TypeInfo>, item_ty: Type, #[cfg(feature = "documentation")] @@ -125,6 +127,7 @@ impl ListInfo { pub fn new() -> Self { Self { ty: Type::of::(), + generics: Generics::new(), item_info: TItem::maybe_type_info, item_ty: Type::of::(), #[cfg(feature = "documentation")] @@ -160,6 +163,8 @@ impl ListInfo { pub fn docs(&self) -> Option<&'static str> { self.docs } + + impl_generic_info_methods!(generics); } /// A list of reflected values. diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 887c92e24e..ffb56a7b02 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -3,9 +3,11 @@ use core::fmt::{Debug, Formatter}; use bevy_reflect_derive::impl_type_path; use bevy_utils::hashbrown::HashTable; +use crate::generics::impl_generic_info_methods; use crate::{ - self as bevy_reflect, type_info::impl_type_methods, ApplyError, MaybeTyped, PartialReflect, - Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, + self as bevy_reflect, type_info::impl_type_methods, ApplyError, Generics, MaybeTyped, + PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, + TypePath, }; /// A trait used to power [map-like] operations via [reflection]. @@ -103,6 +105,7 @@ pub trait Map: PartialReflect { #[derive(Clone, Debug)] pub struct MapInfo { ty: Type, + generics: Generics, key_info: fn() -> Option<&'static TypeInfo>, key_ty: Type, value_info: fn() -> Option<&'static TypeInfo>, @@ -120,6 +123,7 @@ impl MapInfo { >() -> Self { Self { ty: Type::of::(), + generics: Generics::new(), key_info: TKey::maybe_type_info, key_ty: Type::of::(), value_info: TValue::maybe_type_info, @@ -172,6 +176,8 @@ impl MapInfo { pub fn docs(&self) -> Option<&'static str> { self.docs } + + impl_generic_info_methods!(generics); } #[macro_export] diff --git a/crates/bevy_reflect/src/set.rs b/crates/bevy_reflect/src/set.rs index a52181878e..50c5a752d9 100644 --- a/crates/bevy_reflect/src/set.rs +++ b/crates/bevy_reflect/src/set.rs @@ -3,9 +3,11 @@ use core::fmt::{Debug, Formatter}; use bevy_reflect_derive::impl_type_path; use bevy_utils::hashbrown::{hash_table::OccupiedEntry as HashTableOccupiedEntry, HashTable}; +use crate::generics::impl_generic_info_methods; use crate::{ - self as bevy_reflect, hash_error, type_info::impl_type_methods, ApplyError, PartialReflect, - Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, + self as bevy_reflect, hash_error, type_info::impl_type_methods, ApplyError, Generics, + PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, + TypePath, }; /// A trait used to power [set-like] operations via [reflection]. @@ -88,6 +90,7 @@ pub trait Set: PartialReflect { #[derive(Clone, Debug)] pub struct SetInfo { ty: Type, + generics: Generics, value_ty: Type, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -98,6 +101,7 @@ impl SetInfo { pub fn new() -> Self { Self { ty: Type::of::(), + generics: Generics::new(), value_ty: Type::of::(), #[cfg(feature = "documentation")] docs: None, @@ -124,6 +128,8 @@ impl SetInfo { pub fn docs(&self) -> Option<&'static str> { self.docs } + + impl_generic_info_methods!(generics); } /// An ordered set of reflected values. diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 95e3063615..3470e7e4a3 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,9 +1,10 @@ +use crate::generics::impl_generic_info_methods; use crate::{ self as bevy_reflect, attributes::{impl_custom_attribute_methods, CustomAttributes}, type_info::impl_type_methods, - ApplyError, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, - ReflectRef, Type, TypeInfo, TypePath, + ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut, + ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, }; use alloc::{borrow::Cow, sync::Arc}; use bevy_reflect_derive::impl_type_path; @@ -79,6 +80,7 @@ pub trait Struct: PartialReflect { #[derive(Clone, Debug)] pub struct StructInfo { ty: Type, + generics: Generics, fields: Box<[NamedField]>, field_names: Box<[&'static str]>, field_indices: HashMap<&'static str, usize>, @@ -104,6 +106,7 @@ impl StructInfo { Self { ty: Type::of::(), + generics: Generics::new(), fields: fields.to_vec().into_boxed_slice(), field_names, field_indices, @@ -168,6 +171,8 @@ impl StructInfo { } impl_custom_attribute_methods!(self.custom_attributes, "struct"); + + impl_generic_info_methods!(generics); } /// An iterator over the field values of a struct. diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 6ec53efa51..a0bb522c57 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -1,11 +1,12 @@ use bevy_reflect_derive::impl_type_path; use bevy_utils::all_tuples; +use crate::generics::impl_generic_info_methods; use crate::{ self as bevy_reflect, type_info::impl_type_methods, utility::GenericTypePathCell, ApplyError, - FromReflect, GetTypeRegistration, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, - ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, TypeRegistration, TypeRegistry, Typed, - UnnamedField, + FromReflect, Generics, GetTypeRegistration, MaybeTyped, PartialReflect, Reflect, ReflectKind, + ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, TypeRegistration, TypeRegistry, + Typed, UnnamedField, }; use core::{ any::Any, @@ -142,6 +143,7 @@ impl GetTupleField for dyn Tuple { #[derive(Clone, Debug)] pub struct TupleInfo { ty: Type, + generics: Generics, fields: Box<[UnnamedField]>, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -156,6 +158,7 @@ impl TupleInfo { pub fn new(fields: &[UnnamedField]) -> Self { Self { ty: Type::of::(), + generics: Generics::new(), fields: fields.to_vec().into_boxed_slice(), #[cfg(feature = "documentation")] docs: None, @@ -190,6 +193,8 @@ impl TupleInfo { pub fn docs(&self) -> Option<&'static str> { self.docs } + + impl_generic_info_methods!(generics); } /// A tuple which allows fields to be added at runtime. diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 5fad54562e..a38743e8f0 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,11 +1,12 @@ use bevy_reflect_derive::impl_type_path; +use crate::generics::impl_generic_info_methods; use crate::{ self as bevy_reflect, attributes::{impl_custom_attribute_methods, CustomAttributes}, type_info::impl_type_methods, - ApplyError, DynamicTuple, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, - ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField, + ApplyError, DynamicTuple, Generics, PartialReflect, Reflect, ReflectKind, ReflectMut, + ReflectOwned, ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField, }; use alloc::sync::Arc; use core::{ @@ -62,6 +63,7 @@ pub trait TupleStruct: PartialReflect { #[derive(Clone, Debug)] pub struct TupleStructInfo { ty: Type, + generics: Generics, fields: Box<[UnnamedField]>, custom_attributes: Arc, #[cfg(feature = "documentation")] @@ -77,6 +79,7 @@ impl TupleStructInfo { pub fn new(fields: &[UnnamedField]) -> Self { Self { ty: Type::of::(), + generics: Generics::new(), fields: fields.to_vec().into_boxed_slice(), custom_attributes: Arc::new(CustomAttributes::default()), #[cfg(feature = "documentation")] @@ -122,6 +125,8 @@ impl TupleStructInfo { } impl_custom_attribute_methods!(self.custom_attributes, "struct"); + + impl_generic_info_methods!(generics); } /// An iterator over the field values of a tuple struct. diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index aaa4b8861c..9330b8e4aa 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -1,7 +1,7 @@ use crate::{ ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple, - DynamicTupleStruct, EnumInfo, ListInfo, MapInfo, PartialReflect, Reflect, ReflectKind, SetInfo, - StructInfo, TupleInfo, TupleStructInfo, TypePath, TypePathTable, + DynamicTupleStruct, EnumInfo, Generics, ListInfo, MapInfo, PartialReflect, Reflect, + ReflectKind, SetInfo, StructInfo, TupleInfo, TupleStructInfo, TypePath, TypePathTable, }; use core::{ any::{Any, TypeId}, @@ -293,6 +293,20 @@ impl TypeInfo { Self::Opaque(_) => ReflectKind::Opaque, } } + + impl_generic_info_methods!(self => { + match self { + Self::Struct(info) => info.generics(), + Self::TupleStruct(info) => info.generics(), + Self::Tuple(info) => info.generics(), + Self::List(info) => info.generics(), + Self::Array(info) => info.generics(), + Self::Map(info) => info.generics(), + Self::Set(info) => info.generics(), + Self::Enum(info) => info.generics(), + Self::Opaque(info) => info.generics(), + } + }); } macro_rules! impl_cast_method { @@ -467,19 +481,26 @@ impl Hash for Type { } macro_rules! impl_type_methods { + // Generates the type methods based off a single field. ($field:ident) => { + $crate::type_info::impl_type_methods!(self => { + &self.$field + }); + }; + // Generates the type methods based off a custom expression. + ($self:ident => $expr:expr) => { /// The underlying Rust [type]. /// /// [type]: crate::type_info::Type - pub fn ty(&self) -> &$crate::type_info::Type { - &self.$field + pub fn ty(&$self) -> &$crate::type_info::Type { + $expr } /// The [`TypeId`] of this type. /// /// [`TypeId`]: std::any::TypeId pub fn type_id(&self) -> ::core::any::TypeId { - self.$field.id() + self.ty().id() } /// The [stable, full type path] of this type. @@ -489,7 +510,7 @@ macro_rules! impl_type_methods { /// [stable, full type path]: TypePath /// [`type_path_table`]: Self::type_path_table pub fn type_path(&self) -> &'static str { - self.$field.path() + self.ty().path() } /// A representation of the type path of this type. @@ -498,7 +519,7 @@ macro_rules! impl_type_methods { /// /// [`TypePath`]: crate::type_path::TypePath pub fn type_path_table(&self) -> &$crate::type_path::TypePathTable { - &self.$field.type_path_table() + &self.ty().type_path_table() } /// Check if the given type matches this one. @@ -510,11 +531,12 @@ macro_rules! impl_type_methods { /// [`TypeId`]: std::any::TypeId /// [`TypePath`]: crate::type_path::TypePath pub fn is(&self) -> bool { - self.$field.is::() + self.ty().is::() } }; } +use crate::generics::impl_generic_info_methods; pub(crate) use impl_type_methods; /// A container for compile-time info related to reflection-opaque types, including primitives. @@ -528,6 +550,7 @@ pub(crate) use impl_type_methods; #[derive(Debug, Clone)] pub struct OpaqueInfo { ty: Type, + generics: Generics, #[cfg(feature = "documentation")] docs: Option<&'static str>, } @@ -536,6 +559,7 @@ impl OpaqueInfo { pub fn new() -> Self { Self { ty: Type::of::(), + generics: Generics::new(), #[cfg(feature = "documentation")] docs: None, } @@ -554,6 +578,8 @@ impl OpaqueInfo { pub fn docs(&self) -> Option<&'static str> { self.docs } + + impl_generic_info_methods!(generics); } #[cfg(test)] diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index 2457db8faa..f525572671 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -138,7 +138,7 @@ impl Default for NonGenericTypeCell { /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{DynamicTypePath, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField, ApplyError}; +/// # use bevy_reflect::{DynamicTypePath, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField, ApplyError, Generics, TypeParamInfo}; /// use bevy_reflect::utility::GenericTypeInfoCell; /// /// struct Foo(T); @@ -148,7 +148,8 @@ impl Default for NonGenericTypeCell { /// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); /// CELL.get_or_insert::(|| { /// let fields = [UnnamedField::new::(0)]; -/// let info = TupleStructInfo::new::(&fields); +/// let info = TupleStructInfo::new::(&fields) +/// .with_generics(Generics::from_iter([TypeParamInfo::new::("T")])); /// TypeInfo::TupleStruct(info) /// }) /// }