Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions crates/bevy_reflect/derive/src/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TokenStream> {
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();

Expand All @@ -29,11 +26,14 @@ pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option<TokenStream> {
.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
)
})
Expand All @@ -45,16 +45,18 @@ pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option<TokenStream> {
// 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(
clippy::unnecessary_cast,
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
)
})
Expand Down
152 changes: 135 additions & 17 deletions crates/bevy_reflect/src/generics.rs
Original file line number Diff line number Diff line change
@@ -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<T: ::core::any::Any>(&self) -> Option<bool> {
Some(self.ty()?.is::<T>())
}
};
}

/// The generic parameters of a type.
///
/// This is automatically generated via the [`Reflect` derive macro]
Expand All @@ -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
Expand Down Expand Up @@ -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(),
Expand All @@ -107,20 +177,28 @@ impl GenericInfo {
#[derive(Clone, Debug)]
pub struct TypeParamInfo {
name: Cow<'static, str>,
ty: Type,
ty: Option<Type>,
default: Option<Type>,
}

impl TypeParamInfo {
/// Creates a new type parameter with the given name.
pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
ty: Type::of::<T>(),
ty: None,
default: None,
}
}

/// Add a type to the parameter.
pub fn with_type<T: TypePath + ?Sized>(self) -> Self {
Self {
ty: Some(Type::of::<T>()),
..self
}
}

/// Sets the default type for the parameter.
pub fn with_default<T: TypePath + ?Sized>(mut self) -> Self {
self.default = Some(Type::of::<T>());
Expand Down Expand Up @@ -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.
Expand All @@ -163,22 +241,30 @@ impl TypeParamInfo {
#[derive(Clone, Debug)]
pub struct ConstParamInfo {
name: Cow<'static, str>,
ty: Type,
ty: Option<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<Arc<dyn Reflect>>,
}

impl ConstParamInfo {
/// Creates a new const parameter with the given name.
pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
ty: Type::of::<T>(),
ty: None,
default: None,
}
}

/// Add a type to the parameter.
pub fn with_type<T: TypePath + ?Sized>(self) -> Self {
Self {
ty: Some(Type::of::<T>()),
..self
}
}

/// Sets the default value for the parameter.
pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self {
let arc = Arc::new(default);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -272,17 +358,17 @@ mod tests {

let t = iter.next().unwrap();
assert_eq!(t.name(), "T");
assert!(t.ty().is::<f32>());
assert!(t.is::<f32>().unwrap());
assert!(!t.is_const());

let u = iter.next().unwrap();
assert_eq!(u.name(), "U");
assert!(u.ty().is::<String>());
assert!(u.is::<String>().unwrap());
assert!(!u.is_const());

let n = iter.next().unwrap();
assert_eq!(n.name(), "N");
assert!(n.ty().is::<usize>());
assert!(n.is::<usize>().unwrap());
assert!(n.is_const());

assert!(iter.next().is_none());
Expand All @@ -302,17 +388,17 @@ mod tests {

let t = generics.get_named("T").unwrap();
assert_eq!(t.name(), "T");
assert!(t.ty().is::<f32>());
assert!(t.is::<f32>().unwrap());
assert!(!t.is_const());

let u = generics.get_named("U").unwrap();
assert_eq!(u.name(), "U");
assert!(u.ty().is::<String>());
assert!(u.is::<String>().unwrap());
assert!(!u.is_const());

let n = generics.get_named("N").unwrap();
assert_eq!(n.name(), "N");
assert!(n.ty().is::<usize>());
assert!(n.is::<usize>().unwrap());
assert!(n.is_const());
}

Expand All @@ -336,4 +422,36 @@ mod tests {
};
assert_eq!(n.default().unwrap().downcast_ref::<usize>().unwrap(), &10);
}

#[test]
fn should_store_untyped_generics_with_type_path_opt_out() {
#[derive(Reflect)]
#[reflect(type_path = false)]
struct Test<T: Default, const N: usize = 10>(#[reflect(ignore)] T);

impl<T: Default + 'static, const N: usize> TypePath for Test<T, N> {
fn type_path() -> &'static str {
::core::any::type_name::<Self>()
}

fn short_type_path() -> &'static str {
"Test<T, N>"
}
}

let generics = <Test<f32> 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());
}
}
4 changes: 2 additions & 2 deletions crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ where
CELL.get_or_insert::<Self, _>(|| {
TypeInfo::Map(
MapInfo::new::<Self, K, V>().with_generics(Generics::from_iter([
TypeParamInfo::new::<K>("K"),
TypeParamInfo::new::<V>("V"),
TypeParamInfo::new("K").with_type::<K>(),
TypeParamInfo::new("V").with_type::<V>(),
])),
)
})
Expand Down
8 changes: 5 additions & 3 deletions crates/bevy_reflect/src/impls/indexmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ where
CELL.get_or_insert::<Self, _>(|| {
TypeInfo::Map(
MapInfo::new::<Self, K, V>().with_generics(Generics::from_iter([
TypeParamInfo::new::<K>("K"),
TypeParamInfo::new::<V>("V"),
TypeParamInfo::new("K").with_type::<K>(),
TypeParamInfo::new("V").with_type::<V>(),
])),
)
})
Expand Down Expand Up @@ -460,7 +460,9 @@ where
CELL.get_or_insert::<Self, _>(|| {
TypeInfo::Set(
SetInfo::new::<Self, T>()
.with_generics(Generics::from_iter([TypeParamInfo::new::<T>("T")])),
.with_generics(Generics::from_iter([
TypeParamInfo::new("T").with_type::<T>()
])),
)
})
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/impls/macros/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ macro_rules! impl_reflect_for_veclike {
CELL.get_or_insert::<Self, _>(|| {
$crate::type_info::TypeInfo::List(
$crate::list::ListInfo::new::<Self, T>().with_generics($crate::generics::Generics::from_iter([
$crate::generics::TypeParamInfo::new::<T>("T")
$crate::generics::TypeParamInfo::new("T").with_type::<T>()
]))
)
})
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_reflect/src/impls/macros/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ macro_rules! impl_reflect_for_hashmap {
CELL.get_or_insert::<Self, _>(|| {
$crate::type_info::TypeInfo::Map(
$crate::map::MapInfo::new::<Self, K, V>().with_generics($crate::generics::Generics::from_iter([
$crate::generics::TypeParamInfo::new::<K>("K"),
$crate::generics::TypeParamInfo::new::<V>("V"),
$crate::generics::TypeParamInfo::new("K").with_type::<K>(),
$crate::generics::TypeParamInfo::new("V").with_type::<V>(),
])),
)
})
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/impls/macros/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ macro_rules! impl_reflect_for_hashset {
CELL.get_or_insert::<Self, _>(|| {
$crate::type_info::TypeInfo::Set(
$crate::set::SetInfo::new::<Self, V>().with_generics($crate::generics::Generics::from_iter([
$crate::generics::TypeParamInfo::new::<V>("V")
$crate::generics::TypeParamInfo::new("V").with_type::<V>()
]))
)
})
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/impls/smallvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ where
CELL.get_or_insert::<Self, _>(|| {
TypeInfo::List(
ListInfo::new::<Self, T::Item>()
.with_generics(Generics::from_iter([TypeParamInfo::new::<T>("T")])),
.with_generics(Generics::from_iter([TypeParamInfo::new("T").with_type::<T>()])),
)
})
}
Expand Down
Loading