From cf84f70a0fe1d082e0a4b4643c1a8cae67ce3446 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 1 Jun 2026 23:18:58 -0700 Subject: [PATCH 1/7] Reworked type data callbacks --- .../bevy_reflect/derive/src/registration.rs | 17 +- .../derive/src/trait_reflection.rs | 2 + crates/bevy_reflect/src/convert.rs | 11 +- crates/bevy_reflect/src/from_reflect.rs | 4 +- crates/bevy_reflect/src/impls/alloc/borrow.rs | 11 +- .../src/impls/alloc/collections/btree/map.rs | 7 +- crates/bevy_reflect/src/impls/core/panic.rs | 7 +- .../bevy_reflect/src/impls/core/primitives.rs | 9 +- crates/bevy_reflect/src/impls/core/sync.rs | 15 +- crates/bevy_reflect/src/impls/indexmap.rs | 11 +- crates/bevy_reflect/src/impls/macros/list.rs | 7 +- crates/bevy_reflect/src/impls/macros/map.rs | 7 +- crates/bevy_reflect/src/impls/macros/set.rs | 7 +- crates/bevy_reflect/src/impls/smallvec.rs | 5 +- crates/bevy_reflect/src/impls/std/path.rs | 18 +- crates/bevy_reflect/src/lib.rs | 20 +- .../src/serde/de/deserialize_with_registry.rs | 4 +- crates/bevy_reflect/src/serde/de/processor.rs | 6 +- .../bevy_reflect/src/serde/ser/processor.rs | 6 +- .../src/serde/ser/serialize_with_registry.rs | 4 +- crates/bevy_reflect/src/serde/type_data.rs | 4 +- crates/bevy_reflect/src/std_traits.rs | 24 +- crates/bevy_reflect/src/type_data.rs | 559 ++++++++++++++++- crates/bevy_reflect/src/type_registry.rs | 574 ++++++++++++------ 24 files changed, 1040 insertions(+), 299 deletions(-) diff --git a/crates/bevy_reflect/derive/src/registration.rs b/crates/bevy_reflect/derive/src/registration.rs index 8ff6471f4e015..70faff2ba7e75 100644 --- a/crates/bevy_reflect/derive/src/registration.rs +++ b/crates/bevy_reflect/derive/src/registration.rs @@ -28,9 +28,9 @@ pub(crate) fn impl_get_type_registration<'a>( let from_reflect_data = if meta.from_reflect().should_auto_derive() { Some(quote! { - registration.insert( + .insert_data( <#bevy_reflect_path::ReflectFromReflect as #bevy_reflect_path::CreateTypeData>::create_type_data(()) - ); + ) }) } else { None @@ -39,7 +39,7 @@ pub(crate) fn impl_get_type_registration<'a>( let serialization_data = serialization_data.map(|data| { let serialization_data = data.as_serialization_data(bevy_reflect_path); quote! { - registration.insert::<#bevy_reflect_path::serde::SerializationData>(#serialization_data); + .insert_data::<#bevy_reflect_path::serde::SerializationData>(#serialization_data) } }); @@ -56,21 +56,20 @@ pub(crate) fn impl_get_type_registration<'a>( }; quote! { - registration.register_type_data_with::<#reflect_ident, Self, _>(#args); + .register_type_data_with::<#reflect_ident, Self, _>(#args) } }); quote! { impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_path #ty_generics #where_reflect_clause { fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { - let mut registration = #bevy_reflect_path::TypeRegistration::of::(); - registration.insert( - <#bevy_reflect_path::ReflectFromPtr as #bevy_reflect_path::CreateTypeData::>::create_type_data(()) - ); + #bevy_reflect_path::TypeRegistration::of::() + .insert_data( + <#bevy_reflect_path::ReflectFromPtr as #bevy_reflect_path::CreateTypeData::>::create_type_data(()) + ) #from_reflect_data #serialization_data #(#type_data)* - registration } #type_deps_fn diff --git a/crates/bevy_reflect/derive/src/trait_reflection.rs b/crates/bevy_reflect/derive/src/trait_reflection.rs index ce7f26a9b87f2..fc06b7038e3c7 100644 --- a/crates/bevy_reflect/derive/src/trait_reflection.rs +++ b/crates/bevy_reflect/derive/src/trait_reflection.rs @@ -74,6 +74,8 @@ pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStr } } + impl #bevy_reflect_path::TypeData for #reflect_trait_ident {} + impl #bevy_reflect_path::CreateTypeData for #reflect_trait_ident { fn create_type_data(_input: ()) -> Self { Self { diff --git a/crates/bevy_reflect/src/convert.rs b/crates/bevy_reflect/src/convert.rs index 4bc03ec616390..1066fd5ee7aa7 100644 --- a/crates/bevy_reflect/src/convert.rs +++ b/crates/bevy_reflect/src/convert.rs @@ -6,7 +6,7 @@ use core::{any::TypeId, marker::PhantomData}; use bevy_utils::TypeIdMap; -use crate::{Reflect, TypePath}; +use crate::{Reflect, TypeData, TypePath}; /// Provides a mechanism for converting values of one type to another. /// @@ -101,6 +101,8 @@ impl ReflectConvert { } } +impl TypeData for ReflectConvert {} + impl Clone for ReflectConvert { fn clone(&self) -> Self { ReflectConvert { @@ -257,10 +259,9 @@ mod tests { let mut registry = TypeRegistry::default(); registry.add_registration(i32::get_type_registration()); registry.add_registration(String::get_type_registration()); - registry - .get_mut(TypeId::of::()) - .unwrap() - .insert(ReflectConvert::default()); + registry.registration_scope(TypeId::of::(), |mut registration| { + registration.insert_data(ReflectConvert::default()); + }); let reflect_convert = registry .get_type_data::(TypeId::of::()) diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index 534a07a3565c9..d39933f5e86c1 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -1,4 +1,4 @@ -use crate::{CreateTypeData, PartialReflect, Reflect}; +use crate::{CreateTypeData, PartialReflect, Reflect, TypeData}; use alloc::boxed::Box; /// A trait that enables types to be dynamically constructed from reflected data. @@ -117,6 +117,8 @@ impl ReflectFromReflect { } } +impl TypeData for ReflectFromReflect {} + impl CreateTypeData for ReflectFromReflect { fn create_type_data(_input: ()) -> Self { Self { diff --git a/crates/bevy_reflect/src/impls/alloc/borrow.rs b/crates/bevy_reflect/src/impls/alloc/borrow.rs index 0d77bc71c73bb..bbf14f799e3a5 100644 --- a/crates/bevy_reflect/src/impls/alloc/borrow.rs +++ b/crates/bevy_reflect/src/impls/alloc/borrow.rs @@ -123,12 +123,11 @@ impl Typed for Cow<'static, str> { impl GetTypeRegistration for Cow<'static, str> { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::>(); - registration.register_type_data::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::>() + .register_type_data::() + .register_type_data::() + .register_type_data::() + .register_type_data::() } } 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..dfcdce9fce119 100644 --- a/crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs +++ b/crates/bevy_reflect/src/impls/alloc/collections/btree/map.rs @@ -199,10 +199,9 @@ where V: FromReflect + MaybeTyped + TypePath + GetTypeRegistration, { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::() + .register_type_data::() + .register_type_data::() } } diff --git a/crates/bevy_reflect/src/impls/core/panic.rs b/crates/bevy_reflect/src/impls/core/panic.rs index 8ba67c8c4e98f..bb9693504127d 100644 --- a/crates/bevy_reflect/src/impls/core/panic.rs +++ b/crates/bevy_reflect/src/impls/core/panic.rs @@ -149,10 +149,9 @@ impl Typed for &'static Location<'static> { impl GetTypeRegistration for &'static Location<'static> { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::() + .register_type_data::() + .register_type_data::() } } diff --git a/crates/bevy_reflect/src/impls/core/primitives.rs b/crates/bevy_reflect/src/impls/core/primitives.rs index 6dfe41214a390..f298f2966950a 100644 --- a/crates/bevy_reflect/src/impls/core/primitives.rs +++ b/crates/bevy_reflect/src/impls/core/primitives.rs @@ -443,11 +443,10 @@ impl Typed for &'static str { impl GetTypeRegistration for &'static str { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::() + .register_type_data::() + .register_type_data::() + .register_type_data::() } } diff --git a/crates/bevy_reflect/src/impls/core/sync.rs b/crates/bevy_reflect/src/impls/core/sync.rs index a1d0b07b03410..f6852f5047df3 100644 --- a/crates/bevy_reflect/src/impls/core/sync.rs +++ b/crates/bevy_reflect/src/impls/core/sync.rs @@ -22,17 +22,16 @@ macro_rules! impl_reflect_for_atomic { impl GetTypeRegistration for $ty { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration.register_type_data::(); + let registration = TypeRegistration::of::() + .register_type_data::() + .register_type_data::() + .register_type_data::(); // Serde only supports atomic types when the "std" feature is enabled #[cfg(feature = "std")] - { - registration.register_type_data::(); - registration.register_type_data::(); - } + let registration = registration + .register_type_data::() + .register_type_data::(); registration } diff --git a/crates/bevy_reflect/src/impls/indexmap.rs b/crates/bevy_reflect/src/impls/indexmap.rs index fa406f0642ff8..68b5b4b89909a 100644 --- a/crates/bevy_reflect/src/impls/indexmap.rs +++ b/crates/bevy_reflect/src/impls/indexmap.rs @@ -265,10 +265,9 @@ where S: TypePath + BuildHasher + Send + Sync + Default, { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::() + .register_type_data::() + .register_type_data::() } fn register_type_dependencies(registry: &mut TypeRegistry) { @@ -490,9 +489,7 @@ where S: TypePath + BuildHasher + Default + Send + Sync, { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration + TypeRegistration::of::().register_type_data::() } } diff --git a/crates/bevy_reflect/src/impls/macros/list.rs b/crates/bevy_reflect/src/impls/macros/list.rs index a628baf647c45..dd59e965260e7 100644 --- a/crates/bevy_reflect/src/impls/macros/list.rs +++ b/crates/bevy_reflect/src/impls/macros/list.rs @@ -158,10 +158,9 @@ macro_rules! impl_reflect_for_veclike { for $ty { fn get_type_registration() -> $crate::type_registry::TypeRegistration { - let mut registration = $crate::type_registry::TypeRegistration::of::<$ty>(); - registration.register_type_data::<$crate::type_registry::ReflectFromPtr, $ty>(); - registration.register_type_data::<$crate::from_reflect::ReflectFromReflect, $ty>(); - registration + $crate::type_registry::TypeRegistration::of::<$ty>() + .register_type_data::<$crate::type_registry::ReflectFromPtr, $ty>() + .register_type_data::<$crate::from_reflect::ReflectFromReflect, $ty>() } fn register_type_dependencies(registry: &mut $crate::type_registry::TypeRegistry) { diff --git a/crates/bevy_reflect/src/impls/macros/map.rs b/crates/bevy_reflect/src/impls/macros/map.rs index baa7b5f499c3f..40f7057384a75 100644 --- a/crates/bevy_reflect/src/impls/macros/map.rs +++ b/crates/bevy_reflect/src/impls/macros/map.rs @@ -201,10 +201,9 @@ macro_rules! impl_reflect_for_hashmap { S: $crate::type_path::TypePath + core::hash::BuildHasher + Default + Send + Sync + Default, { fn get_type_registration() -> $crate::type_registry::TypeRegistration { - let mut registration = $crate::type_registry::TypeRegistration::of::(); - registration.register_type_data::<$crate::type_registry::ReflectFromPtr, Self>(); - registration.register_type_data::<$crate::from_reflect::ReflectFromReflect, Self>(); - registration + $crate::type_registry::TypeRegistration::of::() + .register_type_data::<$crate::type_registry::ReflectFromPtr, Self>() + .register_type_data::<$crate::from_reflect::ReflectFromReflect, Self>() } fn register_type_dependencies(registry: &mut $crate::type_registry::TypeRegistry) { diff --git a/crates/bevy_reflect/src/impls/macros/set.rs b/crates/bevy_reflect/src/impls/macros/set.rs index 0859998810d67..eaaf8d44c469a 100644 --- a/crates/bevy_reflect/src/impls/macros/set.rs +++ b/crates/bevy_reflect/src/impls/macros/set.rs @@ -164,10 +164,9 @@ macro_rules! impl_reflect_for_hashset { S: $crate::type_path::TypePath + core::hash::BuildHasher + Default + Send + Sync + Default, { fn get_type_registration() -> $crate::type_registry::TypeRegistration { - let mut registration = $crate::type_registry::TypeRegistration::of::(); - registration.register_type_data::<$crate::type_registry::ReflectFromPtr, Self>(); - registration.register_type_data::<$crate::from_reflect::ReflectFromReflect, Self>(); - registration + $crate::type_registry::TypeRegistration::of::() + .register_type_data::<$crate::type_registry::ReflectFromPtr, Self>() + .register_type_data::<$crate::from_reflect::ReflectFromReflect, Self>() } fn register_type_dependencies(registry: &mut $crate::type_registry::TypeRegistry) { diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index da430a381b3ea..9a172d0dafc09 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -229,9 +229,8 @@ where T::Item: FromReflect + MaybeTyped + TypePath, { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::>(); - registration.register_type_data::(); - registration + let registration = TypeRegistration::of::>(); + registration.register_type_data::() } } diff --git a/crates/bevy_reflect/src/impls/std/path.rs b/crates/bevy_reflect/src/impls/std/path.rs index fa376a084af7f..e045f8f9b34ac 100644 --- a/crates/bevy_reflect/src/impls/std/path.rs +++ b/crates/bevy_reflect/src/impls/std/path.rs @@ -155,10 +155,9 @@ impl Typed for &'static Path { impl GetTypeRegistration for &'static Path { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::() + .register_type_data::() + .register_type_data::() } } @@ -306,12 +305,11 @@ impl FromReflect for Cow<'static, Path> { impl GetTypeRegistration for Cow<'static, Path> { fn get_type_registration() -> TypeRegistration { - let mut registration = TypeRegistration::of::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration.register_type_data::(); - registration + TypeRegistration::of::() + .register_type_data::() + .register_type_data::() + .register_type_data::() + .register_type_data::() } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index d7b7ed67f0b5a..67fd01e402fa6 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -378,8 +378,8 @@ //! ``` //! //! The generated type data can be used to convert a valid `dyn Reflect` into a `dyn MyTrait`. -//! See the [dynamic types example](https://github.com/bevyengine/bevy/blob/latest/examples/reflection/dynamic_types.rs) -//! for more information and usage details. +//! See the [type_data] module for details or the [dynamic types example](https://github.com/bevyengine/bevy/blob/latest/examples/reflection/type_data.rs) +//! for additional examples. //! //! # Serialization //! @@ -613,7 +613,7 @@ pub mod set; pub mod structs; pub mod tuple; pub mod tuple_struct; -mod type_data; +pub mod type_data; mod type_info; mod type_path; mod type_registry; @@ -4121,19 +4121,25 @@ bevy_reflect::tests::Test { #[derive(Clone)] struct ReflectA; + impl TypeData for ReflectA { + fn on_insert(&self) -> Option { + Some(|mut registration| { + registration.insert_data(ReflectB); + }) + } + } + impl CreateTypeData for ReflectA { fn create_type_data(_input: ()) -> Self { ReflectA } - - fn insert_dependencies(type_registration: &mut TypeRegistration) { - type_registration.insert(ReflectB); - } } #[derive(Clone)] struct ReflectB; + impl TypeData for ReflectB {} + let mut registry = TypeRegistry::new(); registry.register::(); diff --git a/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs b/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs index b6643bf3592f2..6ab262a42cf42 100644 --- a/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs +++ b/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs @@ -1,5 +1,5 @@ use crate::serde::de::error_utils::make_custom_error; -use crate::{CreateTypeData, PartialReflect, TypeRegistry}; +use crate::{CreateTypeData, PartialReflect, TypeData, TypeRegistry}; use alloc::boxed::Box; use serde::Deserializer; @@ -74,6 +74,8 @@ impl ReflectDeserializeWithRegistry { } } +impl TypeData for ReflectDeserializeWithRegistry {} + impl DeserializeWithRegistry<'de>> CreateTypeData for ReflectDeserializeWithRegistry { diff --git a/crates/bevy_reflect/src/serde/de/processor.rs b/crates/bevy_reflect/src/serde/de/processor.rs index 98bcfa09b55b9..abc84cc7664ea 100644 --- a/crates/bevy_reflect/src/serde/de/processor.rs +++ b/crates/bevy_reflect/src/serde/de/processor.rs @@ -62,11 +62,7 @@ use alloc::boxed::Box; /// # } /// # /// # struct ReflectHandle; -/// # impl TypeData for ReflectHandle { -/// # fn clone_type_data(&self) -> Box { -/// # unimplemented!() -/// # } -/// # } +/// # impl TypeData for ReflectHandle {} /// # impl ReflectHandle { /// # fn asset_type_id(&self) { /// # unimplemented!() diff --git a/crates/bevy_reflect/src/serde/ser/processor.rs b/crates/bevy_reflect/src/serde/ser/processor.rs index fc35ff883ac74..8351c56ef7063 100644 --- a/crates/bevy_reflect/src/serde/ser/processor.rs +++ b/crates/bevy_reflect/src/serde/ser/processor.rs @@ -53,11 +53,7 @@ use crate::{PartialReflect, TypeRegistry}; /// # struct Mesh; /// # /// # struct ReflectHandle; -/// # impl TypeData for ReflectHandle { -/// # fn clone_type_data(&self) -> Box { -/// # unimplemented!() -/// # } -/// # } +/// # impl TypeData for ReflectHandle {} /// # impl ReflectHandle { /// # fn downcast_handle_untyped(&self, handle: &(dyn Any + 'static)) -> Option { /// # unimplemented!() diff --git a/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs b/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs index 6d05ea5ab4e7f..75b37229730a3 100644 --- a/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs +++ b/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs @@ -1,4 +1,4 @@ -use crate::{CreateTypeData, Reflect, TypeRegistry}; +use crate::{CreateTypeData, Reflect, TypeData, TypeRegistry}; use alloc::boxed::Box; use serde::{Serialize, Serializer}; @@ -72,6 +72,8 @@ impl ReflectSerializeWithRegistry { } } +impl TypeData for ReflectSerializeWithRegistry {} + impl CreateTypeData for ReflectSerializeWithRegistry { fn create_type_data(_input: ()) -> Self { Self { diff --git a/crates/bevy_reflect/src/serde/type_data.rs b/crates/bevy_reflect/src/serde/type_data.rs index cb398bab22c19..df106ffee6ae7 100644 --- a/crates/bevy_reflect/src/serde/type_data.rs +++ b/crates/bevy_reflect/src/serde/type_data.rs @@ -1,4 +1,4 @@ -use crate::Reflect; +use crate::{Reflect, TypeData}; use alloc::boxed::Box; use bevy_platform::collections::{hash_map::Iter, HashMap}; @@ -113,6 +113,8 @@ impl SerializationData { } } +impl TypeData for SerializationData {} + /// Data needed for (de)serialization of a skipped field. #[derive(Debug, Clone)] pub struct SkippedField { diff --git a/crates/bevy_reflect/src/std_traits.rs b/crates/bevy_reflect/src/std_traits.rs index f151b6a5a1a7a..b76b67d4796ff 100644 --- a/crates/bevy_reflect/src/std_traits.rs +++ b/crates/bevy_reflect/src/std_traits.rs @@ -3,7 +3,7 @@ use alloc::boxed::Box; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign}; -use crate::{CreateTypeData, PartialReflect, Reflect}; +use crate::{CreateTypeData, PartialReflect, Reflect, TypeData}; /// A struct used to provide the default value of a type. /// @@ -20,6 +20,8 @@ impl ReflectDefault { } } +impl TypeData for ReflectDefault {} + impl CreateTypeData for ReflectDefault { fn create_type_data(_input: ()) -> Self { ReflectDefault { @@ -56,6 +58,8 @@ impl ReflectAdd { } } +impl TypeData for ReflectAdd {} + impl> CreateTypeData for ReflectAdd { fn create_type_data(_input: ()) -> Self { ReflectAdd { @@ -106,6 +110,8 @@ impl ReflectSub { } } +impl TypeData for ReflectSub {} + impl> CreateTypeData for ReflectSub { fn create_type_data(_input: ()) -> Self { ReflectSub { @@ -156,6 +162,8 @@ impl ReflectMul { } } +impl TypeData for ReflectMul {} + impl> CreateTypeData for ReflectMul { fn create_type_data(_input: ()) -> Self { ReflectMul { @@ -206,6 +214,8 @@ impl ReflectDiv { } } +impl TypeData for ReflectDiv {} + impl> CreateTypeData for ReflectDiv { fn create_type_data(_input: ()) -> Self { ReflectDiv { @@ -257,6 +267,8 @@ impl ReflectRem { } } +impl TypeData for ReflectRem {} + impl> CreateTypeData for ReflectRem { fn create_type_data(_input: ()) -> Self { ReflectRem { @@ -307,6 +319,8 @@ impl ReflectAddAssign { } } +impl TypeData for ReflectAddAssign {} + impl CreateTypeData for ReflectAddAssign { fn create_type_data(_input: ()) -> Self { ReflectAddAssign { @@ -353,6 +367,8 @@ impl ReflectSubAssign { } } +impl TypeData for ReflectSubAssign {} + impl CreateTypeData for ReflectSubAssign { fn create_type_data(_input: ()) -> Self { ReflectSubAssign { @@ -399,6 +415,8 @@ impl ReflectMulAssign { } } +impl TypeData for ReflectMulAssign {} + impl CreateTypeData for ReflectMulAssign { fn create_type_data(_input: ()) -> Self { ReflectMulAssign { @@ -445,6 +463,8 @@ impl ReflectDivAssign { } } +impl TypeData for ReflectDivAssign {} + impl CreateTypeData for ReflectDivAssign { fn create_type_data(_input: ()) -> Self { ReflectDivAssign { @@ -491,6 +511,8 @@ impl ReflectRemAssign { } } +impl TypeData for ReflectRemAssign {} + impl CreateTypeData for ReflectRemAssign { fn create_type_data(_input: ()) -> Self { ReflectRemAssign { diff --git a/crates/bevy_reflect/src/type_data.rs b/crates/bevy_reflect/src/type_data.rs index 7f9d03ba3ed9c..74417f7598249 100644 --- a/crates/bevy_reflect/src/type_data.rs +++ b/crates/bevy_reflect/src/type_data.rs @@ -1,46 +1,191 @@ -use ::alloc::boxed::Box; -use bevy_reflect::TypeRegistration; +//! Type data for type registrations. +//! +//! Type data is extra metadata that can be added to a type's [`TypeRegistration`]. +//! Most often this is used to provide information about a type's trait implementation dynamically, +//! but it may be used for other things such as specifying configuration values, opt-ins, and more. +//! +//! # Manual Type Data Creation +//! +//! Any type can be used as type data so long as it implements [`TypeData`]. +//! The following code demonstrates how we might define type data that marks a type as "debuggable" +//! (note that this would probably be better handled through [custom attributes], +//! so this is just for demonstration purposes). +//! +//! It's customary to prefix all type data with `Reflect` as this allows it to be used with +//! the [derive macro](derive@crate::Reflect). +//! +//! ```rust +//! # use bevy_reflect::TypeData; +//! struct ReflectDebuggable; +//! impl TypeData for ReflectDebuggable {} +//! +//! ``` +//! +//! We can then register our type data on any type we want: +//! +//! ``` +//! # use bevy_reflect::{TypeData, TypeRegistration, TypeRegistry}; +//! # struct ReflectDebuggable; +//! # impl TypeData for ReflectDebuggable {} +//! # let mut registry = TypeRegistry::empty(); +//! let registration = TypeRegistration::of::() +//! .insert_data(ReflectDebuggable); +//! registry.add_registration(registration); +//! ``` +//! +//! ## Using `CreateTypeData` +//! +//! In order to be compatible with [`TypeRegistry::register_type_data`], +//! we will also want to implement [`CreateTypeData`] for our struct. +//! This is usually done through a blanket implementation, +//! and allows us to add other requirements for usage. +//! For example, we can adjust out `ReflectDebuggable` to actually make use of [`Debug`]. +//! +//! ``` +//! # use core::fmt::Debug; +//! # use core::any::TypeId; +//! # use bevy_reflect::{CreateTypeData, Reflect, TypeData, TypeRegistration, TypeRegistry}; +//! struct ReflectDebuggable { +//! debug: fn(&dyn Reflect) -> String, +//! } +//! impl TypeData for ReflectDebuggable {} +//! +//! impl ReflectDebuggable { +//! // Helper method for calling our `debug` function. +//! fn debug(&self, value: &T) -> String { +//! (self.debug)(value) +//! } +//! } +//! +//! impl CreateTypeData for ReflectDebuggable { +//! fn create_type_data(input: ()) -> Self { +//! Self { +//! debug: |value| { +//! let value = value .downcast_ref::().unwrap(); +//! format!("{:?}", value) +//! } +//! } +//! } +//! } +//! +//! # let mut registry = TypeRegistry::empty(); +//! # registry.register::>(); +//! // ... +//! +//! registry.register_type_data::, ReflectDebuggable>(); +//! let data = registry.get_type_data::(TypeId::of::>()).unwrap(); +//! assert_eq!(data.debug(&vec![1, 2, 3]), "[1, 2, 3]"); +//! ``` +//! +//! [`CreateTypeData`] can also be created with input if needed. +//! Here, we didn't need input so we used the default input type of `()`. +//! +//! # Automatic Trait Reflection +//! +//! Because it's so commonplace to want to transform trait implementations into type data, +//! this crate provides a macro for doing just that called [`reflect_trait`]. +//! +//! It can be used on any [dyn-compatible] trait and generates a `Reflect`-prefixed type data struct +//! that allows a reflected type to be cast into its trait object. +//! +//! ``` +//! # use core::any::TypeId; +//! # use bevy_reflect::{Reflect, TypeRegistry, reflect_trait}; +//! // This will generate a type data struct called `ReflectShout`. +//! #[reflect_trait] +//! trait Shout { +//! fn shout(&self) -> String; +//! } +//! +//! impl Shout for String { +//! fn shout(&self) -> String { +//! format!("{}!!!", self) +//! } +//! } +//! +//! # let mut registry = TypeRegistry::new(); +//! // ... +//! registry.register_type_data::(); +//! let data = registry.get_type_data::(TypeId::of::()).unwrap(); +//! +//! let value: Box = Box::new(String::from("Hello, world")); +//! let obj: &dyn Shout = data.get(&*value).unwrap(); +//! assert_eq!(obj.shout(), "Hello, world!!!"); +//! ``` +//! +//! # Callbacks +//! +//! Both [`TypeData`] and [`CreateTypeData`] provide mechanisms for specifying registration callbacks. +//! The possible callbacks are: +//! +//! - `on_insert`: Triggered when the type data is inserted into a [`TypeRegistration`]. +//! - `on_register`: Triggered when a [`TypeRegistration`] containing the type data is registered into a [`TypeRegistry`]. +//! If the [`TypeRegistration`] is already registered, then it will be triggered immediately. +//! +//! Callbacks are defined on both traits to provide as much flexibility as possible. +//! The callbacks on [`TypeData`] are always run and are intrinsic to the type itself. +//! Define your callback here if it doesn't rely on knowledge of the type it's being registered on. +//! The callbacks also have access to `&self`, which allows function pointers to be stored on the type data +//! and returned by these methods. +//! +//! The callbacks on [`CreateTypeData`] are associated functions that have access to the type they're +//! being registered on. +//! Note that these callbacks are only invoked when registered directly on a [`TypeRegistration`] or [`TypeRegistry`]. +//! Calling [`CreateTypeData::create_type_data`] and inserting the return value will result in these +//! callbacks not being triggered. +//! +//! [`reflect_trait`]: bevy_reflect_derive::reflect_trait +//! [dyn-compatible]: https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility + +use crate::{TypeRegistration, TypeRegistrationMut, TypeRegistry}; use downcast_rs::{impl_downcast, Downcast}; +/// Type alias representing the callback function for when [`TypeData`] is inserted into a [`TypeRegistration`]. +pub type OnInsertTypeData = fn(registration: TypeRegistrationMut<'_>); + +/// Type alias representing the callback function for when a [`TypeRegistration`] is registered into a [`TypeRegistry`]. +pub type OnRegisterTypeData = fn(registry: &mut TypeRegistry); + /// A trait for representing type metadata. /// /// Type data can be registered to the [`TypeRegistry`] and stored on a type's [`TypeRegistration`]. /// /// While type data is often generated using the [`#[reflect_trait]`](crate::reflect_trait) macro, -/// almost any type that implements [`Clone`] can be considered "type data". -/// This is because it has a blanket implementation over all `T` where `T: Clone + Send + Sync + 'static`. +/// any type that implements this trait can be considered "type data". /// -/// For creating your own type data, see the [`CreateTypeData`] trait. +/// For creating your own type data generically or based on a specific type, +/// see the [`CreateTypeData`] trait. /// -/// See the [crate-level documentation] for more information on type data and type registration. +/// See the [module-level documentation] for more information on type data. /// /// [`TypeRegistry`]: crate::TypeRegistry /// [`TypeRegistration`]: crate::TypeRegistration -/// [crate-level documentation]: crate +/// [module-level documentation]: crate::type_data pub trait TypeData: Downcast + Send + Sync { - /// Creates a type-erased clone of `self`. - fn clone_type_data(&self) -> Box; -} -impl_downcast!(TypeData); + /// Optional callback for when this type data is inserted into a [`TypeRegistration`]. + fn on_insert(&self) -> Option { + None + } -impl TypeData for T -where - T: Clone, -{ - fn clone_type_data(&self) -> Box { - Box::new(self.clone()) + /// Optional callback for when the [`TypeRegistration`] this type data belongs to is + /// registered into a [`TypeRegistry`]. + /// + /// Note that if this type data is inserted into a [`TypeRegistration`] that already belongs to a [`TypeRegistry`], + /// then this should trigger immediately. + fn on_register(&self) -> Option { + None } } +impl_downcast!(TypeData); /// A trait for creating [`TypeData`]. /// -/// Normally any type that is `Clone + Send + Sync + 'static` can be used as type data -/// and inserted into a [`TypeRegistration`] using [`TypeRegistration::insert`] -/// However, only types that implement this trait may be registered using [`TypeRegistry::register_type_data`], -/// [`TypeRegistry::register_type_data_with`], or via the `#[reflect(MyTrait)]` attribute with the [`Reflect` derive macro]. +/// Normally any type that implements [`TypeData`] can be inserted into a [`TypeRegistration`] using [`TypeRegistration::insert_data`]. +/// However, only types that implement this trait may be registered using [`TypeRegistration::register_type_data`], +/// [`TypeRegistration::register_type_data_with`], or via the `#[reflect(MyTrait)]` attribute with the [`Reflect` derive macro]. /// /// Note that in order to work with the `#[reflect(MyTrait)]` attribute, -/// implementors must be named with the `Reflect` prefix (e.g. `ReflectMyTrait`). +/// implementors must be named with the `Reflect` prefix (e.g., `ReflectMyTrait`). /// /// # Input /// @@ -48,7 +193,7 @@ where /// (the `Input` type parameter defaults to `()`). /// /// However, implementors may choose to implement this trait with other input types. -/// As long as the implementations don't conflict, multiple different input types can be specified. +/// As long as the implementations don't conflict, multiple different input types may be specified. /// /// # Example /// @@ -140,5 +285,373 @@ pub trait CreateTypeData: TypeData { unused_variables, reason = "default implementation does not have any dependencies" )] + #[deprecated( + since = "0.21.0", + note = "This function will be removed in a future release. Use either `CreateTypeData::on_insert` or `TypeData::on_insert` instead." + )] fn insert_dependencies(type_registration: &mut TypeRegistration) {} + + /// Optional callback for when this type data is created and inserted into a [`TypeRegistration`]. + fn on_insert() -> Option { + None + } + + /// Optional callback for when the [`TypeRegistration`] the created type data belongs to is + /// registered into a [`TypeRegistry`]. + /// + /// Note that if this type data is inserted into a [`TypeRegistration`] that already belongs to a [`TypeRegistry`], + /// then this should trigger immediately. + fn on_register() -> Option { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate as bevy_reflect; + use crate::{Reflect, TypeRegistration}; + use core::any::TypeId; + use core::marker::PhantomData; + + #[test] + fn should_register_dependencies_on_insert() { + #[derive(Reflect)] + struct Foo; + + struct ReflectA; + impl TypeData for ReflectA { + fn on_insert(&self) -> Option { + Some(|mut registration| { + registration.insert_data(ReflectB); + }) + } + } + + struct ReflectB; + impl TypeData for ReflectB {} + + let registration = TypeRegistration::of::().insert_data(ReflectA); + assert!(registration.contains::()); + assert!(registration.contains::()); + } + + #[test] + fn should_register_dependencies_on_create_type_data_insert() { + #[derive(Reflect)] + struct Foo; + + struct ReflectA(PhantomData); + impl TypeData for ReflectA {} + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::, _>(); + }) + } + } + + struct ReflectB(PhantomData); + impl TypeData for ReflectB {} + + impl CreateTypeData for ReflectB { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + } + + let registration = TypeRegistration::of::().register_type_data::, _>(); + assert_eq!(registration.len(), 2); + assert!(registration.contains::>()); + assert!(registration.contains::>()); + } + + #[test] + fn should_register_dependencies_on_insert_from_registry() { + #[derive(Reflect)] + struct Foo; + + struct ReflectA; + impl TypeData for ReflectA { + fn on_insert(&self) -> Option { + Some(|mut registration| { + registration.insert_data(ReflectB); + }) + } + } + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self + } + } + + struct ReflectB; + impl TypeData for ReflectB {} + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register_type_data::(); + + let registration = registry.get(TypeId::of::()).unwrap(); + assert!(registration.contains::()); + assert!(registration.contains::()); + } + + #[test] + fn should_register_dependencies_on_create_type_data_insert_from_registry() { + #[derive(Reflect)] + struct Foo; + + struct ReflectA(PhantomData); + impl TypeData for ReflectA {} + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::, _>(); + }) + } + } + + struct ReflectB(PhantomData); + impl TypeData for ReflectB {} + + impl CreateTypeData for ReflectB { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + } + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register_type_data::>(); + + let registration = registry.get(TypeId::of::()).unwrap(); + assert!(registration.contains::>()); + assert!(registration.contains::>()); + } + + #[test] + fn should_handle_dependency_cycles_on_insert() { + #[derive(Reflect)] + struct Foo; + + struct ReflectA; + impl TypeData for ReflectA { + fn on_insert(&self) -> Option { + Some(|mut registration| { + registration.insert_data(ReflectB); + }) + } + } + + struct ReflectB; + impl TypeData for ReflectB { + fn on_insert(&self) -> Option { + Some(|mut registration| { + registration.insert_data(ReflectC); + }) + } + } + + struct ReflectC; + impl TypeData for ReflectC { + fn on_insert(&self) -> Option { + Some(|mut registration| { + registration.insert_data(ReflectA); + }) + } + } + + let registration = TypeRegistration::of::().insert_data(ReflectA); + assert!(registration.contains::()); + assert!(registration.contains::()); + assert!(registration.contains::()); + } + + #[test] + fn should_handle_dependency_cycles_on_create_type_data_insert() { + #[derive(Reflect)] + struct Foo; + + struct ReflectA(PhantomData); + impl TypeData for ReflectA {} + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::, _>(); + }) + } + } + + struct ReflectB(PhantomData); + impl TypeData for ReflectB {} + + impl CreateTypeData for ReflectB { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::, _>(); + }) + } + } + + struct ReflectC(PhantomData); + impl TypeData for ReflectC {} + + impl CreateTypeData for ReflectC { + fn create_type_data(_: ()) -> Self { + Self(PhantomData) + } + + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::, _>(); + }) + } + } + + let registration = TypeRegistration::of::().register_type_data::, _>(); + assert!(registration.contains::>()); + assert!(registration.contains::>()); + assert!(registration.contains::>()); + } + + #[test] + fn should_register_dependencies_on_register() { + #[derive(Reflect)] + struct Foo; + + #[derive(Reflect)] + struct Bar; + + struct ReflectA; + impl TypeData for ReflectA { + fn on_register(&self) -> Option { + Some(|registry| { + registry.register::(); + }) + } + } + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self + } + } + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register_type_data::(); + + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + } + + #[test] + fn should_register_dependencies_on_create_type_data_register() { + #[derive(Reflect)] + struct Foo; + + #[derive(Reflect)] + struct Bar; + + struct ReflectA; + impl TypeData for ReflectA {} + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self + } + + fn on_register() -> Option { + Some(|registry| { + registry.register::(); + }) + } + } + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register_type_data::(); + + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + } + + #[test] + fn should_register_dependencies_on_deferred_register() { + #[derive(Reflect)] + struct Foo; + + #[derive(Reflect)] + struct Bar; + + struct ReflectA; + impl TypeData for ReflectA { + fn on_register(&self) -> Option { + Some(|registry| { + registry.register::(); + }) + } + } + + let registration = TypeRegistration::of::().insert_data(ReflectA); + + let mut registry = TypeRegistry::empty(); + registry.add_registration(registration); + + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + } + + #[test] + fn should_register_dependencies_on_deferred_create_type_data_register() { + #[derive(Reflect)] + struct Foo; + + #[derive(Reflect)] + struct Bar; + + struct ReflectA; + impl TypeData for ReflectA {} + + impl CreateTypeData for ReflectA { + fn create_type_data(_: ()) -> Self { + Self + } + + fn on_register() -> Option { + Some(|registry| { + registry.register::(); + }) + } + } + + let registration = TypeRegistration::of::().register_type_data::(); + + let mut registry = TypeRegistry::empty(); + registry.add_registration(registration); + + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + } } diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 45be0f653af4d..7373687b4f13e 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,8 +1,9 @@ +use crate::type_data::{OnInsertTypeData, OnRegisterTypeData}; use crate::{ convert::ReflectConvert, serde::Serializable, FromReflect, Reflect, TypeData, TypeInfo, TypePath, Typed, }; -use alloc::{boxed::Box, string::String}; +use alloc::{boxed::Box, string::String, vec::Vec}; use bevy_platform::{ collections::{HashMap, HashSet}, sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -203,7 +204,7 @@ impl TypeRegistry { where T: GetTypeRegistration, { - if self.register_internal(TypeId::of::(), T::get_type_registration) { + if self.register_internal(TypeId::of::(), T::get_type_registration, false) { T::register_type_dependencies(self); } } @@ -258,7 +259,7 @@ impl TypeRegistry { /// Returns `true` if the registration was added and `false` if it already exists. pub fn add_registration(&mut self, registration: TypeRegistration) -> bool { let type_id = registration.type_id(); - self.register_internal(type_id, || registration) + self.register_internal(type_id, || registration, false) } /// Registers the type described by `registration`. @@ -271,14 +272,7 @@ impl TypeRegistry { /// This method will _not_ register type dependencies. /// Use [`register`](Self::register) to register a type with its dependencies. pub fn overwrite_registration(&mut self, registration: TypeRegistration) { - Self::update_registration_indices( - ®istration, - &mut self.short_path_to_id, - &mut self.type_path_to_id, - &mut self.ambiguous_names, - ); - self.registrations - .insert(registration.type_id(), registration); + self.register_internal(registration.type_id(), || registration, true); } /// Internal method to register a type with a given [`TypeId`] and [`TypeRegistration`]. @@ -292,18 +286,29 @@ impl TypeRegistry { &mut self, type_id: TypeId, get_registration: impl FnOnce() -> TypeRegistration, + overwrite: bool, ) -> bool { - if self.registrations.contains_key(&type_id) { + if !overwrite && self.registrations.contains_key(&type_id) { return false; } - let registration = get_registration(); + + let mut registration = get_registration(); + Self::update_registration_indices( ®istration, &mut self.short_path_to_id, &mut self.type_path_to_id, &mut self.ambiguous_names, ); + + let callbacks = registration.flush_on_register_callbacks(); + self.registrations.insert(type_id, registration); + + for callback in callbacks { + callback(self); + } + true } @@ -342,14 +347,7 @@ impl TypeRegistry { /// type_registry.register_type_data::, ReflectDeserialize>(); /// ``` pub fn register_type_data>(&mut self) { - let data = self.get_mut(TypeId::of::()).unwrap_or_else(|| { - panic!( - "attempted to call `TypeRegistry::register_type_data` for type `{T}` with data `{D}` without registering `{T}` first", - T = T::type_path(), - D = core::any::type_name::(), - ) - }); - data.insert(D::create_type_data(())); + self.register_type_data_with::(()); } /// Registers the type data `D` with parameter `P` for type `T`. @@ -365,15 +363,35 @@ impl TypeRegistry { &mut self, params: P, ) { - let data = self.get_mut(TypeId::of::()).unwrap_or_else(|| { + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( - "attempted to call `TypeRegistry::register_type_data_with` for type `{T}` with data `{D}` and params `{P}` without registering `{T}` first", + "attempted to register type data `{D}` with params `{P}` for type `{T}` without registering `{T}` first", T = T::type_path(), D = ::core::any::type_name::(), P = ::core::any::type_name::

(), ) }); - data.insert(D::create_type_data(params)); + + let data = D::create_type_data(params); + + let on_register = data.on_register(); + + registration.insert_internal( + TypeId::of::(), + Box::new(data), + >::on_insert(), + // Set to `None` since we already handle the `on_register` callback in this method + None, + true, + ); + + if let Some(on_register) = on_register { + on_register(self); + } + + if let Some(on_register) = >::on_register() { + on_register(self); + } } /// Registers a fallible conversion from type T to U with the reflection @@ -398,13 +416,21 @@ impl TypeRegistry { U: Reflect + TypePath, F: Fn(T) -> Result + Clone + Send + Sync + 'static, { - let data = self.get_mut(TypeId::of::()).unwrap_or_else(|| { + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( "attempted to call `TypeRegistry::register_type_conversion` for type `{U}` without registering `{U}` first", U = U::type_path(), ) }); - data.get_or_insert_data_with(ReflectConvert::default) + + // We know `ReflectConvert` doesn't have any type data callbacks, + // so we're safe to register it directly like this + registration + .data + .entry(TypeId::of::()) + .or_insert_with(|| Box::new(ReflectConvert::default())) + .downcast_mut::() + .expect("ReflectConvert is always registered") .register_type_conversion(function); } @@ -425,13 +451,21 @@ impl TypeRegistry { T: Reflect + TypePath, U: Reflect + TypePath + From, { - let data = self.get_mut(TypeId::of::()).unwrap_or_else(|| { + let registration = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( "attempted to call `TypeRegistry::register_type_conversion` for type `{U}` without registering `{U}` first", U = U::type_path(), ) }); - data.get_or_insert_data_with(ReflectConvert::default) + + // We know `ReflectConvert` doesn't have any type data callbacks, + // so we're safe to register it directly like this + registration + .data + .entry(TypeId::of::()) + .or_insert_with(|| Box::new(ReflectConvert::default())) + .downcast_mut::() + .expect("ReflectConvert is always registered") .register_type_conversion::(|input| Ok(input.into())); } @@ -453,10 +487,41 @@ impl TypeRegistry { /// the given [`TypeId`]. /// /// If the specified type has not been registered, returns `None`. + /// + /// If you need to insert new type data into this [`TypeRegistration`], + /// use [`Self::registration_scope`] instead. pub fn get_mut(&mut self, type_id: TypeId) -> Option<&mut TypeRegistration> { self.registrations.get_mut(&type_id) } + /// Provides scoped access to the [`TypeRegistration`] of the type with + /// the given [`TypeId`] as a [`TypeRegistrationMut`]. + /// + /// This allows for [`TypeData`] to be inserted into the [`TypeRegistration`], + /// and for any registration callbacks associated with that type data to take effect afterwards. + /// + /// If you do not need to insert any new type data, [`Self::get_mut`] is preferred. + /// + /// Returns true if the registration was found, false otherwise. + pub fn registration_scope( + &mut self, + type_id: TypeId, + mut f: impl FnMut(TypeRegistrationMut<'_>), + ) -> bool { + let Some(registration) = self.registrations.get_mut(&type_id) else { + return false; + }; + + f(TypeRegistrationMut::new(registration)); + + let callbacks = registration.flush_on_register_callbacks(); + for callback in callbacks { + callback(self); + } + + true + } + /// Returns a reference to the [`TypeRegistration`] of the type with the /// given [type path]. /// @@ -603,6 +668,206 @@ impl TypeRegistryArc { } } +macro_rules! type_registration_methods { + ($self:ident => $registration:expr) => { + /// Returns the [`TypeId`] of the type. + #[inline] + pub fn type_id(&$self) -> TypeId { + $registration.type_info.type_id() + } + + /// Returns a reference to the registration's [`TypeInfo`] + pub fn type_info(&$self) -> &'static TypeInfo { + $registration.type_info + } + + /// Returns a reference to the value of type `T` in this registration's + /// [type data]. + /// + /// Returns `None` if no such value exists. + /// + /// For a dynamic version of this method, see [`data_by_id`]. + /// + /// [type data]: TypeData + /// [`data_by_id`]: Self::data_by_id + pub fn data(&$self) -> Option<&T> { + $registration.data + .get(&TypeId::of::()) + .and_then(|value| value.downcast_ref()) + } + + /// Returns a reference to the value with the given [`TypeId`] in this registration's + /// [type data]. + /// + /// Returns `None` if no such value exists. + /// + /// For a static version of this method, see [`data`]. + /// + /// [type data]: TypeData + /// [`data`]: Self::data + pub fn data_by_id(&$self, type_id: TypeId) -> Option<&dyn TypeData> { + $registration.data.get(&type_id).map(Deref::deref) + } + + /// Returns a mutable reference to the value of type `T` in this registration's + /// [type data]. + /// + /// Returns `None` if no such value exists. + /// + /// For a dynamic version of this method, see [`data_mut_by_id`]. + /// + /// [type data]: TypeData + /// [`data_mut_by_id`]: Self::data_mut_by_id + pub fn data_mut(&mut $self) -> Option<&mut T> { + $registration.data + .get_mut(&TypeId::of::()) + .and_then(|value| value.downcast_mut()) + } + + /// Returns a mutable reference to the value with the given [`TypeId`] in this registration's + /// [type data]. + /// + /// Returns `None` if no such value exists. + /// + /// For a static version of this method, see [`data_mut`]. + /// + /// [type data]: TypeData + /// [`data_mut`]: Self::data_mut + pub fn data_mut_by_id(&mut $self, type_id: TypeId) -> Option<&mut dyn TypeData> { + $registration.data.get_mut(&type_id).map(DerefMut::deref_mut) + } + + /// Returns true if this registration contains the given [type data]. + /// + /// For a dynamic version of this method, see [`contains_by_id`]. + /// + /// [type data]: TypeData + /// [`contains_by_id`]: Self::contains_by_id + pub fn contains(&$self) -> bool { + $registration.data.contains_key(&TypeId::of::()) + } + + /// Returns true if this registration contains the given [type data] with [`TypeId`]. + /// + /// For a static version of this method, see [`contains`]. + /// + /// [type data]: TypeData + /// [`contains`]: Self::contains + pub fn contains_by_id(&$self, type_id: TypeId) -> bool { + $registration.data.contains_key(&type_id) + } + + /// The total count of [type data] in this registration. + /// + /// [type data]: TypeData + pub fn len(&$self) -> usize { + $registration.data.len() + } + + /// Returns true if this registration has no [type data]. + /// + /// [type data]: TypeData + pub fn is_empty(&$self) -> bool { + $registration.data.is_empty() + } + + /// Returns an iterator over all [type data] in this registration. + /// + /// The iterator yields a tuple of the [`TypeId`] and its corresponding type data. + /// + /// [type data]: TypeData + pub fn iter(&$self) -> impl ExactSizeIterator { + $registration.data.iter().map(|(id, data)| (*id, data.deref())) + } + + /// Returns a mutable iterator over all [type data] in this registration. + /// + /// The iterator yields a tuple of the [`TypeId`] and its corresponding type data. + /// + /// [type data]: TypeData + pub fn iter_mut(&mut $self) -> impl ExactSizeIterator { + $registration.data + .iter_mut() + .map(|(id, data)| (*id, data.deref_mut())) + } + }; +} + +macro_rules! type_data_insertion_methods { + ($self:ident => $registration:expr; [$($mut:tt)?] $self_ty:ty => $return_ty:ty) => { + /// Inserts an instance of `T` into this registration's [type data] map. + /// If another instance of `T` was previously inserted, it is replaced. + /// + /// If this is the first time `T` is being inserted, + /// this method will also trigger the [`TypeData::on_insert`] callbacks for `T`, + /// as well as queue up its [`TypeData::on_register`] callback. + /// + /// See also [`Self::register_type_data`] for type data that can be instantiated + /// automatically using [`CreateTypeData`]. + /// + /// [type data]: TypeData + pub fn insert_data($($mut)? $self: $self_ty, data: T) -> $return_ty { + $registration.insert_internal(TypeId::of::(), Box::new(data), None, None, true); + $self + } + + /// Inserts the [`TypeData`] instance of `T` created for `V`. + /// If another instance of `T` was previously inserted, it is replaced. + /// + /// If this is the first time `T` is being inserted, + /// this method will also trigger the [`TypeData::on_insert`] and [`CreateTypeData::on_insert`] callbacks for `T`, + /// as well as queue up its [`TypeData::on_register`] and [`CreateTypeData::on_register`] callbacks. + /// + /// To register [`TypeData`] that requires input (i.e. it doesn't take `()` as its input), + /// see [`Self::register_type_data_with`]. + #[inline] + pub fn register_type_data, V>($self: $self_ty) -> $return_ty { + $self.register_type_data_with::(()) + } + + /// Inserts the [`TypeData`] instance of `T` created for `V` with some input `I`. + /// If another instance of `T` was previously inserted, it is replaced. + /// + /// If this is the first time `T` is being inserted, + /// this method will also trigger the [`TypeData::on_insert`] and [`CreateTypeData::on_insert`] callbacks for `T`, + /// as well as queue up its [`TypeData::on_register`] and [`CreateTypeData::on_register`] callbacks. + /// + /// To register [`TypeData`] that doesn't require input (i.e. it expects `()`), + /// see [`Self::register_type_data`]. + #[inline] + pub fn register_type_data_with, V, I>( + $($mut)? $self: $self_ty, + input: I, + ) -> $return_ty { + let data = T::create_type_data(input); + let on_insert = >::on_insert(); + let on_register = >::on_register(); + + let has_deps = on_insert.is_some() || data.on_insert().is_some(); + + if !$registration.insert_internal( + TypeId::of::(), + Box::new(data), + on_insert, + on_register, + true, + ) { + return $self; + } + + if !has_deps { + #[expect( + deprecated, + reason = "Will continue to call function until fully removed" + )] + >::insert_dependencies(&mut $registration); + } + + $self + } + }; +} + /// Runtime storage for type metadata, registered into the [`TypeRegistry`]. /// /// An instance of `TypeRegistration` can be created using the [`TypeRegistration::of`] method, @@ -623,7 +888,7 @@ impl TypeRegistryArc { /// assert_eq!("core::option::Option", registration.type_info().type_path()); /// assert_eq!("Option", registration.type_info().type_path_table().short_path()); /// -/// registration.insert::(CreateTypeData::>::create_type_data(())); +/// registration.insert_data::(CreateTypeData::>::create_type_data(())); /// assert!(registration.data::().is_some()) /// ``` /// @@ -631,6 +896,17 @@ impl TypeRegistryArc { pub struct TypeRegistration { data: TypeIdMap>, type_info: &'static TypeInfo, + /// The `on_register` callbacks waiting to be applied. + /// + /// These are generally the ones from [`CreateTypeData::on_register`] + /// since the [`TypeData::on_register`] callbacks can be accessed dynamically from `data`. + #[expect( + clippy::box_collection, + reason = "We intentionally box the `Vec` to save 16 bytes per registration. \ + And since these don't need to be accessed as often or in performance-sensitive code, \ + we can spare the extra layer of indirection" + )] + pending_callbacks: Option>>, } impl Debug for TypeRegistration { @@ -647,20 +923,10 @@ impl TypeRegistration { Self { data: Default::default(), type_info: T::type_info(), + pending_callbacks: None, } } - /// Returns the [`TypeId`] of the type. - #[inline] - pub fn type_id(&self) -> TypeId { - self.type_info.type_id() - } - - /// Returns a reference to the registration's [`TypeInfo`] - pub fn type_info(&self) -> &'static TypeInfo { - self.type_info - } - /// Inserts an instance of `T` into this registration's [type data]. /// /// If another instance of `T` was previously inserted, it is replaced. @@ -670,6 +936,10 @@ impl TypeRegistration { /// That will need to be done manually using [`CreateTypeData::insert_dependencies`]. /// /// [type data]: TypeData + #[deprecated( + since = "0.21.0", + note = "This method will be removed in a future release. Use `TypeRegistry::insert_data` or `TypeRegistry::register_type_data` instead." + )] pub fn insert(&mut self, data: T) { self.data.insert(TypeId::of::(), Box::new(data)); } @@ -678,159 +948,95 @@ impl TypeRegistration { /// exist, it will insert a new instance using `get_data` and then return it. /// /// [type data]: TypeData + #[deprecated( + since = "0.21.0", + note = "This method will be removed in a future release. Use `TypeRegistry::contains` in conjunction with either `TypeRegistry::insert_data` or `TypeRegistry::register_type_data` instead." + )] pub fn get_or_insert_data_with(&mut self, get_data: impl FnOnce() -> T) -> &mut T { - let boxed_data = self - .data - .entry(TypeId::of::()) - .or_insert_with(|| Box::new(get_data())); - boxed_data.downcast_mut::().unwrap() - } - - /// Inserts the [`TypeData`] instance of `T` created for `V`, and inserts any - /// [`TypeData`] dependencies for that combination of `T` and `V`. - /// - /// To register [`TypeData`] that requires input (i.e. it doesn't take `()` as its input), - /// see [`TypeRegistration::register_type_data_with`]. - #[inline] - pub fn register_type_data, V>(&mut self) { - self.insert(T::create_type_data(())); - T::insert_dependencies(self); - } - - /// Inserts the [`TypeData`] instance of `T` created for `V` with some input `I`, - /// and inserts any [`TypeData`] dependencies for that combination of `T`, `V`, and `I`. - /// - /// To register [`TypeData`] that doesn't require input (i.e. it expects `()`), - /// see [`TypeRegistration::register_type_data`]. - #[inline] - pub fn register_type_data_with, V, I>(&mut self, input: I) { - self.insert(T::create_type_data(input)); - T::insert_dependencies(self); + self.insert_internal(TypeId::of::(), Box::new(get_data()), None, None, false); + self.data_mut::().expect("data was just inserted") } - /// Returns a reference to the value of type `T` in this registration's - /// [type data]. - /// - /// Returns `None` if no such value exists. + /// Internal method for inserting type data. /// - /// For a dynamic version of this method, see [`data_by_id`]. + /// Avoids generics in an effort to reduce binary size costs due to monomorphization. /// - /// [type data]: TypeData - /// [`data_by_id`]: Self::data_by_id - pub fn data(&self) -> Option<&T> { - self.data - .get(&TypeId::of::()) - .and_then(|value| value.downcast_ref()) - } - - /// Returns a reference to the value with the given [`TypeId`] in this registration's - /// [type data]. - /// - /// Returns `None` if no such value exists. - /// - /// For a static version of this method, see [`data`]. - /// - /// [type data]: TypeData - /// [`data`]: Self::data - pub fn data_by_id(&self, type_id: TypeId) -> Option<&dyn TypeData> { - self.data.get(&type_id).map(Deref::deref) - } - - /// Returns a mutable reference to the value of type `T` in this registration's - /// [type data]. - /// - /// Returns `None` if no such value exists. - /// - /// For a dynamic version of this method, see [`data_mut_by_id`]. - /// - /// [type data]: TypeData - /// [`data_mut_by_id`]: Self::data_mut_by_id - pub fn data_mut(&mut self) -> Option<&mut T> { - self.data - .get_mut(&TypeId::of::()) - .and_then(|value| value.downcast_mut()) - } - - /// Returns a mutable reference to the value with the given [`TypeId`] in this registration's - /// [type data]. - /// - /// Returns `None` if no such value exists. - /// - /// For a static version of this method, see [`data_mut`]. - /// - /// [type data]: TypeData - /// [`data_mut`]: Self::data_mut - pub fn data_mut_by_id(&mut self, type_id: TypeId) -> Option<&mut dyn TypeData> { - self.data.get_mut(&type_id).map(DerefMut::deref_mut) - } - - /// Returns true if this registration contains the given [type data]. + /// # Arguments /// - /// For a dynamic version of this method, see [`contains_by_id`]. - /// - /// [type data]: TypeData - /// [`contains_by_id`]: Self::contains_by_id - pub fn contains(&self) -> bool { - self.data.contains_key(&TypeId::of::()) - } - - /// Returns true if this registration contains the given [type data] with [`TypeId`]. - /// - /// For a static version of this method, see [`contains`]. - /// - /// [type data]: TypeData - /// [`contains`]: Self::contains - pub fn contains_by_id(&self, type_id: TypeId) -> bool { - self.data.contains_key(&type_id) - } - - /// The total count of [type data] in this registration. - /// - /// [type data]: TypeData - pub fn len(&self) -> usize { - self.data.len() + /// * `type_id`: The `TypeId` of the type data + /// * `data`: The type data itself + /// * `on_insert`: An optional `CreateTypeData::on_insert` callback + /// * `on_register`: An optional `CreateTypeData::on_register` callback + /// * `overwrite`: Indicates whether overwrites are allowed (but does not re-trigger callbacks) + fn insert_internal( + &mut self, + type_id: TypeId, + data: Box, + on_insert: Option, + on_register: Option, + overwrite: bool, + ) -> bool { + let entry = self.data.entry(type_id); + match entry { + indexmap::map::Entry::Vacant(entry) => { + if let Some(on_insert) = entry.insert(data).on_insert() { + on_insert(TypeRegistrationMut::new(self)); + } + if let Some(on_insert) = on_insert { + on_insert(TypeRegistrationMut::new(self)); + } + if let Some(on_register) = on_register { + self.pending_callbacks + .get_or_insert_default() + .push(on_register); + } + true + } + indexmap::map::Entry::Occupied(mut entry) => { + if overwrite { + entry.insert(data); + } + // We don't handle callbacks here to avoid cycles and since in most cases + // doing so would simply be unnecessarily duplicated effort + false + } + } } - /// Returns true if this registration has no [type data]. + /// Collects all `on_register` callbacks from registered [`TypeData`]. /// - /// [type data]: TypeData - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } + /// This includes all pending callbacks and callbacks from inserted type data. + /// It should therefore only be used when the this [`TypeRegistration`] is first registered into a [`TypeRegistry`]. + fn flush_on_register_callbacks(&mut self) -> Vec { + let pending_callbacks = self.pending_callbacks.take(); - /// Returns an iterator over all [type data] in this registration. - /// - /// The iterator yields a tuple of the [`TypeId`] and its corresponding type data. - /// - /// [type data]: TypeData - pub fn iter(&self) -> impl ExactSizeIterator { - self.data.iter().map(|(id, data)| (*id, data.deref())) + self.iter() + .filter_map(|(_, data)| data.on_register()) + .chain(pending_callbacks.as_deref().into_iter().flatten().copied()) + .collect() } - /// Returns a mutable iterator over all [type data] in this registration. - /// - /// The iterator yields a tuple of the [`TypeId`] and its corresponding type data. - /// - /// [type data]: TypeData - pub fn iter_mut(&mut self) -> impl ExactSizeIterator { - self.data - .iter_mut() - .map(|(id, data)| (*id, data.deref_mut())) - } + type_data_insertion_methods!(self => self; [mut] Self => Self); + type_registration_methods!(self => self); } -impl Clone for TypeRegistration { - fn clone(&self) -> Self { - let mut data = TypeIdMap::default(); - for (id, type_data) in &self.data { - data.insert(*id, (*type_data).clone_type_data()); - } +/// A wrapper around a mutable reference to a [`TypeRegistration`] that allows for type data +/// to be inserted mutably. +/// +/// See [`TypeRegistry::registration_scope`] for how to access this. +pub struct TypeRegistrationMut<'a> { + registration: &'a mut TypeRegistration, +} - TypeRegistration { - data, - type_info: self.type_info, - } +impl<'a> TypeRegistrationMut<'a> { + // Private to prevent users from side-stepping the insertion-restrictions, + // and thus potentially forgetting to trigger all the relevant type data callbacks. + fn new(registration: &'a mut TypeRegistration) -> Self { + Self { registration } } + + type_data_insertion_methods!(self => self.registration; [] &mut Self => &mut Self); + type_registration_methods!(self => self.registration); } /// A struct used to serialize reflected instances of a type. @@ -842,6 +1048,8 @@ pub struct ReflectSerialize { get_serializable: fn(value: &dyn Reflect) -> Serializable, } +impl TypeData for ReflectSerialize {} + impl CreateTypeData for ReflectSerialize { fn create_type_data(_input: ()) -> Self { ReflectSerialize { @@ -905,6 +1113,8 @@ impl ReflectDeserialize { } } +impl TypeData for ReflectDeserialize {} + impl Deserialize<'a> + Reflect> CreateTypeData for ReflectDeserialize { fn create_type_data(_input: ()) -> Self { ReflectDeserialize { @@ -1005,6 +1215,8 @@ impl ReflectFromPtr { } } +impl TypeData for ReflectFromPtr {} + #[expect( unsafe_code, reason = "We must interact with pointers here, which are inherently unsafe." @@ -1087,9 +1299,9 @@ mod test { #[derive(Clone)] struct DataA(i32); + impl TypeData for DataA {} - let mut registration = TypeRegistration::of::(); - registration.insert(DataA(123)); + let registration = TypeRegistration::of::().insert_data(DataA(123)); let mut iter = registration.iter(); @@ -1107,9 +1319,9 @@ mod test { #[derive(Clone)] struct DataA(i32); + impl TypeData for DataA {} - let mut registration = TypeRegistration::of::(); - registration.insert(DataA(123)); + let mut registration = TypeRegistration::of::().insert_data(DataA(123)); { let mut iter = registration.iter_mut(); From 2ff8b69666fe72ad21c5b24a5e3f67d212d40d9b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 2 Jun 2026 00:37:21 -0700 Subject: [PATCH 2/7] Added `TypeData` derive macro and updated other crates --- crates/bevy_asset/src/reflect.rs | 6 +-- crates/bevy_ecs/src/entity/clone_entities.rs | 9 ++-- crates/bevy_ecs/src/reflect/bundle.rs | 5 ++- crates/bevy_ecs/src/reflect/component.rs | 6 ++- crates/bevy_ecs/src/reflect/event.rs | 6 ++- crates/bevy_ecs/src/reflect/from_world.rs | 4 +- crates/bevy_ecs/src/reflect/map_entities.rs | 4 +- crates/bevy_ecs/src/reflect/message.rs | 6 ++- crates/bevy_ecs/src/reflect/resource.rs | 10 +++-- crates/bevy_reflect/derive/src/lib.rs | 15 +++++++ crates/bevy_reflect/src/convert.rs | 4 +- crates/bevy_reflect/src/from_reflect.rs | 4 +- crates/bevy_reflect/src/lib.rs | 7 ++- .../src/serde/de/deserialize_with_registry.rs | 4 +- .../src/serde/ser/serialize_with_registry.rs | 4 +- crates/bevy_reflect/src/serde/type_data.rs | 4 +- crates/bevy_reflect/src/std_traits.rs | 44 +++++-------------- crates/bevy_reflect/src/type_data.rs | 10 ++--- crates/bevy_reflect/src/type_registry.rs | 22 +++------- crates/bevy_remote/src/schemas/json_schema.rs | 34 ++++++-------- crates/bevy_settings/src/lib.rs | 12 ++--- crates/bevy_state/src/reflect.rs | 6 +-- 22 files changed, 102 insertions(+), 124 deletions(-) diff --git a/crates/bevy_asset/src/reflect.rs b/crates/bevy_asset/src/reflect.rs index 792aaddb6a2da..1d6d6a1ac0522 100644 --- a/crates/bevy_asset/src/reflect.rs +++ b/crates/bevy_asset/src/reflect.rs @@ -8,7 +8,7 @@ use uuid::Uuid; use bevy_ecs::world::{unsafe_world_cell::UnsafeWorldCell, World}; use bevy_reflect::{ serde::{ReflectDeserializerProcessor, ReflectSerializerProcessor}, - CreateTypeData, FromReflect, PartialReflect, Reflect, TypeRegistry, + CreateTypeData, FromReflect, PartialReflect, Reflect, TypeData, TypeRegistry, }; use crate::{ @@ -23,7 +23,7 @@ use crate::{ /// until runtime. /// /// [`ReflectAsset`] can be obtained via [`TypeRegistration::data`](bevy_reflect::TypeRegistration::data) if the asset was registered using [`register_asset_reflect`](crate::AssetApp::register_asset_reflect). -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectAsset { handle_type_id: TypeId, assets_resource_type_id: TypeId, @@ -228,7 +228,7 @@ impl CreateTypeData for ReflectAsset { /// println!("{value:?}"); /// } /// ``` -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectHandle { asset_type_id: TypeId, downcast_handle_untyped: fn(&dyn Any) -> Option, diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index aa3bdd52237a4..6d935c8d3e793 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1476,7 +1476,7 @@ mod tests { use super::*; use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}; use alloc::vec; - use bevy_reflect::{std_traits::ReflectDefault, CreateTypeData, Reflect, ReflectFromPtr}; + use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectFromPtr}; #[test] fn clone_entity_using_reflect() { @@ -1614,10 +1614,9 @@ mod tests { { let mut registry = registry.write(); registry.register::(); - registry - .get_mut(TypeId::of::()) - .unwrap() - .insert(>::create_type_data(())); + registry.registration_scope(TypeId::of::(), |mut registration| { + registration.register_type_data::(); + }); } let e = world.spawn(A).id(); diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index 244da020a0cde..8d079fb1ebe59 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -16,7 +16,8 @@ use crate::{ world::{EntityMut, EntityWorldMut}, }; use bevy_reflect::{ - CreateTypeData, FromReflect, PartialReflect, Reflect, ReflectRef, TypePath, TypeRegistry, + CreateTypeData, FromReflect, PartialReflect, Reflect, ReflectRef, TypeData, TypePath, + TypeRegistry, }; use super::{from_reflect_with_fallback, ReflectComponent}; @@ -25,7 +26,7 @@ use super::{from_reflect_with_fallback, ReflectComponent}; /// /// A [`ReflectBundle`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectBundle(ReflectBundleFns); /// The raw function pointers needed to make up a [`ReflectBundle`]. diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index d237a8f61f91f..264200ece5da4 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -70,14 +70,16 @@ use crate::{ }, }; use alloc::boxed::Box; -use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{ + CreateTypeData, FromReflect, PartialReflect, Reflect, TypeData, TypePath, TypeRegistry, +}; use bevy_utils::prelude::DebugName; /// A struct used to operate on reflected [`Component`] trait of a type. /// /// A [`ReflectComponent`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectComponent(ReflectComponentFns); /// The raw function pointers needed to make up a [`ReflectComponent`]. diff --git a/crates/bevy_ecs/src/reflect/event.rs b/crates/bevy_ecs/src/reflect/event.rs index b6c9d3f93915d..d5685f72482a2 100644 --- a/crates/bevy_ecs/src/reflect/event.rs +++ b/crates/bevy_ecs/src/reflect/event.rs @@ -13,13 +13,15 @@ use crate::{ reflect::from_reflect_with_fallback, world::{DeferredWorld, World}, }; -use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{ + CreateTypeData, FromReflect, PartialReflect, Reflect, TypeData, TypePath, TypeRegistry, +}; /// A struct used to operate on reflected [`Event`] trait of a type. /// /// A [`ReflectEvent`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectEvent(ReflectEventFns); /// The raw function pointers needed to make up a [`ReflectEvent`]. diff --git a/crates/bevy_ecs/src/reflect/from_world.rs b/crates/bevy_ecs/src/reflect/from_world.rs index d133c5eba15aa..c0d194afb685d 100644 --- a/crates/bevy_ecs/src/reflect/from_world.rs +++ b/crates/bevy_ecs/src/reflect/from_world.rs @@ -7,7 +7,7 @@ //! Same as [`component`](`super::component`), but for [`FromWorld`]. use alloc::boxed::Box; -use bevy_reflect::{CreateTypeData, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect, TypeData}; use crate::world::{FromWorld, World}; @@ -15,7 +15,7 @@ use crate::world::{FromWorld, World}; /// /// A [`ReflectFromWorld`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectFromWorld(ReflectFromWorldFns); /// The raw function pointers needed to make up a [`ReflectFromWorld`]. diff --git a/crates/bevy_ecs/src/reflect/map_entities.rs b/crates/bevy_ecs/src/reflect/map_entities.rs index 04b597ab9c953..a6b0d794d3c02 100644 --- a/crates/bevy_ecs/src/reflect/map_entities.rs +++ b/crates/bevy_ecs/src/reflect/map_entities.rs @@ -1,5 +1,5 @@ use crate::entity::{EntityMapper, MapEntities}; -use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect}; +use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, TypeData}; /// For a specific type of value, this maps any fields with values of type [`Entity`] to a new world. /// @@ -10,7 +10,7 @@ use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect}; /// /// [`Entity`]: crate::entity::Entity /// [`EntityMapper`]: crate::entity::EntityMapper -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectMapEntities { map_entities: fn(&mut dyn PartialReflect, &mut dyn EntityMapper), } diff --git a/crates/bevy_ecs/src/reflect/message.rs b/crates/bevy_ecs/src/reflect/message.rs index fce5ab5245e11..7a25bbf1b93b7 100644 --- a/crates/bevy_ecs/src/reflect/message.rs +++ b/crates/bevy_ecs/src/reflect/message.rs @@ -6,13 +6,15 @@ //! Same as [`component`](`super::component`), but for messages. use crate::{message::Message, reflect::from_reflect_with_fallback, world::World}; -use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{ + CreateTypeData, FromReflect, PartialReflect, Reflect, TypeData, TypePath, TypeRegistry, +}; /// A struct used to operate on reflected [`Message`] trait of a type. /// /// A [`ReflectMessage`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectMessage(ReflectMessageFns); /// The raw function pointers needed to make up a [`ReflectMessage`]. diff --git a/crates/bevy_ecs/src/reflect/resource.rs b/crates/bevy_ecs/src/reflect/resource.rs index 74dc3a0e0306e..514c697306953 100644 --- a/crates/bevy_ecs/src/reflect/resource.rs +++ b/crates/bevy_ecs/src/reflect/resource.rs @@ -5,7 +5,7 @@ //! See the module doc for [`reflect::component`](`crate::reflect::component`). use crate::{reflect::ReflectComponent, resource::Resource}; -use bevy_reflect::{CreateTypeData, FromReflect, TypePath, TypeRegistration}; +use bevy_reflect::{CreateTypeData, FromReflect, OnInsertTypeData, TypeData, TypePath}; /// A struct that marks a reflected [`Resource`] of a type. /// @@ -27,7 +27,7 @@ use bevy_reflect::{CreateTypeData, FromReflect, TypePath, TypeRegistration}; /// /// A [`ReflectResource`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectResource; impl CreateTypeData for ReflectResource { @@ -35,7 +35,9 @@ impl CreateTypeData for ReflectResource ReflectResource } - fn insert_dependencies(type_registration: &mut TypeRegistration) { - type_registration.register_type_data::(); + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::(); + }) } } diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index b2e07a3bc9bc2..0884b03c8ba41 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -512,6 +512,21 @@ pub fn derive_type_path(input: TokenStream) -> TokenStream { }) } +/// Derives the `TypePath` trait. +#[proc_macro_derive(TypeData)] +pub fn derive_type_data(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let ident = &ast.ident; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + let bevy_reflect_path = meta::get_bevy_reflect_path(); + + TokenStream::from(quote! { + const _: () = { + impl #impl_generics #bevy_reflect_path::TypeData for #ident #ty_generics #where_clause {} + }; + }) +} + /// A macro that automatically generates type data for traits, which their implementors can then register. /// /// The output of this macro is a struct that takes reflected instances of the implementor's type diff --git a/crates/bevy_reflect/src/convert.rs b/crates/bevy_reflect/src/convert.rs index 1066fd5ee7aa7..03b4ae6386ada 100644 --- a/crates/bevy_reflect/src/convert.rs +++ b/crates/bevy_reflect/src/convert.rs @@ -32,7 +32,7 @@ use crate::{Reflect, TypeData, TypePath}; /// .downcast::() /// .unwrap(); /// ``` -#[derive(Default)] +#[derive(Default, TypeData)] pub struct ReflectConvert { /// A mapping from the type to be converted *from* to its associated /// [`Converter`]. @@ -101,8 +101,6 @@ impl ReflectConvert { } } -impl TypeData for ReflectConvert {} - impl Clone for ReflectConvert { fn clone(&self) -> Self { ReflectConvert { diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index d39933f5e86c1..68d8040ab9c29 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -102,7 +102,7 @@ pub trait FromReflect: Reflect + Sized { /// /// [`DynamicStruct`]: crate::structs::DynamicStruct /// [`DynamicEnum`]: crate::enums::DynamicEnum -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectFromReflect { from_reflect: fn(&dyn PartialReflect) -> Option>, } @@ -117,8 +117,6 @@ impl ReflectFromReflect { } } -impl TypeData for ReflectFromReflect {} - impl CreateTypeData for ReflectFromReflect { fn create_type_data(_input: ()) -> Self { Self { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 67fd01e402fa6..b43f8fc0523c3 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -378,7 +378,8 @@ //! ``` //! //! The generated type data can be used to convert a valid `dyn Reflect` into a `dyn MyTrait`. -//! See the [type_data] module for details or the [dynamic types example](https://github.com/bevyengine/bevy/blob/latest/examples/reflection/type_data.rs) +//! +//! See the [`type_data`] module for details on type data or the [dynamic types example](https://github.com/bevyengine/bevy/blob/latest/examples/reflection/type_data.rs) //! for additional examples. //! //! # Serialization @@ -4135,11 +4136,9 @@ bevy_reflect::tests::Test { } } - #[derive(Clone)] + #[derive(Clone, TypeData)] struct ReflectB; - impl TypeData for ReflectB {} - let mut registry = TypeRegistry::new(); registry.register::(); diff --git a/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs b/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs index 6ab262a42cf42..fd58a47676745 100644 --- a/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs +++ b/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs @@ -51,7 +51,7 @@ pub trait DeserializeWithRegistry<'de>: Sized { } /// Type data used to deserialize a [`PartialReflect`] type with a custom [`DeserializeWithRegistry`] implementation. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectDeserializeWithRegistry { deserialize: fn( deserializer: &mut dyn erased_serde::Deserializer, @@ -74,8 +74,6 @@ impl ReflectDeserializeWithRegistry { } } -impl TypeData for ReflectDeserializeWithRegistry {} - impl DeserializeWithRegistry<'de>> CreateTypeData for ReflectDeserializeWithRegistry { diff --git a/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs b/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs index 75b37229730a3..a6d1d8e0bda8b 100644 --- a/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs +++ b/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs @@ -49,7 +49,7 @@ pub trait SerializeWithRegistry { } /// Type data used to serialize a [`Reflect`] type with a custom [`SerializeWithRegistry`] implementation. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectSerializeWithRegistry { serialize: for<'a> fn( value: &'a dyn Reflect, @@ -72,8 +72,6 @@ impl ReflectSerializeWithRegistry { } } -impl TypeData for ReflectSerializeWithRegistry {} - impl CreateTypeData for ReflectSerializeWithRegistry { fn create_type_data(_input: ()) -> Self { Self { diff --git a/crates/bevy_reflect/src/serde/type_data.rs b/crates/bevy_reflect/src/serde/type_data.rs index df106ffee6ae7..629dda7fee440 100644 --- a/crates/bevy_reflect/src/serde/type_data.rs +++ b/crates/bevy_reflect/src/serde/type_data.rs @@ -3,7 +3,7 @@ use alloc::boxed::Box; use bevy_platform::collections::{hash_map::Iter, HashMap}; /// Contains data relevant to the automatic reflect powered (de)serialization of a type. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, TypeData)] pub struct SerializationData { skipped_fields: HashMap, } @@ -113,8 +113,6 @@ impl SerializationData { } } -impl TypeData for SerializationData {} - /// Data needed for (de)serialization of a skipped field. #[derive(Debug, Clone)] pub struct SkippedField { diff --git a/crates/bevy_reflect/src/std_traits.rs b/crates/bevy_reflect/src/std_traits.rs index b76b67d4796ff..851238025a8de 100644 --- a/crates/bevy_reflect/src/std_traits.rs +++ b/crates/bevy_reflect/src/std_traits.rs @@ -8,7 +8,7 @@ use crate::{CreateTypeData, PartialReflect, Reflect, TypeData}; /// A struct used to provide the default value of a type. /// /// A [`ReflectDefault`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectDefault { default: fn() -> Box, } @@ -20,8 +20,6 @@ impl ReflectDefault { } } -impl TypeData for ReflectDefault {} - impl CreateTypeData for ReflectDefault { fn create_type_data(_input: ()) -> Self { ReflectDefault { @@ -33,7 +31,7 @@ impl CreateTypeData for ReflectDefault { /// A struct used to perform addition on reflected values. /// /// A [`ReflectAdd`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectAdd { /// Function pointer implementing [`ReflectAdd::add()`]. pub add: fn( @@ -58,8 +56,6 @@ impl ReflectAdd { } } -impl TypeData for ReflectAdd {} - impl> CreateTypeData for ReflectAdd { fn create_type_data(_input: ()) -> Self { ReflectAdd { @@ -85,7 +81,7 @@ impl> CreateTypeData for ReflectAdd { /// A struct used to perform subtraction on reflected values. /// /// A [`ReflectSub`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectSub { /// Function pointer implementing [`ReflectSub::sub()`]. pub sub: fn( @@ -110,8 +106,6 @@ impl ReflectSub { } } -impl TypeData for ReflectSub {} - impl> CreateTypeData for ReflectSub { fn create_type_data(_input: ()) -> Self { ReflectSub { @@ -137,7 +131,7 @@ impl> CreateTypeData for ReflectSub { /// A struct used to perform multiplication on reflected values. /// /// A [`ReflectMul`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectMul { /// Function pointer implementing [`ReflectMul::mul()`]. pub mul: fn( @@ -162,8 +156,6 @@ impl ReflectMul { } } -impl TypeData for ReflectMul {} - impl> CreateTypeData for ReflectMul { fn create_type_data(_input: ()) -> Self { ReflectMul { @@ -189,7 +181,7 @@ impl> CreateTypeData for ReflectMul { /// A struct used to perform division on reflected values. /// /// A [`ReflectDiv`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectDiv { /// Function pointer implementing [`ReflectDiv::div()`]. pub div: fn( @@ -214,8 +206,6 @@ impl ReflectDiv { } } -impl TypeData for ReflectDiv {} - impl> CreateTypeData for ReflectDiv { fn create_type_data(_input: ()) -> Self { ReflectDiv { @@ -241,7 +231,7 @@ impl> CreateTypeData for ReflectDiv { /// A struct used to perform remainder on reflected values. /// /// A [`ReflectRem`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectRem { /// Function pointer implementing [`ReflectRem::rem()`]. pub rem: fn( @@ -267,8 +257,6 @@ impl ReflectRem { } } -impl TypeData for ReflectRem {} - impl> CreateTypeData for ReflectRem { fn create_type_data(_input: ()) -> Self { ReflectRem { @@ -294,7 +282,7 @@ impl> CreateTypeData for ReflectRem { /// A struct used to perform addition assignment on reflected values. /// /// A [`ReflectAddAssign`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectAddAssign { /// Function pointer implementing [`ReflectAddAssign::add_assign()`]. pub add_assign: fn( @@ -319,8 +307,6 @@ impl ReflectAddAssign { } } -impl TypeData for ReflectAddAssign {} - impl CreateTypeData for ReflectAddAssign { fn create_type_data(_input: ()) -> Self { ReflectAddAssign { @@ -342,7 +328,7 @@ impl CreateTypeData for ReflectAddAssign { /// A struct used to perform subtraction assignment on reflected values. /// /// A [`ReflectSubAssign`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectSubAssign { /// Function pointer implementing [`ReflectSubAssign::sub_assign()`]. pub sub_assign: fn( @@ -367,8 +353,6 @@ impl ReflectSubAssign { } } -impl TypeData for ReflectSubAssign {} - impl CreateTypeData for ReflectSubAssign { fn create_type_data(_input: ()) -> Self { ReflectSubAssign { @@ -390,7 +374,7 @@ impl CreateTypeData for ReflectSubAssign { /// A struct used to perform multiplication assignment on reflected values. /// /// A [`ReflectMulAssign`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectMulAssign { /// Function pointer implementing [`ReflectMulAssign::mul_assign()`]. pub mul_assign: fn( @@ -415,8 +399,6 @@ impl ReflectMulAssign { } } -impl TypeData for ReflectMulAssign {} - impl CreateTypeData for ReflectMulAssign { fn create_type_data(_input: ()) -> Self { ReflectMulAssign { @@ -438,7 +420,7 @@ impl CreateTypeData for ReflectMulAssign { /// A struct used to perform division assignment on reflected values. /// /// A [`ReflectDivAssign`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectDivAssign { /// Function pointer implementing [`ReflectDivAssign::div_assign()`]. pub div_assign: fn( @@ -463,8 +445,6 @@ impl ReflectDivAssign { } } -impl TypeData for ReflectDivAssign {} - impl CreateTypeData for ReflectDivAssign { fn create_type_data(_input: ()) -> Self { ReflectDivAssign { @@ -486,7 +466,7 @@ impl CreateTypeData for ReflectDivAssign { /// A struct used to perform remainder assignment on reflected values. /// /// A [`ReflectRemAssign`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectRemAssign { /// Function pointer implementing [`ReflectRemAssign::rem_assign()`]. pub rem_assign: fn( @@ -511,8 +491,6 @@ impl ReflectRemAssign { } } -impl TypeData for ReflectRemAssign {} - impl CreateTypeData for ReflectRemAssign { fn create_type_data(_input: ()) -> Self { ReflectRemAssign { diff --git a/crates/bevy_reflect/src/type_data.rs b/crates/bevy_reflect/src/type_data.rs index 74417f7598249..b57899cc19c4e 100644 --- a/crates/bevy_reflect/src/type_data.rs +++ b/crates/bevy_reflect/src/type_data.rs @@ -310,7 +310,7 @@ pub trait CreateTypeData: TypeData { mod tests { use super::*; use crate as bevy_reflect; - use crate::{Reflect, TypeRegistration}; + use crate::{Reflect, TypeData, TypeRegistration}; use core::any::TypeId; use core::marker::PhantomData; @@ -328,8 +328,8 @@ mod tests { } } + #[derive(TypeData)] struct ReflectB; - impl TypeData for ReflectB {} let registration = TypeRegistration::of::().insert_data(ReflectA); assert!(registration.contains::()); @@ -391,8 +391,8 @@ mod tests { } } + #[derive(TypeData)] struct ReflectB; - impl TypeData for ReflectB {} let mut registry = TypeRegistry::empty(); registry.register::(); @@ -574,8 +574,8 @@ mod tests { #[derive(Reflect)] struct Bar; + #[derive(TypeData)] struct ReflectA; - impl TypeData for ReflectA {} impl CreateTypeData for ReflectA { fn create_type_data(_: ()) -> Self { @@ -631,8 +631,8 @@ mod tests { #[derive(Reflect)] struct Bar; + #[derive(TypeData)] struct ReflectA; - impl TypeData for ReflectA {} impl CreateTypeData for ReflectA { fn create_type_data(_: ()) -> Self { diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 7373687b4f13e..37929807ba42e 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -883,12 +883,12 @@ macro_rules! type_data_insertion_methods { /// /// ``` /// # use bevy_reflect::{TypeRegistration, std_traits::ReflectDefault, CreateTypeData}; -/// let mut registration = TypeRegistration::of::>(); +/// let registration = TypeRegistration::of::>(); /// /// assert_eq!("core::option::Option", registration.type_info().type_path()); /// assert_eq!("Option", registration.type_info().type_path_table().short_path()); /// -/// registration.insert_data::(CreateTypeData::>::create_type_data(())); +/// let registration = registration.insert_data::(CreateTypeData::>::create_type_data(())); /// assert!(registration.data::().is_some()) /// ``` /// @@ -1043,13 +1043,11 @@ impl<'a> TypeRegistrationMut<'a> { /// /// A `ReflectSerialize` for type `T` can be obtained via /// [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectSerialize { get_serializable: fn(value: &dyn Reflect) -> Serializable, } -impl TypeData for ReflectSerialize {} - impl CreateTypeData for ReflectSerialize { fn create_type_data(_input: ()) -> Self { ReflectSerialize { @@ -1088,7 +1086,7 @@ impl ReflectSerialize { /// /// A `ReflectDeserialize` for type `T` can be obtained via /// [`CreateTypeData::create_type_data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectDeserialize { /// Function used by [`ReflectDeserialize::deserialize`] to /// perform deserialization. @@ -1113,8 +1111,6 @@ impl ReflectDeserialize { } } -impl TypeData for ReflectDeserialize {} - impl Deserialize<'a> + Reflect> CreateTypeData for ReflectDeserialize { fn create_type_data(_input: ()) -> Self { ReflectDeserialize { @@ -1153,7 +1149,7 @@ impl Deserialize<'a> + Reflect> CreateTypeData for ReflectDeserial /// /// assert_eq!(value.downcast_ref::().unwrap().0, "Hello world!"); /// ``` -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectFromPtr { type_id: TypeId, from_ptr: unsafe fn(Ptr) -> &dyn Reflect, @@ -1215,8 +1211,6 @@ impl ReflectFromPtr { } } -impl TypeData for ReflectFromPtr {} - #[expect( unsafe_code, reason = "We must interact with pointers here, which are inherently unsafe." @@ -1297,9 +1291,8 @@ mod test { #[derive(Reflect)] struct Foo; - #[derive(Clone)] + #[derive(Clone, TypeData)] struct DataA(i32); - impl TypeData for DataA {} let registration = TypeRegistration::of::().insert_data(DataA(123)); @@ -1317,9 +1310,8 @@ mod test { #[derive(Reflect)] struct Foo; - #[derive(Clone)] + #[derive(Clone, TypeData)] struct DataA(i32); - impl TypeData for DataA {} let mut registration = TypeRegistration::of::().insert_data(DataA(123)); diff --git a/crates/bevy_remote/src/schemas/json_schema.rs b/crates/bevy_remote/src/schemas/json_schema.rs index 0519c220ad045..f18b6d59cb9d7 100644 --- a/crates/bevy_remote/src/schemas/json_schema.rs +++ b/crates/bevy_remote/src/schemas/json_schema.rs @@ -463,7 +463,7 @@ mod tests { use bevy_ecs::{component::Component, reflect::AppTypeRegistry, resource::Resource}; use bevy_reflect::prelude::ReflectDefault; - use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; + use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize, TypeData}; #[test] fn reflect_export_struct() { @@ -482,10 +482,9 @@ mod tests { let type_registry = atr.read(); let foo_registration = type_registry .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); + .expect("SHOULD BE REGISTERED"); let (_, schema) = export_type( - &foo_registration, + foo_registration, &SchemaTypesMetadata::default(), &Components::default(), ); @@ -527,10 +526,9 @@ mod tests { let type_registry = atr.read(); let foo_registration = type_registry .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); + .expect("SHOULD BE REGISTERED"); let (_, schema) = export_type( - &foo_registration, + foo_registration, &SchemaTypesMetadata::default(), &Components::default(), ); @@ -566,10 +564,9 @@ mod tests { let type_registry = atr.read(); let foo_registration = type_registry .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); + .expect("SHOULD BE REGISTERED"); let (_, schema) = export_type( - &foo_registration, + foo_registration, &SchemaTypesMetadata::default(), &Components::default(), ); @@ -598,7 +595,7 @@ mod tests { NoValue, } - #[derive(Clone)] + #[derive(Clone, TypeData)] pub struct ReflectCustomData; impl bevy_reflect::CreateTypeData for ReflectCustomData { @@ -618,9 +615,8 @@ mod tests { let type_registry = atr.read(); let foo_registration = type_registry .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - let (_, schema) = export_type(&foo_registration, &metadata, &Components::default()); + .expect("SHOULD BE REGISTERED"); + let (_, schema) = export_type(foo_registration, &metadata, &Components::default()); assert!( !metadata.has_type_data::(&schema.reflect_types), "Should not be a component" @@ -655,10 +651,9 @@ mod tests { let type_registry = atr.read(); let foo_registration = type_registry .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); + .expect("SHOULD BE REGISTERED"); let (_, schema) = export_type( - &foo_registration, + foo_registration, &SchemaTypesMetadata::default(), &Components::default(), ); @@ -690,10 +685,9 @@ mod tests { let type_registry = atr.read(); let foo_registration = type_registry .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); + .expect("SHOULD BE REGISTERED"); let (_, schema) = export_type( - &foo_registration, + foo_registration, &SchemaTypesMetadata::default(), &Components::default(), ); diff --git a/crates/bevy_settings/src/lib.rs b/crates/bevy_settings/src/lib.rs index 2bd75d75086a7..b4669fd11bbd4 100644 --- a/crates/bevy_settings/src/lib.rs +++ b/crates/bevy_settings/src/lib.rs @@ -30,8 +30,8 @@ use bevy_log::warn; use bevy_reflect::{ prelude::ReflectDefault, serde::{TypedReflectDeserializer, TypedReflectSerializer}, - CreateTypeData, FromReflect, PartialReflect, ReflectMut, TypeInfo, TypePath, TypeRegistration, - TypeRegistry, + CreateTypeData, FromReflect, OnInsertTypeData, PartialReflect, ReflectMut, TypeData, TypeInfo, + TypePath, TypeRegistry, }; #[cfg(not(target_arch = "wasm32"))] @@ -162,7 +162,7 @@ pub trait SettingsGroup: Resource { } /// Reflected data from a [`SettingsGroup`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectSettingsGroup { /// The name of the logical section within the settings file. settings_group_name: &'static str, @@ -181,8 +181,10 @@ impl CreateTypeData for ReflectSet } } - fn insert_dependencies(type_registration: &mut TypeRegistration) { - type_registration.register_type_data::(); + fn on_insert() -> Option { + Some(|mut registration| { + registration.register_type_data::(); + }) } } diff --git a/crates/bevy_state/src/reflect.rs b/crates/bevy_state/src/reflect.rs index e1e4bec643de3..12054c9ec9d37 100644 --- a/crates/bevy_state/src/reflect.rs +++ b/crates/bevy_state/src/reflect.rs @@ -1,13 +1,13 @@ use crate::state::{FreelyMutableState, NextState, State, States}; use bevy_ecs::{reflect::from_reflect_with_fallback, world::World}; -use bevy_reflect::{CreateTypeData, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{CreateTypeData, Reflect, TypeData, TypePath, TypeRegistry}; /// A struct used to operate on the reflected [`States`] trait of a type. /// /// A [`ReflectState`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectState(ReflectStateFns); /// The raw function pointers needed to make up a [`ReflectState`]. @@ -51,7 +51,7 @@ impl CreateTypeData for ReflectState { /// /// A [`ReflectFreelyMutableState`] for type `T` can be obtained via /// [`bevy_reflect::TypeRegistration::data`]. -#[derive(Clone)] +#[derive(Clone, TypeData)] pub struct ReflectFreelyMutableState(ReflectFreelyMutableStateFns); /// The raw function pointers needed to make up a [`ReflectFreelyMutableState`]. From 6199ba8e180f7f74e7d43984ddbfa6ec7d9ea3b5 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 2 Jun 2026 09:16:29 -0700 Subject: [PATCH 3/7] Widen callbacks to accept FnOnce --- crates/bevy_ecs/src/reflect/resource.rs | 6 +- crates/bevy_reflect/src/lib.rs | 4 +- crates/bevy_reflect/src/type_data.rs | 217 ++++++++++++++++++----- crates/bevy_reflect/src/type_registry.rs | 34 ++-- crates/bevy_settings/src/lib.rs | 6 +- 5 files changed, 202 insertions(+), 65 deletions(-) diff --git a/crates/bevy_ecs/src/reflect/resource.rs b/crates/bevy_ecs/src/reflect/resource.rs index 514c697306953..6af4978c994d0 100644 --- a/crates/bevy_ecs/src/reflect/resource.rs +++ b/crates/bevy_ecs/src/reflect/resource.rs @@ -35,9 +35,9 @@ impl CreateTypeData for ReflectResource ReflectResource } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::(); - }) + })) } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index b43f8fc0523c3..ccb35071b0ec0 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -4124,9 +4124,9 @@ bevy_reflect::tests::Test { impl TypeData for ReflectA { fn on_insert(&self) -> Option { - Some(|mut registration| { + Some(OnInsertTypeData::new(|mut registration| { registration.insert_data(ReflectB); - }) + })) } } diff --git a/crates/bevy_reflect/src/type_data.rs b/crates/bevy_reflect/src/type_data.rs index b57899cc19c4e..c63ce7be7a418 100644 --- a/crates/bevy_reflect/src/type_data.rs +++ b/crates/bevy_reflect/src/type_data.rs @@ -138,13 +138,87 @@ //! [dyn-compatible]: https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility use crate::{TypeRegistration, TypeRegistrationMut, TypeRegistry}; +use alloc::boxed::Box; use downcast_rs::{impl_downcast, Downcast}; -/// Type alias representing the callback function for when [`TypeData`] is inserted into a [`TypeRegistration`]. -pub type OnInsertTypeData = fn(registration: TypeRegistrationMut<'_>); +/// Callback object for when [`TypeData`] is inserted into a [`TypeRegistration`]. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::{OnInsertTypeData, TypeData, TypeRegistration}; +/// # +/// #[derive(TypeData)] +/// struct ReflectSomeOtherData; +/// +/// struct ReflectSomeData; +/// +/// impl TypeData for ReflectSomeData { +/// fn on_insert(&self) -> Option { +/// Some(OnInsertTypeData::new(|mut registration| { +/// registration.insert_data(ReflectSomeOtherData); +/// })) +/// } +/// } +/// +/// // ... +/// let registration = TypeRegistration::of::().insert_data(ReflectSomeData); +/// assert!(registration.contains::()); +/// ``` +pub struct OnInsertTypeData(Box) + Send + Sync + 'static>); + +impl OnInsertTypeData { + /// Create a new instance of [`OnInsertTypeData`] with the given callback function. + pub fn new) + Send + Sync + 'static>(f: F) -> Self { + Self(Box::new(f)) + } -/// Type alias representing the callback function for when a [`TypeRegistration`] is registered into a [`TypeRegistry`]. -pub type OnRegisterTypeData = fn(registry: &mut TypeRegistry); + pub(crate) fn call(self, registration: TypeRegistrationMut<'_>) { + self.0(registration); + } +} + +/// Callback object for when a [`TypeRegistration`] is registered into a [`TypeRegistry`]. +/// +/// # Example +/// +/// ``` +/// # use core::any::TypeId; +/// # use bevy_reflect::{OnRegisterTypeData, Reflect, TypeData, TypeRegistration, TypeRegistry}; +/// # +/// #[derive(Reflect)] +/// struct SomeType; +/// +/// struct ReflectSomeData; +/// +/// impl TypeData for ReflectSomeData { +/// fn on_register(&self) -> Option { +/// Some(OnRegisterTypeData::new(|registry| { +/// registry.register::(); +/// })) +/// } +/// } +/// +/// // ... +/// let mut registry = TypeRegistry::empty(); +/// let registration = TypeRegistration::of::().insert_data(ReflectSomeData); +/// +/// assert!(!registry.contains(TypeId::of::())); +/// registry.add_registration(registration); +/// assert!(registry.contains(TypeId::of::())); +/// ``` +pub struct OnRegisterTypeData(Box); + +impl OnRegisterTypeData { + /// Create a new instance of [`OnRegisterTypeData`] with the given callback function. + pub fn new(f: F) -> Self { + Self(Box::new(f)) + } + + pub(crate) fn call(self, registry: &mut TypeRegistry) { + self.0(registry); + } +} /// A trait for representing type metadata. /// @@ -198,12 +272,13 @@ impl_downcast!(TypeData); /// # Example /// /// ``` -/// # use bevy_reflect::{CreateTypeData, Reflect}; +/// # use bevy_reflect::{CreateTypeData, Reflect, TypeData}; +/// /// trait Combine { /// fn combine(a: f32, b: f32) -> f32; /// } /// -/// #[derive(Clone)] +/// #[derive(TypeData)] /// struct ReflectCombine { /// multiplier: f32, /// additional: f32, @@ -292,7 +367,11 @@ pub trait CreateTypeData: TypeData { fn insert_dependencies(type_registration: &mut TypeRegistration) {} /// Optional callback for when this type data is created and inserted into a [`TypeRegistration`]. - fn on_insert() -> Option { + #[expect( + unused_variables, + reason = "default implementation does not need input" + )] + fn on_insert(input: &Input) -> Option { None } @@ -301,7 +380,11 @@ pub trait CreateTypeData: TypeData { /// /// Note that if this type data is inserted into a [`TypeRegistration`] that already belongs to a [`TypeRegistry`], /// then this should trigger immediately. - fn on_register() -> Option { + #[expect( + unused_variables, + reason = "default implementation does not need input" + )] + fn on_register(input: &Input) -> Option { None } } @@ -311,6 +394,7 @@ mod tests { use super::*; use crate as bevy_reflect; use crate::{Reflect, TypeData, TypeRegistration}; + use alloc::string::String; use core::any::TypeId; use core::marker::PhantomData; @@ -322,9 +406,9 @@ mod tests { struct ReflectA; impl TypeData for ReflectA { fn on_insert(&self) -> Option { - Some(|mut registration| { + Some(OnInsertTypeData::new(|mut registration| { registration.insert_data(ReflectB); - }) + })) } } @@ -349,10 +433,10 @@ mod tests { Self(PhantomData) } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::, _>(); - }) + })) } } @@ -379,9 +463,9 @@ mod tests { struct ReflectA; impl TypeData for ReflectA { fn on_insert(&self) -> Option { - Some(|mut registration| { + Some(OnInsertTypeData::new(|mut registration| { registration.insert_data(ReflectB); - }) + })) } } @@ -416,10 +500,10 @@ mod tests { Self(PhantomData) } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::, _>(); - }) + })) } } @@ -449,27 +533,27 @@ mod tests { struct ReflectA; impl TypeData for ReflectA { fn on_insert(&self) -> Option { - Some(|mut registration| { + Some(OnInsertTypeData::new(|mut registration| { registration.insert_data(ReflectB); - }) + })) } } struct ReflectB; impl TypeData for ReflectB { fn on_insert(&self) -> Option { - Some(|mut registration| { + Some(OnInsertTypeData::new(|mut registration| { registration.insert_data(ReflectC); - }) + })) } } struct ReflectC; impl TypeData for ReflectC { fn on_insert(&self) -> Option { - Some(|mut registration| { + Some(OnInsertTypeData::new(|mut registration| { registration.insert_data(ReflectA); - }) + })) } } @@ -492,10 +576,10 @@ mod tests { Self(PhantomData) } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::, _>(); - }) + })) } } @@ -507,10 +591,10 @@ mod tests { Self(PhantomData) } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::, _>(); - }) + })) } } @@ -522,10 +606,10 @@ mod tests { Self(PhantomData) } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::, _>(); - }) + })) } } @@ -546,9 +630,9 @@ mod tests { struct ReflectA; impl TypeData for ReflectA { fn on_register(&self) -> Option { - Some(|registry| { + Some(OnRegisterTypeData::new(|registry| { registry.register::(); - }) + })) } } @@ -582,10 +666,10 @@ mod tests { Self } - fn on_register() -> Option { - Some(|registry| { + fn on_register(_: &()) -> Option { + Some(OnRegisterTypeData::new(|registry| { registry.register::(); - }) + })) } } @@ -608,9 +692,9 @@ mod tests { struct ReflectA; impl TypeData for ReflectA { fn on_register(&self) -> Option { - Some(|registry| { + Some(OnRegisterTypeData::new(|registry| { registry.register::(); - }) + })) } } @@ -639,10 +723,10 @@ mod tests { Self } - fn on_register() -> Option { - Some(|registry| { + fn on_register(_: &()) -> Option { + Some(OnRegisterTypeData::new(|registry| { registry.register::(); - }) + })) } } @@ -654,4 +738,51 @@ mod tests { assert!(registry.contains(TypeId::of::())); assert!(registry.contains(TypeId::of::())); } + + #[test] + fn should_invoke_callbacks_with_input() { + #[derive(Reflect)] + struct Foo; + + #[derive(TypeData)] + struct ReflectA; + + #[derive(TypeData, PartialEq, Debug)] + struct InsertedData(String); + + #[derive(TypeData, PartialEq, Debug)] + struct RegisteredData(String); + + impl CreateTypeData for ReflectA { + fn create_type_data(_: String) -> Self { + Self + } + + fn on_insert(input: &String) -> Option { + let data = input.clone(); + Some(OnInsertTypeData::new(move |mut registration| { + registration.insert_data(InsertedData(data)); + })) + } + + fn on_register(input: &String) -> Option { + let data = input.clone(); + Some(OnRegisterTypeData::new(move |registry| { + registry.registration_scope(TypeId::of::(), |mut registration| { + registration.insert_data(RegisteredData(data)); + }); + })) + } + } + + let mut registry = TypeRegistry::empty(); + registry.register::(); + registry.register_type_data_with::(String::from("test")); + + let data = registry.get_type_data::(TypeId::of::()); + assert_eq!(data, Some(&InsertedData(String::from("test")))); + + let data = registry.get_type_data::(TypeId::of::()); + assert_eq!(data, Some(&RegisteredData(String::from("test")))); + } } diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 37929807ba42e..f4d2b1c7587b0 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -306,7 +306,7 @@ impl TypeRegistry { self.registrations.insert(type_id, registration); for callback in callbacks { - callback(self); + callback.call(self); } true @@ -372,6 +372,8 @@ impl TypeRegistry { ) }); + let on_create_data_insert = >::on_insert(¶ms); + let on_create_data_register = >::on_register(¶ms); let data = D::create_type_data(params); let on_register = data.on_register(); @@ -379,18 +381,18 @@ impl TypeRegistry { registration.insert_internal( TypeId::of::(), Box::new(data), - >::on_insert(), + on_create_data_insert, // Set to `None` since we already handle the `on_register` callback in this method None, true, ); if let Some(on_register) = on_register { - on_register(self); + on_register.call(self); } - if let Some(on_register) = >::on_register() { - on_register(self); + if let Some(on_register) = on_create_data_register { + on_register.call(self); } } @@ -506,7 +508,7 @@ impl TypeRegistry { pub fn registration_scope( &mut self, type_id: TypeId, - mut f: impl FnMut(TypeRegistrationMut<'_>), + f: impl FnOnce(TypeRegistrationMut<'_>), ) -> bool { let Some(registration) = self.registrations.get_mut(&type_id) else { return false; @@ -516,7 +518,7 @@ impl TypeRegistry { let callbacks = registration.flush_on_register_callbacks(); for callback in callbacks { - callback(self); + callback.call(self); } true @@ -839,9 +841,9 @@ macro_rules! type_data_insertion_methods { $($mut)? $self: $self_ty, input: I, ) -> $return_ty { + let on_insert = >::on_insert(&input); + let on_register = >::on_register(&input); let data = T::create_type_data(input); - let on_insert = >::on_insert(); - let on_register = >::on_register(); let has_deps = on_insert.is_some() || data.on_insert().is_some(); @@ -906,7 +908,7 @@ pub struct TypeRegistration { And since these don't need to be accessed as often or in performance-sensitive code, \ we can spare the extra layer of indirection" )] - pending_callbacks: Option>>, + pending_callbacks: Option>>, } impl Debug for TypeRegistration { @@ -980,10 +982,10 @@ impl TypeRegistration { match entry { indexmap::map::Entry::Vacant(entry) => { if let Some(on_insert) = entry.insert(data).on_insert() { - on_insert(TypeRegistrationMut::new(self)); + on_insert.call(TypeRegistrationMut::new(self)); } if let Some(on_insert) = on_insert { - on_insert(TypeRegistrationMut::new(self)); + on_insert.call(TypeRegistrationMut::new(self)); } if let Some(on_register) = on_register { self.pending_callbacks @@ -1007,12 +1009,16 @@ impl TypeRegistration { /// /// This includes all pending callbacks and callbacks from inserted type data. /// It should therefore only be used when the this [`TypeRegistration`] is first registered into a [`TypeRegistry`]. - fn flush_on_register_callbacks(&mut self) -> Vec { + fn flush_on_register_callbacks(&mut self) -> Vec { let pending_callbacks = self.pending_callbacks.take(); self.iter() .filter_map(|(_, data)| data.on_register()) - .chain(pending_callbacks.as_deref().into_iter().flatten().copied()) + .chain( + pending_callbacks + .into_iter() + .flat_map(|callbacks| callbacks.into_iter()), + ) .collect() } diff --git a/crates/bevy_settings/src/lib.rs b/crates/bevy_settings/src/lib.rs index b4669fd11bbd4..ac2db065a8e1b 100644 --- a/crates/bevy_settings/src/lib.rs +++ b/crates/bevy_settings/src/lib.rs @@ -181,10 +181,10 @@ impl CreateTypeData for ReflectSet } } - fn on_insert() -> Option { - Some(|mut registration| { + fn on_insert(_: &()) -> Option { + Some(OnInsertTypeData::new(|mut registration| { registration.register_type_data::(); - }) + })) } } From 7785d619149eafc35d61b3905f49c24768ab17b6 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 2 Jun 2026 10:28:35 -0700 Subject: [PATCH 4/7] Update example --- examples/reflection/type_data.rs | 83 ++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/examples/reflection/type_data.rs b/examples/reflection/type_data.rs index ecc2f51ee8613..ffb8c91b6cb50 100644 --- a/examples/reflection/type_data.rs +++ b/examples/reflection/type_data.rs @@ -2,7 +2,10 @@ use bevy::{ prelude::*, - reflect::{CreateTypeData, TypeRegistry}, + reflect::{ + CreateTypeData, OnInsertTypeData, OnRegisterTypeData, TypeData, TypeRegistration, + TypeRegistry, + }, }; // It's recommended to read this example from top to bottom. @@ -40,8 +43,8 @@ fn main() { // Let's create a type data struct for `Damageable` that we can associate with `Zombie`! - // Firstly, type data must be cloneable. - #[derive(Clone)] + // Firstly, the simplest way to create type data is to derive `TypeData`. + #[derive(TypeData)] // Next, they are usually named with the `Reflect` prefix (we'll see why in a bit). struct ReflectDamageable { // Type data can contain whatever you want, but it's common to include function pointers @@ -197,6 +200,80 @@ fn main() { let value: &dyn Health = reflect_health.get(value.as_reflect()).unwrap(); assert_eq!(value.health(), 50); + // One final thing you can do with type data is define callbacks for when they are inserted or registered. + // We'll create a new type data struct and give it a set of callbacks to see what this looks like. + struct MyTypeData; + + // We can add callbacks directly to type data, which will always be invoked. + impl TypeData for MyTypeData { + // This callback runs when the type data is inserted into a `TypeRegistration`. + // We can use it to insert other type data into the same registration. + fn on_insert(&self) -> Option { + Some(OnInsertTypeData::new(|_registration| { + println!("called TypeData::on_insert"); + })) + } + + // This callback runs when the `TypeRegistration` is registered (or is already registered). + // We can use it to register other types we know we'll need. + fn on_register(&self) -> Option { + Some(OnRegisterTypeData::new(|_registry| { + println!("called TypeData::on_register"); + })) + } + } + + // We can also register callbacks on `CreateTypeData`. + // These will only be run when using a method like `TypeRegistry::register_type_data` + // or `TypeRegistration::register_type_data`. + // However, it has the benefit of providing access to the type we're registering, `T`, as well as any input. + impl CreateTypeData for MyTypeData { + fn create_type_data(_: String) -> Self { + Self + } + + // This callback runs when the type data is inserted into a `TypeRegistration`. + // We can use it to insert other type data into the same registration. + fn on_insert(input: &String) -> Option { + let input = input.clone(); + Some(OnInsertTypeData::new(move |_registration| { + println!("called CreateTypeData::on_insert with input: {input}"); + })) + } + + // This callback runs when the `TypeRegistration` is registered (or is already registered). + // We can use it to register other types we know we'll need. + fn on_register(input: &String) -> Option { + let input = input.clone(); + Some(OnRegisterTypeData::new(move |_registry| { + println!("called CreateTypeData::on_register with input: {input}"); + })) + } + } + + println!("i32:"); + let registration = TypeRegistration::of::(); + // This should invoke `TypeData::on_insert`: + let registration = registration.insert_data(MyTypeData); + + let mut registry = TypeRegistry::empty(); + // This should invoke `TypeData::on_register`: + registry.add_registration(registration); + + println!("u32:"); + registry.register::(); + // This should invoke both `TypeData` and `CreateTypeData` callbacks: + registry.register_type_data_with::(String::from("HELLO!")); + + println!("f32:"); + // But again, note that `CreateTypeData` callbacks are not automatically invoked when used directly + let data = + >::create_type_data(String::from("HELLO!")); + let registration = TypeRegistration::of::().insert_data(data); + registry.add_registration(registration); + + // ========================================================================================== // + // Lastly, here's a list of some useful type data provided by Bevy that you might want to register for your types: // - `ReflectDefault` for types that implement `Default` // - `ReflectFromWorld` for types that implement `FromWorld` From 7de53f0599a56ccde723621e77d8861f84839473 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 2 Jun 2026 14:06:15 -0700 Subject: [PATCH 5/7] Fix compile tests --- .../compile_fail/tests/reflect_derive/custom_where_fail.rs | 4 ++-- .../compile_fail/tests/reflect_derive/custom_where_pass.rs | 4 ++-- .../compile_fail/tests/reflect_derive/type_data_fail.rs | 4 ++-- .../compile_fail/tests/reflect_derive/type_data_pass.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_fail.rs b/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_fail.rs index 379ecd18b201a..c8b2975fbd7cc 100644 --- a/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_fail.rs +++ b/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_fail.rs @@ -1,7 +1,7 @@ -use bevy_reflect::{CreateTypeData, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect, TypeData}; use core::marker::PhantomData; -#[derive(Clone)] +#[derive(TypeData)] struct ReflectMyTrait; impl CreateTypeData for ReflectMyTrait { diff --git a/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_pass.rs b/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_pass.rs index 02e62f737d281..cabc0fc34d70e 100644 --- a/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_pass.rs +++ b/crates/bevy_reflect/compile_fail/tests/reflect_derive/custom_where_pass.rs @@ -1,8 +1,8 @@ //@check-pass -use bevy_reflect::{CreateTypeData, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect, TypeData}; use core::marker::PhantomData; -#[derive(Clone)] +#[derive(TypeData)] struct ReflectMyTrait; impl CreateTypeData for ReflectMyTrait { diff --git a/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_fail.rs b/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_fail.rs index dcad3bc56a84f..01246ee0c7f2d 100644 --- a/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_fail.rs +++ b/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_fail.rs @@ -1,7 +1,7 @@ //@no-rustfix -use bevy_reflect::{CreateTypeData, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect, TypeData}; -#[derive(Clone)] +#[derive(TypeData)] struct ReflectMyTrait; impl CreateTypeData for ReflectMyTrait { diff --git a/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_pass.rs b/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_pass.rs index db2c8c67e3f53..0015aa989007a 100644 --- a/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_pass.rs +++ b/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_pass.rs @@ -1,7 +1,7 @@ //@check-pass -use bevy_reflect::{CreateTypeData, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect, TypeData}; -#[derive(Clone)] +#[derive(TypeData)] struct ReflectMyTrait; impl CreateTypeData for ReflectMyTrait { From 7174bbcd4cfa7b0191288860913c5cd1d359f3e7 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 2 Jun 2026 13:17:05 -0700 Subject: [PATCH 6/7] Add migration guide --- .../bevy_reflect_type_data_callbacks.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 _release-content/migration-guides/bevy_reflect_type_data_callbacks.md diff --git a/_release-content/migration-guides/bevy_reflect_type_data_callbacks.md b/_release-content/migration-guides/bevy_reflect_type_data_callbacks.md new file mode 100644 index 0000000000000..4698c922a7f43 --- /dev/null +++ b/_release-content/migration-guides/bevy_reflect_type_data_callbacks.md @@ -0,0 +1,53 @@ +--- +title: "Type data creation and registration changes" +pull_requests: [24518] +--- + +Type data definition and registration have been reworked to enable `on_insert` and `on_register` registration callbacks. + +Type data is no longer automatically implemented on types implementing `Clone`. +Instead, it must be manually implemented or derived: + +```rust +// BEFORE +#[derive(Clone)] +struct ReflectSomeTypeData; + +// AFTER +#[derive(TypeData)] +struct ReflectSomeTypeData; + +// or manually: +// impl TypeData for ReflectSomeTypeData {} +``` + +Additionally, type data can no longer be inserted directly onto a `&mut TypeRegistration`. +Instead, it must be inserted during construction, with an owned `TypeRegistration`. +This was done to ensure that registration callbacks weren't accidentally missed. + +```rust +// BEFORE +let mut registration = TypeRegistration::of::(); +registration.register_type_data::(); + +// AFTER +let registration = TypeRegistration::of::() + .register_type_data::(); +}); +``` + +Methods for registering type data on the `TypeRegistry` remain unchanged. +However, because of the new restrictions around inserting type data on `TypeRegistration`, +instances of `TypeRegistry::get_mut` can be replaced with the new `TypeRegistry::registration_scope` +in order to insert multiple type data without additional lookups. + +```rust +// BEFORE +let registration = registry.get_mut(TypeId::of::()); +registration.register_type_data::(); + +// AFTER +let registration = registry.registration_scope(TypeId::of::(), |mut registration| { + registration.register_type_data::(); +}); +``` From 6820649c43d31a4aff6aeff2e62813961129ec21 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:24:38 -0700 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: andriyDev Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> --- crates/bevy_reflect/derive/src/lib.rs | 2 +- crates/bevy_reflect/src/type_data.rs | 1 - crates/bevy_reflect/src/type_registry.rs | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index 0884b03c8ba41..be6bffd7ed854 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -512,7 +512,7 @@ pub fn derive_type_path(input: TokenStream) -> TokenStream { }) } -/// Derives the `TypePath` trait. +/// Derives the `TypeData` trait. #[proc_macro_derive(TypeData)] pub fn derive_type_data(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); diff --git a/crates/bevy_reflect/src/type_data.rs b/crates/bevy_reflect/src/type_data.rs index c63ce7be7a418..97e439f7e8548 100644 --- a/crates/bevy_reflect/src/type_data.rs +++ b/crates/bevy_reflect/src/type_data.rs @@ -18,7 +18,6 @@ //! # use bevy_reflect::TypeData; //! struct ReflectDebuggable; //! impl TypeData for ReflectDebuggable {} -//! //! ``` //! //! We can then register our type data on any type we want: diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index f4d2b1c7587b0..3020c5a724161 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -939,7 +939,7 @@ impl TypeRegistration { /// /// [type data]: TypeData #[deprecated( - since = "0.21.0", + since = "0.20.0", note = "This method will be removed in a future release. Use `TypeRegistry::insert_data` or `TypeRegistry::register_type_data` instead." )] pub fn insert(&mut self, data: T) { @@ -951,7 +951,7 @@ impl TypeRegistration { /// /// [type data]: TypeData #[deprecated( - since = "0.21.0", + since = "0.20.0", note = "This method will be removed in a future release. Use `TypeRegistry::contains` in conjunction with either `TypeRegistry::insert_data` or `TypeRegistry::register_type_data` instead." )] pub fn get_or_insert_data_with(&mut self, get_data: impl FnOnce() -> T) -> &mut T {