diff --git a/crates/bevy_reflect/derive/src/generics.rs b/crates/bevy_reflect/derive/src/generics.rs index 88b9c3f70b303..48b923df26d34 100644 --- a/crates/bevy_reflect/derive/src/generics.rs +++ b/crates/bevy_reflect/derive/src/generics.rs @@ -8,10 +8,7 @@ use syn::{GenericParam, Token}; /// /// 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 include_generic_type = meta.attrs().type_path_attrs().should_auto_derive(); let bevy_reflect_path = meta.bevy_reflect_path(); @@ -29,11 +26,14 @@ pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option { .as_ref() .map(|default_ty| quote!(.with_default::<#default_ty>())); + let with_type = include_generic_type.then(|| quote!(.with_type::<#ident>())); + Some(quote! { #bevy_reflect_path::GenericInfo::Type( - #bevy_reflect_path::TypeParamInfo::new::<#ident>( + #bevy_reflect_path::TypeParamInfo::new( #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name), ) + #with_type #with_default ) }) @@ -45,6 +45,7 @@ pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option { // We add the `as #ty` to ensure that the correct type is inferred. quote!(.with_default(#default as #ty)) }); + let with_type = include_generic_type.then(|| quote!(.with_type::<#ty>())); Some(quote! { #[allow( @@ -52,9 +53,10 @@ pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option { reason = "reflection requires an explicit type hint for const generics" )] #bevy_reflect_path::GenericInfo::Const( - #bevy_reflect_path::ConstParamInfo::new::<#ty>( + #bevy_reflect_path::ConstParamInfo::new( #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name), ) + #with_type #with_default ) }) diff --git a/crates/bevy_reflect/src/generics.rs b/crates/bevy_reflect/src/generics.rs index 8c9c4816baf5e..54b5f597bb78a 100644 --- a/crates/bevy_reflect/src/generics.rs +++ b/crates/bevy_reflect/src/generics.rs @@ -1,10 +1,80 @@ -use crate::type_info::impl_type_methods; use crate::{Reflect, Type, TypePath}; use alloc::{borrow::Cow, boxed::Box}; use bevy_platform::sync::Arc; use core::ops::Deref; use derive_more::derive::From; +macro_rules! impl_generic_type_methods { + // Generates the type methods based off a single field. + ($field:ident) => { + impl_generic_type_methods!(self => { + self.$field.as_ref() + }); + }; + // Generates the type methods based off a custom expression. + ($self:ident => $expr:expr) => { + /// The underlying Rust [type]. + /// + /// Note: If the type was not originally provided (possibly due to reflection opt-outs), + /// then this method will return `None`. + /// + /// [type]: crate::type_info::Type + pub fn ty(&$self) -> core::option::Option<&$crate::type_info::Type> { + $expr + } + + /// The [`TypeId`] of this type. + /// + /// Note: If the type was not originally provided (possibly due to reflection opt-outs), + /// then this method will return `None`. + /// + /// [`TypeId`]: core::any::TypeId + pub fn type_id(&self) -> core::option::Option<::core::any::TypeId> { + Some(self.ty()?.id()) + } + + /// The [stable, full type path] of this type. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// Note: If the type was not originally provided (possibly due to reflection opt-outs), + /// then this method will return `None`. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> core::option::Option<&'static str> { + Some(self.ty()?.path()) + } + + /// A representation of the type path of this type. + /// + /// Provides dynamic access to all methods on [`TypePath`]. + /// + /// Note: If the type was not originally provided (possibly due to reflection opt-outs), + /// then this method will return `None`. + /// + /// [`TypePath`]: crate::type_path::TypePath + pub fn type_path_table(&self) -> core::option::Option<&$crate::type_path::TypePathTable> { + Some(&self.ty()?.type_path_table()) + } + + /// Check if the given type matches this one. + /// + /// This only compares the [`TypeId`] of the types + /// and does not verify they share the same [`TypePath`] + /// (though it implies they do). + /// + /// Note: If the type was not originally provided (possibly due to reflection opt-outs), + /// then this method will return `None`. + /// + /// [`TypeId`]: core::any::TypeId + /// [`TypePath`]: crate::type_path::TypePath + pub fn is(&self) -> Option { + Some(self.ty()?.is::()) + } + }; +} + /// The generic parameters of a type. /// /// This is automatically generated via the [`Reflect` derive macro] @@ -17,7 +87,7 @@ use derive_more::derive::From; /// 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. +/// the generics will only their capture the parameter names and their [`Type`] information. /// /// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [`TypeInfo`]: crate::type_info::TypeInfo @@ -93,7 +163,7 @@ impl GenericInfo { } } - impl_type_methods!(self => { + impl_generic_type_methods!(self => { match self { Self::Type(info) => info.ty(), Self::Const(info) => info.ty(), @@ -107,20 +177,28 @@ impl GenericInfo { #[derive(Clone, Debug)] pub struct TypeParamInfo { name: Cow<'static, str>, - ty: Type, + ty: Option, default: Option, } impl TypeParamInfo { /// Creates a new type parameter with the given name. - pub fn new(name: impl Into>) -> Self { + pub fn new(name: impl Into>) -> Self { Self { name: name.into(), - ty: Type::of::(), + ty: None, default: None, } } + /// Add a type to the parameter. + pub fn with_type(self) -> Self { + Self { + ty: Some(Type::of::()), + ..self + } + } + /// Sets the default type for the parameter. pub fn with_default(mut self) -> Self { self.default = Some(Type::of::()); @@ -154,7 +232,7 @@ impl TypeParamInfo { self.default.as_ref() } - impl_type_methods!(ty); + impl_generic_type_methods!(ty); } /// Type information for a const generic parameter. @@ -163,7 +241,7 @@ impl TypeParamInfo { #[derive(Clone, Debug)] pub struct ConstParamInfo { name: Cow<'static, str>, - ty: Type, + ty: Option, // 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>, @@ -171,14 +249,22 @@ pub struct ConstParamInfo { impl ConstParamInfo { /// Creates a new const parameter with the given name. - pub fn new(name: impl Into>) -> Self { + pub fn new(name: impl Into>) -> Self { Self { name: name.into(), - ty: Type::of::(), + ty: None, default: None, } } + /// Add a type to the parameter. + pub fn with_type(self) -> Self { + Self { + ty: Some(Type::of::()), + ..self + } + } + /// Sets the default value for the parameter. pub fn with_default(mut self, default: T) -> Self { let arc = Arc::new(default); @@ -224,7 +310,7 @@ impl ConstParamInfo { self.default.as_deref() } - impl_type_methods!(ty); + impl_generic_type_methods!(ty); } macro_rules! impl_generic_info_methods { @@ -272,17 +358,17 @@ mod tests { let t = iter.next().unwrap(); assert_eq!(t.name(), "T"); - assert!(t.ty().is::()); + assert!(t.is::().unwrap()); assert!(!t.is_const()); let u = iter.next().unwrap(); assert_eq!(u.name(), "U"); - assert!(u.ty().is::()); + assert!(u.is::().unwrap()); assert!(!u.is_const()); let n = iter.next().unwrap(); assert_eq!(n.name(), "N"); - assert!(n.ty().is::()); + assert!(n.is::().unwrap()); assert!(n.is_const()); assert!(iter.next().is_none()); @@ -302,17 +388,17 @@ mod tests { let t = generics.get_named("T").unwrap(); assert_eq!(t.name(), "T"); - assert!(t.ty().is::()); + assert!(t.is::().unwrap()); assert!(!t.is_const()); let u = generics.get_named("U").unwrap(); assert_eq!(u.name(), "U"); - assert!(u.ty().is::()); + assert!(u.is::().unwrap()); assert!(!u.is_const()); let n = generics.get_named("N").unwrap(); assert_eq!(n.name(), "N"); - assert!(n.ty().is::()); + assert!(n.is::().unwrap()); assert!(n.is_const()); } @@ -336,4 +422,36 @@ mod tests { }; assert_eq!(n.default().unwrap().downcast_ref::().unwrap(), &10); } + + #[test] + fn should_store_untyped_generics_with_type_path_opt_out() { + #[derive(Reflect)] + #[reflect(type_path = false)] + struct Test(#[reflect(ignore)] T); + + impl TypePath for Test { + fn type_path() -> &'static str { + ::core::any::type_name::() + } + + fn short_type_path() -> &'static str { + "Test" + } + } + + let generics = as Typed>::type_info() + .as_tuple_struct() + .unwrap() + .generics(); + + let t = generics.get_named("T").unwrap(); + assert_eq!(t.name(), "T"); + assert_eq!(t.ty(), None); + assert!(!t.is_const()); + + let n = generics.get_named("N").unwrap(); + assert_eq!(n.name(), "N"); + assert_eq!(n.ty(), None); + assert!(n.is_const()); + } } diff --git a/crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs b/crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs index 241befc9705c2..93068f063919b 100644 --- a/crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs +++ b/crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs @@ -185,8 +185,8 @@ where CELL.get_or_insert::(|| { TypeInfo::Map( MapInfo::new::().with_generics(Generics::from_iter([ - TypeParamInfo::new::("K"), - TypeParamInfo::new::("V"), + TypeParamInfo::new("K").with_type::(), + TypeParamInfo::new("V").with_type::(), ])), ) }) diff --git a/crates/bevy_reflect/src/impls/indexmap.rs b/crates/bevy_reflect/src/impls/indexmap.rs index fa406f0642ff8..dca726cde87d7 100644 --- a/crates/bevy_reflect/src/impls/indexmap.rs +++ b/crates/bevy_reflect/src/impls/indexmap.rs @@ -229,8 +229,8 @@ where CELL.get_or_insert::(|| { TypeInfo::Map( MapInfo::new::().with_generics(Generics::from_iter([ - TypeParamInfo::new::("K"), - TypeParamInfo::new::("V"), + TypeParamInfo::new("K").with_type::(), + TypeParamInfo::new("V").with_type::(), ])), ) }) @@ -460,7 +460,9 @@ where CELL.get_or_insert::(|| { TypeInfo::Set( SetInfo::new::() - .with_generics(Generics::from_iter([TypeParamInfo::new::("T")])), + .with_generics(Generics::from_iter([ + TypeParamInfo::new("T").with_type::() + ])), ) }) } diff --git a/crates/bevy_reflect/src/impls/macros/list.rs b/crates/bevy_reflect/src/impls/macros/list.rs index a628baf647c45..e88cfc2232c3c 100644 --- a/crates/bevy_reflect/src/impls/macros/list.rs +++ b/crates/bevy_reflect/src/impls/macros/list.rs @@ -147,7 +147,7 @@ macro_rules! impl_reflect_for_veclike { CELL.get_or_insert::(|| { $crate::type_info::TypeInfo::List( $crate::list::ListInfo::new::().with_generics($crate::generics::Generics::from_iter([ - $crate::generics::TypeParamInfo::new::("T") + $crate::generics::TypeParamInfo::new("T").with_type::() ])) ) }) diff --git a/crates/bevy_reflect/src/impls/macros/map.rs b/crates/bevy_reflect/src/impls/macros/map.rs index baa7b5f499c3f..88ff85dabe2a2 100644 --- a/crates/bevy_reflect/src/impls/macros/map.rs +++ b/crates/bevy_reflect/src/impls/macros/map.rs @@ -186,8 +186,8 @@ macro_rules! impl_reflect_for_hashmap { CELL.get_or_insert::(|| { $crate::type_info::TypeInfo::Map( $crate::map::MapInfo::new::().with_generics($crate::generics::Generics::from_iter([ - $crate::generics::TypeParamInfo::new::("K"), - $crate::generics::TypeParamInfo::new::("V"), + $crate::generics::TypeParamInfo::new("K").with_type::(), + $crate::generics::TypeParamInfo::new("V").with_type::(), ])), ) }) diff --git a/crates/bevy_reflect/src/impls/macros/set.rs b/crates/bevy_reflect/src/impls/macros/set.rs index 0859998810d67..af137f6853db0 100644 --- a/crates/bevy_reflect/src/impls/macros/set.rs +++ b/crates/bevy_reflect/src/impls/macros/set.rs @@ -151,7 +151,7 @@ macro_rules! impl_reflect_for_hashset { CELL.get_or_insert::(|| { $crate::type_info::TypeInfo::Set( $crate::set::SetInfo::new::().with_generics($crate::generics::Generics::from_iter([ - $crate::generics::TypeParamInfo::new::("V") + $crate::generics::TypeParamInfo::new("V").with_type::() ])) ) }) diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index da430a381b3ea..a72a6165b95df 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -199,7 +199,7 @@ where CELL.get_or_insert::(|| { TypeInfo::List( ListInfo::new::() - .with_generics(Generics::from_iter([TypeParamInfo::new::("T")])), + .with_generics(Generics::from_iter([TypeParamInfo::new("T").with_type::()])), ) }) }