From ee3714abf2063e741c6698823bcc322c2c6e873d Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 6 Jun 2024 13:50:28 -0700 Subject: [PATCH 1/3] Allow type data to be given input Replaces `FromType` with `CreateTypeData` --- crates/bevy_app/src/app.rs | 2 +- crates/bevy_app/src/sub_app.rs | 2 +- crates/bevy_asset/src/reflect.rs | 10 +- crates/bevy_ecs/src/change_detection.rs | 4 +- crates/bevy_ecs/src/entity/clone_entities.rs | 4 +- crates/bevy_ecs/src/reflect/bundle.rs | 10 +- crates/bevy_ecs/src/reflect/component.rs | 20 +-- .../bevy_ecs/src/reflect/entity_commands.rs | 4 +- crates/bevy_ecs/src/reflect/from_world.rs | 10 +- crates/bevy_ecs/src/reflect/map_entities.rs | 6 +- crates/bevy_ecs/src/reflect/resource.rs | 10 +- crates/bevy_ecs/src/reflect/visit_entities.rs | 10 +- crates/bevy_ecs/src/system/builder.rs | 4 +- .../tests/reflect_derive/custom_where_fail.rs | 6 +- .../tests/reflect_derive/custom_where_pass.rs | 6 +- .../derive/src/container_attributes.rs | 68 ++++----- crates/bevy_reflect/derive/src/ident.rs | 18 +-- crates/bevy_reflect/derive/src/lib.rs | 17 ++- .../bevy_reflect/derive/src/registration.rs | 20 ++- .../derive/src/trait_reflection.rs | 6 +- crates/bevy_reflect/derive/src/type_data.rs | 57 ++++++++ crates/bevy_reflect/src/from_reflect.rs | 6 +- crates/bevy_reflect/src/impls/smallvec.rs | 10 +- crates/bevy_reflect/src/impls/std.rs | 71 +++++----- crates/bevy_reflect/src/lib.rs | 2 + .../src/serde/de/deserialize_with_registry.rs | 6 +- .../src/serde/ser/serialize_with_registry.rs | 6 +- crates/bevy_reflect/src/std_traits.rs | 8 +- crates/bevy_reflect/src/type_data.rs | 133 ++++++++++++++++++ crates/bevy_reflect/src/type_registry.rs | 88 ++++++------ crates/bevy_state/src/reflect.rs | 18 +-- examples/reflection/type_data.rs | 8 +- 32 files changed, 424 insertions(+), 226 deletions(-) create mode 100644 crates/bevy_reflect/derive/src/type_data.rs create mode 100644 crates/bevy_reflect/src/type_data.rs diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 9c5669423ffed..854a2abbd5e0f 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -622,7 +622,7 @@ impl App { #[cfg(feature = "bevy_reflect")] pub fn register_type_data< T: bevy_reflect::Reflect + bevy_reflect::TypePath, - D: bevy_reflect::TypeData + bevy_reflect::FromType, + D: bevy_reflect::CreateTypeData, >( &mut self, ) -> &mut Self { diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index 1843143c2ed46..dfb29ecd3b8eb 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -452,7 +452,7 @@ impl SubApp { #[cfg(feature = "bevy_reflect")] pub fn register_type_data< T: bevy_reflect::Reflect + bevy_reflect::TypePath, - D: bevy_reflect::TypeData + bevy_reflect::FromType, + D: bevy_reflect::CreateTypeData, >( &mut self, ) -> &mut Self { diff --git a/crates/bevy_asset/src/reflect.rs b/crates/bevy_asset/src/reflect.rs index 5c436c10610f0..95d89b5f9f08d 100644 --- a/crates/bevy_asset/src/reflect.rs +++ b/crates/bevy_asset/src/reflect.rs @@ -2,7 +2,7 @@ use alloc::boxed::Box; use core::any::{Any, TypeId}; use bevy_ecs::world::{unsafe_world_cell::UnsafeWorldCell, World}; -use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect}; +use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, Reflect}; use crate::{Asset, AssetId, Assets, Handle, UntypedAssetId, UntypedHandle}; @@ -132,8 +132,8 @@ impl ReflectAsset { } } -impl FromType for ReflectAsset { - fn from_type() -> Self { +impl CreateTypeData for ReflectAsset { + fn create_type_data(_input: ()) -> Self { ReflectAsset { handle_type_id: TypeId::of::>(), assets_resource_type_id: TypeId::of::>(), @@ -228,8 +228,8 @@ impl ReflectHandle { } } -impl FromType> for ReflectHandle { - fn from_type() -> Self { +impl CreateTypeData> for ReflectHandle { + fn create_type_data(_input: ()) -> Self { ReflectHandle { asset_type_id: TypeId::of::(), downcast_handle_untyped: |handle: &dyn Any| { diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index a0e00fdc15a23..57f6dfca2f91a 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -1471,7 +1471,7 @@ impl MaybeLocation { mod tests { use bevy_ecs_macros::Resource; use bevy_ptr::PtrMut; - use bevy_reflect::{FromType, ReflectFromPtr}; + use bevy_reflect::{CreateTypeData, ReflectFromPtr}; use core::ops::{Deref, DerefMut}; use crate::{ @@ -1783,7 +1783,7 @@ mod tests { changed_by: caller.as_mut(), }; - let reflect_from_ptr = >::from_type(); + let reflect_from_ptr = >::create_type_data(()); let mut new = value.map_unchanged(|ptr| { // SAFETY: The underlying type of `ptr` matches `reflect_from_ptr`. diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index b9564c1352c33..949ff172f8486 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -860,7 +860,7 @@ mod tests { system::Commands, }; use alloc::vec; - use bevy_reflect::{std_traits::ReflectDefault, FromType, Reflect, ReflectFromPtr}; + use bevy_reflect::{std_traits::ReflectDefault, CreateTypeData, Reflect, ReflectFromPtr}; #[test] fn clone_entity_using_reflect() { @@ -977,7 +977,7 @@ mod tests { registry .get_mut(core::any::TypeId::of::()) .unwrap() - .insert(>::from_type()); + .insert(>::create_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 6216adf40c2f2..6f16807b73c5c 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -15,7 +15,7 @@ use crate::{ world::{EntityMut, EntityWorldMut}, }; use bevy_reflect::{ - FromReflect, FromType, PartialReflect, Reflect, ReflectRef, TypePath, TypeRegistry, + CreateTypeData, FromReflect, PartialReflect, Reflect, ReflectRef, TypePath, TypeRegistry, }; use super::{from_reflect_with_fallback, ReflectComponent}; @@ -52,12 +52,12 @@ pub struct ReflectBundleFns { impl ReflectBundleFns { /// Get the default set of [`ReflectBundleFns`] for a specific bundle type using its - /// [`FromType`] implementation. + /// [`CreateTypeData`] implementation. /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. pub fn new() -> Self { - >::from_type().0 + >::create_type_data(()).0 } } @@ -153,8 +153,8 @@ impl ReflectBundle { } } -impl FromType for ReflectBundle { - fn from_type() -> Self { +impl CreateTypeData for ReflectBundle { + fn create_type_data(_input: ()) -> Self { ReflectBundle(ReflectBundleFns { insert: |entity, reflected_bundle, registry| { let bundle = entity.world_scope(|world| { diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index da772b079fc5b..1d490dade65f4 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -16,14 +16,14 @@ //! [`get_type_registration`] method (see the relevant code[^1]). //! //! ``` -//! # use bevy_reflect::{FromType, Reflect}; +//! # use bevy_reflect::{CreateTypeData, Reflect}; //! # use bevy_ecs::prelude::{ReflectComponent, Component}; //! # #[derive(Default, Reflect, Component)] //! # struct A; //! # impl A { //! # fn foo() { //! # let mut registration = bevy_reflect::TypeRegistration::of::(); -//! registration.insert::(FromType::::from_type()); +//! registration.insert::(CreateTypeData::::create_type_data(())); //! # } //! # } //! ``` @@ -32,10 +32,10 @@ //! The user can access the `ReflectComponent` for type `T` through the type registry, //! as per the `trait_reflection.rs` example. //! -//! The `FromType::::from_type()` in the previous line calls the `FromType` -//! implementation of `ReflectComponent`. +//! The `CreateTypeData::::create_type_data(())` in the previous line calls the +//! `CreateTypeData` implementation of `ReflectComponent`. //! -//! The `FromType` impl creates a function per field of [`ReflectComponentFns`]. +//! The `CreateTypeData` impl creates a function per field of [`ReflectComponentFns`]. //! In those functions, we call generic methods on [`World`] and [`EntityWorldMut`]. //! //! The result is a `ReflectComponent` completely independent of `C`, yet capable @@ -69,7 +69,7 @@ use crate::{ FilteredEntityRef, World, }, }; -use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, Reflect, TypePath, TypeRegistry}; use disqualified::ShortName; /// A struct used to operate on reflected [`Component`] trait of a type. @@ -138,12 +138,12 @@ pub struct ReflectComponentFns { impl ReflectComponentFns { /// Get the default set of [`ReflectComponentFns`] for a specific component type using its - /// [`FromType`] implementation. + /// [`CreateTypeData`] implementation. /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. pub fn new() -> Self { - >::from_type().0 + >::create_type_data(()).0 } } @@ -312,8 +312,8 @@ impl ReflectComponent { } } -impl FromType for ReflectComponent { - fn from_type() -> Self { +impl CreateTypeData for ReflectComponent { + fn create_type_data(_input: ()) -> Self { // TODO: Currently we panic if a component is immutable and you use // reflection to mutate it. Perhaps the mutation methods should be fallible? ReflectComponent(ReflectComponentFns { diff --git a/crates/bevy_ecs/src/reflect/entity_commands.rs b/crates/bevy_ecs/src/reflect/entity_commands.rs index 725c61265f865..9a5598db67bde 100644 --- a/crates/bevy_ecs/src/reflect/entity_commands.rs +++ b/crates/bevy_ecs/src/reflect/entity_commands.rs @@ -38,7 +38,7 @@ pub trait ReflectCommandExt { /// /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::reflect::{ReflectCommandExt, ReflectBundle}; - /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + /// # use bevy_reflect::{FromReflect, CreateTypeData, Reflect, TypeRegistry}; /// // A resource that can hold any component that implements reflect as a boxed reflect component /// #[derive(Resource)] /// struct Prefab { @@ -126,7 +126,7 @@ pub trait ReflectCommandExt { /// /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::reflect::{ReflectCommandExt, ReflectBundle}; - /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + /// # use bevy_reflect::{FromReflect, CreateTypeData, Reflect, TypeRegistry}; /// /// // A resource that can hold any component or bundle that implements reflect as a boxed reflect /// #[derive(Resource)] diff --git a/crates/bevy_ecs/src/reflect/from_world.rs b/crates/bevy_ecs/src/reflect/from_world.rs index c4f5912ff4b5f..ea6dec5a982c2 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 [`super::component`], but for [`FromWorld`]. use alloc::boxed::Box; -use bevy_reflect::{FromType, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect}; use crate::world::{FromWorld, World}; @@ -27,12 +27,12 @@ pub struct ReflectFromWorldFns { impl ReflectFromWorldFns { /// Get the default set of [`ReflectFromWorldFns`] for a specific type using its - /// [`FromType`] implementation. + /// [`CreateTypeData`] implementation. /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. pub fn new() -> Self { - >::from_type().0 + >::create_type_data(()).0 } } @@ -78,8 +78,8 @@ impl ReflectFromWorld { } } -impl FromType for ReflectFromWorld { - fn from_type() -> Self { +impl CreateTypeData for ReflectFromWorld { + fn create_type_data(_input: ()) -> Self { ReflectFromWorld(ReflectFromWorldFns { from_world: |world| Box::new(B::from_world(world)), }) diff --git a/crates/bevy_ecs/src/reflect/map_entities.rs b/crates/bevy_ecs/src/reflect/map_entities.rs index 04fc25579906d..04b597ab9c953 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::{FromReflect, FromType, PartialReflect}; +use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect}; /// For a specific type of value, this maps any fields with values of type [`Entity`] to a new world. /// @@ -25,8 +25,8 @@ impl ReflectMapEntities { } } -impl FromType for ReflectMapEntities { - fn from_type() -> Self { +impl CreateTypeData for ReflectMapEntities { + fn create_type_data(_input: ()) -> Self { ReflectMapEntities { map_entities: |reflected, mut mapper| { let mut concrete = C::from_reflect(reflected).expect("reflected type should match"); diff --git a/crates/bevy_ecs/src/reflect/resource.rs b/crates/bevy_ecs/src/reflect/resource.rs index 34e593a6ef00e..309f25f3c5b57 100644 --- a/crates/bevy_ecs/src/reflect/resource.rs +++ b/crates/bevy_ecs/src/reflect/resource.rs @@ -10,7 +10,7 @@ use crate::{ resource::Resource, world::{unsafe_world_cell::UnsafeWorldCell, FilteredResources, FilteredResourcesMut, World}, }; -use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect, Reflect, TypePath, TypeRegistry}; use super::from_reflect_with_fallback; @@ -68,12 +68,12 @@ pub struct ReflectResourceFns { impl ReflectResourceFns { /// Get the default set of [`ReflectResourceFns`] for a specific resource type using its - /// [`FromType`] implementation. + /// [`CreateTypeData`] implementation. /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. pub fn new() -> Self { - >::from_type().0 + >::create_type_data(()).0 } } @@ -200,8 +200,8 @@ impl ReflectResource { } } -impl FromType for ReflectResource { - fn from_type() -> Self { +impl CreateTypeData for ReflectResource { + fn create_type_data(_input: ()) -> Self { ReflectResource(ReflectResourceFns { insert: |world, reflected_resource, registry| { let resource = from_reflect_with_fallback::(reflected_resource, world, registry); diff --git a/crates/bevy_ecs/src/reflect/visit_entities.rs b/crates/bevy_ecs/src/reflect/visit_entities.rs index 11f02612ba1f9..b0c8482f105fc 100644 --- a/crates/bevy_ecs/src/reflect/visit_entities.rs +++ b/crates/bevy_ecs/src/reflect/visit_entities.rs @@ -1,5 +1,5 @@ use crate::entity::{Entity, VisitEntities, VisitEntitiesMut}; -use bevy_reflect::{FromReflect, FromType, PartialReflect}; +use bevy_reflect::{CreateTypeData, FromReflect, PartialReflect}; /// For a reflected value, apply an operation to all contained entities. /// @@ -17,8 +17,8 @@ impl ReflectVisitEntities { } } -impl FromType for ReflectVisitEntities { - fn from_type() -> Self { +impl CreateTypeData for ReflectVisitEntities { + fn create_type_data(_input: ()) -> Self { ReflectVisitEntities { visit_entities: |component, f| { let concrete = C::from_reflect(component).unwrap(); @@ -49,8 +49,8 @@ impl ReflectVisitEntitiesMut { } } -impl FromType for ReflectVisitEntitiesMut { - fn from_type() -> Self { +impl CreateTypeData for ReflectVisitEntitiesMut { + fn create_type_data(_input: ()) -> Self { ReflectVisitEntitiesMut { visit_entities_mut: |component, f| { let mut concrete = C::from_reflect(component).unwrap(); diff --git a/crates/bevy_ecs/src/system/builder.rs b/crates/bevy_ecs/src/system/builder.rs index 6261b9e35587e..31a3853df89bd 100644 --- a/crates/bevy_ecs/src/system/builder.rs +++ b/crates/bevy_ecs/src/system/builder.rs @@ -719,7 +719,7 @@ mod tests { system::{Local, RunSystemOnce}, }; use alloc::vec; - use bevy_reflect::{FromType, Reflect, ReflectRef}; + use bevy_reflect::{CreateTypeData, Reflect, ReflectRef}; use super::*; @@ -1087,7 +1087,7 @@ mod tests { }),) .build_state(&mut world) .build_system(|res: FilteredResources| { - let reflect_resource = >::from_type(); + let reflect_resource = >::create_type_data(()); let ReflectRef::Struct(reflect_struct) = reflect_resource.reflect(res).unwrap().reflect_ref() else { 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 28370ca7555de..379ecd18b201a 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,11 +1,11 @@ -use bevy_reflect::{FromType, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect}; use core::marker::PhantomData; #[derive(Clone)] struct ReflectMyTrait; -impl FromType for ReflectMyTrait { - fn from_type() -> Self { +impl CreateTypeData for ReflectMyTrait { + fn create_type_data(_input: ()) -> Self { Self } } 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 456afd35f5240..02e62f737d281 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,12 +1,12 @@ //@check-pass -use bevy_reflect::{FromType, Reflect}; +use bevy_reflect::{CreateTypeData, Reflect}; use core::marker::PhantomData; #[derive(Clone)] struct ReflectMyTrait; -impl FromType for ReflectMyTrait { - fn from_type() -> Self { +impl CreateTypeData for ReflectMyTrait { + fn create_type_data(_input: ()) -> Self { Self } } diff --git a/crates/bevy_reflect/derive/src/container_attributes.rs b/crates/bevy_reflect/derive/src/container_attributes.rs index bdb94db06bc7e..a109afff0e3dd 100644 --- a/crates/bevy_reflect/derive/src/container_attributes.rs +++ b/crates/bevy_reflect/derive/src/container_attributes.rs @@ -7,7 +7,7 @@ use crate::{ attribute_parser::terminated_parser, custom_attributes::CustomAttributes, - derive_data::ReflectTraitToImpl, + derive_data::ReflectTraitToImpl, type_data::TypeDataRegistration, }; use bevy_macro_utils::fq_std::{FQAny, FQOption}; use proc_macro2::{Ident, Span}; @@ -27,12 +27,6 @@ mod kw { syn::custom_keyword!(opaque); } -// The "special" trait idents that are used internally for reflection. -// Received via attributes like `#[reflect(PartialEq, Hash, ...)]` -const DEBUG_ATTR: &str = "Debug"; -const PARTIAL_EQ_ATTR: &str = "PartialEq"; -const HASH_ATTR: &str = "Hash"; - // The traits listed below are not considered "special" (i.e. they use the `ReflectMyTrait` syntax) // but useful to know exist nonetheless pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault"; @@ -184,7 +178,7 @@ pub(crate) struct ContainerAttributes { no_field_bounds: bool, custom_attributes: CustomAttributes, is_opaque: bool, - idents: Vec, + type_data: Vec, } impl ContainerAttributes { @@ -243,33 +237,32 @@ impl ContainerAttributes { } else if lookahead.peek(kw::Hash) { self.parse_hash(input) } else if lookahead.peek(Ident::peek_any) { - self.parse_ident(input) + self.parse_type_data(input) } else { Err(lookahead.error()) } } - /// Parse an ident (for registration). + /// Parse a type data registration. /// /// Examples: - /// - `#[reflect(MyTrait)]` (registers `ReflectMyTrait`) - fn parse_ident(&mut self, input: ParseStream) -> syn::Result<()> { - let ident = input.parse::()?; - - if input.peek(token::Paren) { - return Err(syn::Error::new(ident.span(), format!( - "only [{DEBUG_ATTR:?}, {PARTIAL_EQ_ATTR:?}, {HASH_ATTR:?}] may specify custom functions", - ))); + /// - `#[reflect(Default)]` + /// - `#[reflect(Hash(custom_hash_fn))]` + fn parse_type_data(&mut self, input: ParseStream) -> syn::Result<()> { + let type_data = input.parse::()?; + + if self + .type_data + .iter() + .any(|existing| existing.ident() == type_data.ident()) + { + return Err(syn::Error::new( + type_data.ident().span(), + CONFLICTING_TYPE_DATA_MESSAGE, + )); } - let ident_name = ident.to_string(); - - // Create the reflect ident - let mut reflect_ident = crate::ident::get_reflect_ident(&ident_name); - // We set the span to the old ident so any compile errors point to that ident instead - reflect_ident.set_span(ident.span()); - - add_unique_ident(&mut self.idents, reflect_ident)?; + self.type_data.push(type_data); Ok(()) } @@ -432,12 +425,14 @@ impl ContainerAttributes { /// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`) /// is registered for this type. pub fn contains(&self, name: &str) -> bool { - self.idents.iter().any(|ident| ident == name) + self.type_data + .iter() + .any(|data| data.reflect_ident() == name) } - /// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`). - pub fn idents(&self) -> &[Ident] { - &self.idents + /// The list of type data registrations. + pub fn type_data(&self) -> &[TypeDataRegistration] { + &self.type_data } /// The `FromReflect` configuration found within `#[reflect(...)]` attributes on this type. @@ -543,19 +538,6 @@ impl ContainerAttributes { } } -/// Adds an identifier to a vector of identifiers if it is not already present. -/// -/// Returns an error if the identifier already exists in the list. -fn add_unique_ident(idents: &mut Vec, ident: Ident) -> Result<(), syn::Error> { - let ident_name = ident.to_string(); - if idents.iter().any(|i| i == ident_name.as_str()) { - return Err(syn::Error::new(ident.span(), CONFLICTING_TYPE_DATA_MESSAGE)); - } - - idents.push(ident); - Ok(()) -} - /// Extract a boolean value from an expression. /// /// The mapper exists so that the caller can conditionally choose to use the given diff --git a/crates/bevy_reflect/derive/src/ident.rs b/crates/bevy_reflect/derive/src/ident.rs index f14c7ff98f68e..eeac06bbb9785 100644 --- a/crates/bevy_reflect/derive/src/ident.rs +++ b/crates/bevy_reflect/derive/src/ident.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Ident, Span}; +use proc_macro2::Ident; use syn::Member; /// Returns the "reflected" ident for a given string. @@ -6,18 +6,18 @@ use syn::Member; /// # Example /// /// ``` -/// # use proc_macro2::Ident; +/// # use proc_macro2::{Ident, Span}; /// # // We can't import this method because of its visibility. -/// # fn get_reflect_ident(name: &str) -> Ident { -/// # let reflected = format!("Reflect{name}"); -/// # Ident::new(&reflected, proc_macro2::Span::call_site()) +/// # fn get_reflect_ident(base_ident: &Ident) -> Ident { +/// # let reflected = format!("Reflect{base_ident}"); +/// # Ident::new(&reflected, base_ident.span()) /// # } -/// let reflected: Ident = get_reflect_ident("Hash"); +/// let reflected: Ident = get_reflect_ident(&Ident::new("Hash", Span::call_site())); /// assert_eq!("ReflectHash", reflected.to_string()); /// ``` -pub(crate) fn get_reflect_ident(name: &str) -> Ident { - let reflected = format!("Reflect{name}"); - Ident::new(&reflected, Span::call_site()) +pub(crate) fn get_reflect_ident(base_ident: &Ident) -> Ident { + let reflected = format!("Reflect{base_ident}"); + Ident::new(&reflected, base_ident.span()) } /// Returns a [`Member`] made of `ident` or `index` if `ident` is `None`. diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index 276371427b620..b55e2541832cc 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -37,6 +37,7 @@ mod serialization; mod string_expr; mod struct_utility; mod trait_reflection; +mod type_data; mod type_path; mod where_clause_options; @@ -144,6 +145,18 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre /// This is often used with traits that have been marked by the [`#[reflect_trait]`](macro@reflect_trait) /// macro in order to register the type's implementation of that trait. /// +/// ### Type Data Input +/// +/// If the type data's implementation allows for input, +/// that input can be specified using a function-call-like syntax. +/// For example, `#[reflect(Foo(42))]` would pass the value `42` +/// as the input to the `ReflectFoo` type data. +/// +/// Some type data accept a tuple of values. +/// The macro will automatically convert the input to a tuple if given >=2 parameters. +/// For example, `#[reflect(Foo(42, "hello"))]` would pass the tuple `(42, "hello")` +/// as the input to the `ReflectFoo` type data. +/// /// ### Default Registrations /// /// The following types are automatically registered when deriving `Reflect`: @@ -477,7 +490,7 @@ pub fn derive_type_path(input: TokenStream) -> TokenStream { /// Because of this, **it can only be used on [object-safe] traits.** /// /// For a trait named `MyTrait`, this will generate the struct `ReflectMyTrait`. -/// The generated struct can be created using `FromType` with any type that implements the trait. +/// The generated struct can be created using `CreateTypeData` with any type that implements the trait. /// The creation and registration of this generated struct as type data can be automatically handled /// by [`#[derive(Reflect)]`](Reflect). /// @@ -502,7 +515,7 @@ pub fn derive_type_path(input: TokenStream) -> TokenStream { /// } /// /// // We can create the type data manually if we wanted: -/// let my_trait: ReflectMyTrait = FromType::::from_type(); +/// let my_trait: ReflectMyTrait = CreateTypeData::::create_type_data(()); /// /// // Or we can simply get it from the registry: /// let mut registry = TypeRegistry::default(); diff --git a/crates/bevy_reflect/derive/src/registration.rs b/crates/bevy_reflect/derive/src/registration.rs index f60791215cef8..00b5eee634245 100644 --- a/crates/bevy_reflect/derive/src/registration.rs +++ b/crates/bevy_reflect/derive/src/registration.rs @@ -16,7 +16,6 @@ pub(crate) fn impl_get_type_registration<'a>( ) -> proc_macro2::TokenStream { let type_path = meta.type_path(); let bevy_reflect_path = meta.bevy_reflect_path(); - let registration_data = meta.attrs().idents(); let type_deps_fn = type_dependencies.map(|deps| { quote! { @@ -32,7 +31,9 @@ pub(crate) fn impl_get_type_registration<'a>( let from_reflect_data = if meta.from_reflect().should_auto_derive() { Some(quote! { - registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::::from_type()); + registration.insert( + <#bevy_reflect_path::ReflectFromReflect as #bevy_reflect_path::CreateTypeData>::create_type_data(()) + ); }) } else { None @@ -45,15 +46,26 @@ pub(crate) fn impl_get_type_registration<'a>( } }); + let type_data = meta.attrs().type_data().iter().map(|data| { + let reflect_ident = data.reflect_ident(); + let args = data.args(); + + quote! { + <#reflect_ident as #bevy_reflect_path::CreateTypeData>::create_type_data((#args)) + } + }); + quote! { #[allow(unused_mut)] 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>(#bevy_reflect_path::FromType::::from_type()); + registration.insert( + <#bevy_reflect_path::ReflectFromPtr as #bevy_reflect_path::CreateTypeData::>::create_type_data(()) + ); #from_reflect_data #serialization_data - #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::::from_type());)* + #(registration.insert(#type_data);)* registration } diff --git a/crates/bevy_reflect/derive/src/trait_reflection.rs b/crates/bevy_reflect/derive/src/trait_reflection.rs index 8df40e47a5fd0..ce7f26a9b87f2 100644 --- a/crates/bevy_reflect/derive/src/trait_reflection.rs +++ b/crates/bevy_reflect/derive/src/trait_reflection.rs @@ -30,7 +30,7 @@ pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStr let item_trait = &trait_info.item_trait; let trait_ident = &item_trait.ident; let trait_vis = &item_trait.vis; - let reflect_trait_ident = crate::ident::get_reflect_ident(&item_trait.ident.to_string()); + let reflect_trait_ident = crate::ident::get_reflect_ident(&item_trait.ident); let bevy_reflect_path = crate::meta::get_bevy_reflect_path(); let struct_doc = format!( @@ -74,8 +74,8 @@ pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStr } } - impl #bevy_reflect_path::FromType for #reflect_trait_ident { - fn from_type() -> Self { + impl #bevy_reflect_path::CreateTypeData for #reflect_trait_ident { + fn create_type_data(_input: ()) -> Self { Self { get_func: |reflect_value| { ::downcast_ref::(reflect_value).map(|value| value as &dyn #trait_ident) diff --git a/crates/bevy_reflect/derive/src/type_data.rs b/crates/bevy_reflect/derive/src/type_data.rs new file mode 100644 index 0000000000000..0ca145f865477 --- /dev/null +++ b/crates/bevy_reflect/derive/src/type_data.rs @@ -0,0 +1,57 @@ +use proc_macro2::Ident; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::{parenthesized, token, Expr, Token}; + +/// A `TypeData` registration. +/// +/// This would be the `Default` and `Hash(custom_hash_fn)` in +/// `#[reflect(Default, Hash(custom_hash_fn))]`. +#[derive(Clone)] +pub(crate) struct TypeDataRegistration { + ident: Ident, + reflect_ident: Ident, + args: Punctuated, +} + +impl TypeDataRegistration { + /// The shortened ident of the registration. + /// + /// This would be `Default` in `#[reflect(Default)]`. + pub fn ident(&self) -> &Ident { + &self.ident + } + + /// The full reflection ident of the registration. + /// + /// This would be `ReflectDefault` in `#[reflect(Default)]`. + pub fn reflect_ident(&self) -> &Ident { + &self.reflect_ident + } + + /// The optional arguments of the type data. + pub fn args(&self) -> &Punctuated { + &self.args + } +} + +impl Parse for TypeDataRegistration { + fn parse(input: ParseStream) -> syn::Result { + let ident = input.parse::()?; + let reflect_ident = crate::ident::get_reflect_ident(&ident); + + let args = if input.peek(token::Paren) { + let content; + parenthesized!(content in input); + content.parse_terminated(Expr::parse, Token![,])? + } else { + Default::default() + }; + + Ok(Self { + ident, + reflect_ident, + args, + }) + } +} diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index dc113d869f3c7..8d1235028ed1f 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -1,4 +1,4 @@ -use crate::{FromType, PartialReflect, Reflect}; +use crate::{CreateTypeData, PartialReflect, Reflect}; use alloc::boxed::Box; /// A trait that enables types to be dynamically constructed from reflected data. @@ -117,8 +117,8 @@ impl ReflectFromReflect { } } -impl FromType for ReflectFromReflect { - fn from_type() -> Self { +impl CreateTypeData for ReflectFromReflect { + fn create_type_data(_input: ()) -> Self { Self { from_reflect: |reflect_value| { T::from_reflect(reflect_value).map(|value| Box::new(value) as Box) diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index afb75aff8d997..b3d93dc0aea8a 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -4,10 +4,10 @@ use core::any::Any; use smallvec::{Array as SmallArray, SmallVec}; use crate::{ - utility::GenericTypeInfoCell, ApplyError, FromReflect, FromType, Generics, GetTypeRegistration, - List, ListInfo, ListIter, MaybeTyped, PartialReflect, Reflect, ReflectFromPtr, ReflectKind, - ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, - Typed, + utility::GenericTypeInfoCell, ApplyError, CreateTypeData, FromReflect, Generics, + GetTypeRegistration, List, ListInfo, ListIter, MaybeTyped, PartialReflect, Reflect, + ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeParamInfo, + TypePath, TypeRegistration, Typed, }; impl List for SmallVec @@ -217,7 +217,7 @@ where { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); - registration.insert::(FromType::>::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); registration } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 119b66e3ed6d0..c0fd1aff5d543 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -9,11 +9,12 @@ use crate::{ reflect::impl_full_reflect, set_apply, set_partial_eq, set_try_apply, utility::{reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell}, - ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicSet, DynamicTypePath, FromReflect, - FromType, Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, - MaybeTyped, OpaqueInfo, PartialReflect, Reflect, ReflectDeserialize, ReflectFromPtr, - ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set, - SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, TypeRegistry, Typed, + ApplyError, Array, ArrayInfo, ArrayIter, CreateTypeData, DynamicMap, DynamicSet, + DynamicTypePath, FromReflect, Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, + MapInfo, MapIter, MaybeTyped, OpaqueInfo, PartialReflect, Reflect, ReflectDeserialize, + ReflectFromPtr, ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, + ReflectSerialize, Set, SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, + TypeRegistry, Typed, }; use alloc::{ borrow::{Cow, ToOwned}, @@ -245,15 +246,15 @@ macro_rules! impl_reflect_for_atomic { { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); // Serde only supports atomic types when the "std" feature is enabled #[cfg(feature = "std")] { - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); } registration @@ -559,8 +560,8 @@ macro_rules! impl_reflect_for_veclike { { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::<$ty>(); - registration.insert::(FromType::<$ty>::from_type()); - registration.insert::(FromType::<$ty>::from_type()); + registration.insert::(CreateTypeData::<$ty>::create_type_data(())); + registration.insert::(CreateTypeData::<$ty>::create_type_data(())); registration } @@ -809,8 +810,8 @@ macro_rules! impl_reflect_for_hashmap { { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } @@ -1035,8 +1036,8 @@ macro_rules! impl_reflect_for_hashset { { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } @@ -1297,8 +1298,8 @@ where { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } } @@ -1661,10 +1662,16 @@ impl Typed for Cow<'static, str> { impl GetTypeRegistration for Cow<'static, str> { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); - registration.insert::(FromType::>::from_type()); - registration.insert::(FromType::>::from_type()); - registration.insert::(FromType::>::from_type()); - registration.insert::(FromType::>::from_type()); + registration.insert::( + CreateTypeData::>::create_type_data(()), + ); + registration + .insert::(CreateTypeData::>::create_type_data(())); + registration.insert::( + CreateTypeData::>::create_type_data(()), + ); + registration + .insert::(CreateTypeData::>::create_type_data(())); registration } } @@ -1986,8 +1993,8 @@ impl Typed for &'static str { impl GetTypeRegistration for &'static str { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } } @@ -2124,8 +2131,8 @@ impl Typed for &'static Path { impl GetTypeRegistration for &'static Path { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } } @@ -2278,10 +2285,10 @@ impl FromReflect for Cow<'static, Path> { impl GetTypeRegistration for Cow<'static, Path> { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } } @@ -2418,8 +2425,8 @@ impl Typed for &'static Location<'static> { impl GetTypeRegistration for &'static Location<'static> { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); - registration.insert::(FromType::::from_type()); - registration.insert::(FromType::::from_type()); + registration.insert::(CreateTypeData::::create_type_data(())); + registration.insert::(CreateTypeData::::create_type_data(())); registration } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index cf38c1da5086f..78869405e7dd8 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -583,6 +583,7 @@ mod set; mod struct_trait; mod tuple; mod tuple_struct; +mod type_data; mod type_info; mod type_path; mod type_registry; @@ -647,6 +648,7 @@ pub use set::*; pub use struct_trait::*; pub use tuple::*; pub use tuple_struct::*; +pub use type_data::*; pub use type_info::*; pub use type_path::*; pub use type_registry::*; 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 f92a8e68e24da..0e8248e87ef73 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::{FromType, PartialReflect, TypeRegistry}; +use crate::{CreateTypeData, PartialReflect, TypeRegistry}; use alloc::boxed::Box; use serde::Deserializer; @@ -71,10 +71,10 @@ impl ReflectDeserializeWithRegistry { } } -impl DeserializeWithRegistry<'de>> FromType +impl DeserializeWithRegistry<'de>> CreateTypeData for ReflectDeserializeWithRegistry { - fn from_type() -> Self { + fn create_type_data(_input: ()) -> Self { Self { deserialize: |deserializer, registry| { Ok(Box::new(T::deserialize(deserializer, registry)?)) 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 9c5bfb06f1ca8..a52bb81953ea8 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::{FromType, Reflect, TypeRegistry}; +use crate::{CreateTypeData, Reflect, TypeRegistry}; use alloc::boxed::Box; use serde::{Serialize, Serializer}; @@ -69,8 +69,8 @@ impl ReflectSerializeWithRegistry { } } -impl FromType for ReflectSerializeWithRegistry { - fn from_type() -> Self { +impl CreateTypeData for ReflectSerializeWithRegistry { + fn create_type_data(_input: ()) -> Self { Self { serialize: |value: &dyn Reflect, registry| { let value = value.downcast_ref::().unwrap_or_else(|| { diff --git a/crates/bevy_reflect/src/std_traits.rs b/crates/bevy_reflect/src/std_traits.rs index cad001132bd25..06ce315d68512 100644 --- a/crates/bevy_reflect/src/std_traits.rs +++ b/crates/bevy_reflect/src/std_traits.rs @@ -1,9 +1,9 @@ -use crate::{FromType, Reflect}; +use crate::{CreateTypeData, Reflect}; use alloc::boxed::Box; /// A struct used to provide the default value of a type. /// -/// A [`ReflectDefault`] for type `T` can be obtained via [`FromType::from_type`]. +/// A [`ReflectDefault`] for type `T` can be obtained via [`CreateTypeData::create_type_data`]. #[derive(Clone)] pub struct ReflectDefault { default: fn() -> Box, @@ -15,8 +15,8 @@ impl ReflectDefault { } } -impl FromType for ReflectDefault { - fn from_type() -> Self { +impl CreateTypeData for ReflectDefault { + fn create_type_data(_input: ()) -> Self { ReflectDefault { default: || Box::::default(), } diff --git a/crates/bevy_reflect/src/type_data.rs b/crates/bevy_reflect/src/type_data.rs new file mode 100644 index 0000000000000..2c741ec3b7917 --- /dev/null +++ b/crates/bevy_reflect/src/type_data.rs @@ -0,0 +1,133 @@ +use ::alloc::boxed::Box; +use downcast_rs::{impl_downcast, Downcast}; + +/// 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`. +/// +/// For creating your own type data, see the [`CreateTypeData`] trait. +/// +/// See the [crate-level documentation] for more information on type data and type registration. +/// +/// [`TypeRegistry`]: crate::TypeRegistry +/// [`TypeRegistration`]: crate::TypeRegistration +/// [crate-level documentation]: crate +pub trait TypeData: Downcast + Send + Sync { + fn clone_type_data(&self) -> Box; +} +impl_downcast!(TypeData); + +impl TypeData for T +where + T: Clone, +{ + fn clone_type_data(&self) -> Box { + Box::new(self.clone()) + } +} + +/// 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]. +/// +/// Note that in order to work with the `#[reflect(MyTrait)]` attribute, +/// implementors must be named with the `Reflect` prefix (e.g. `ReflectMyTrait`). +/// +/// # Input +/// +/// By default, this trait expects no input for creating the type data +/// (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. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::{CreateTypeData, Reflect}; +/// trait Combine { +/// fn combine(a: f32, b: f32) -> f32; +/// } +/// +/// #[derive(Clone)] +/// struct ReflectCombine { +/// multiplier: f32, +/// additional: f32, +/// combine: fn(f32, f32) -> f32, +/// } +/// +/// impl ReflectCombine { +/// pub fn combine(&self, a: f32, b: f32) -> f32 { +/// let combined = (self.combine)(a, b); +/// let multiplied = self.multiplier * combined; +/// multiplied + self.additional +/// } +/// } +/// +/// // A default implementation for when no input is given +/// impl CreateTypeData for ReflectCombine { +/// fn create_type_data(_: ()) -> Self { +/// Self { +/// multiplier: 1.0, +/// additional: 0.0, +/// combine: T::combine, +/// } +/// } +/// } +/// +/// // A custom implementation for when a multiplier is given +/// impl CreateTypeData for ReflectCombine { +/// fn create_type_data(input: (f32, f32)) -> Self { +/// Self { +/// multiplier: input.0, +/// additional: input.1, +/// combine: T::combine, +/// } +/// } +/// } +/// +/// #[derive(Reflect)] +/// // We can have the `Reflect` derive automatically register `ReflectCombine`: +/// #[reflect(Combine)] +/// struct WithoutMultiplier; +/// +/// impl Combine for WithoutMultiplier { +/// fn combine(a: f32, b: f32) -> f32 { +/// a + b +/// } +/// } +/// +/// #[derive(Reflect)] +/// // We can also given it some input: +/// #[reflect(Combine(2.0, 4.0))] +/// struct WithMultiplier; +/// +/// impl Combine for WithMultiplier { +/// fn combine(a: f32, b: f32) -> f32 { +/// a + b +/// } +/// } +/// +/// // Or we can simply create the data manually: +/// let without_multiplier = >::create_type_data(()); +/// let with_multiplier = >::create_type_data((2.0, 4.0)); +/// +/// assert_eq!(without_multiplier.combine(1.0, 2.0), 3.0); +/// assert_eq!(with_multiplier.combine(1.0, 2.0), 10.0); +/// ``` +/// +/// [`TypeRegistration`]: crate::TypeRegistration +/// [`TypeRegistry::register_type_data`]: crate::TypeRegistry::register_type_data +/// [`TypeRegistry::register_type_data_with`]: crate::TypeRegistry::register_type_data_with +/// [`Reflect` derive macro]: derive@crate::Reflect +pub trait CreateTypeData: TypeData { + /// Create this type data using the given input. + fn create_type_data(input: Input) -> Self; +} diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index cf80749edebbb..5858190db4e4f 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,4 +1,6 @@ -use crate::{serde::Serializable, FromReflect, Reflect, TypeInfo, TypePath, Typed}; +use crate::{ + serde::Serializable, CreateTypeData, FromReflect, Reflect, TypeData, TypeInfo, TypePath, Typed, +}; use alloc::{boxed::Box, string::String}; use bevy_platform_support::{ collections::{HashMap, HashSet}, @@ -11,7 +13,6 @@ use core::{ fmt::Debug, ops::{Deref, DerefMut}, }; -use downcast_rs::{impl_downcast, Downcast}; use serde::Deserialize; /// A registry of [reflected] types. @@ -290,7 +291,7 @@ impl TypeRegistry { /// /// Most of the time [`TypeRegistry::register`] can be used instead to register a type you derived [`Reflect`] for. /// However, in cases where you want to add a piece of type data that was not included in the list of `#[reflect(...)]` type data in the derive, - /// or where the type is generic and cannot register e.g. [`ReflectSerialize`] unconditionally without knowing the specific type parameters, + /// or where the type is generic and cannot register the type data unconditionally without knowing the specific type parameters, /// this method can be used to insert additional type data. /// /// # Example @@ -302,7 +303,7 @@ impl TypeRegistry { /// type_registry.register_type_data::, ReflectSerialize>(); /// type_registry.register_type_data::, ReflectDeserialize>(); /// ``` - pub fn register_type_data>(&mut self) { + 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", @@ -310,7 +311,31 @@ impl TypeRegistry { D = core::any::type_name::(), ) }); - data.insert(D::from_type()); + data.insert(D::create_type_data(())); + } + + /// Registers the type data `D` with parameter `P` for type `T`. + /// + /// Most of the time [`TypeRegistry::register`] can be used instead to register a type you derived [`Reflect`] for. + /// However, in cases where you want to add a piece of type data that was not included in the list of `#[reflect(...)]` type data in the derive, + /// or where the type is generic and cannot register the type data unconditionally without knowing the specific type parameters, + /// this method can be used to insert additional type data. + /// + /// If no parameters are needed for the type data or the type data does not accept parameters, + /// then [`TypeRegistry::register_type_data`] may be used instead. + pub fn register_type_data_with, P>( + &mut self, + params: P, + ) { + let data = 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", + T = T::type_path(), + D = ::core::any::type_name::(), + P = ::core::any::type_name::

(), + ) + }); + data.insert(D::create_type_data(params)); } pub fn contains(&self, type_id: TypeId) -> bool { @@ -494,13 +519,13 @@ impl TypeRegistryArc { /// # Example /// /// ``` -/// # use bevy_reflect::{TypeRegistration, std_traits::ReflectDefault, FromType}; +/// # use bevy_reflect::{TypeRegistration, std_traits::ReflectDefault, CreateTypeData}; /// let mut 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::(FromType::>::from_type()); +/// registration.insert::(CreateTypeData::>::create_type_data(())); /// assert!(registration.data::().is_some()) /// ``` /// @@ -672,50 +697,17 @@ impl Clone for TypeRegistration { } } -/// A trait used to type-erase 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`. -/// -/// See the [crate-level documentation] for more information on type data and type registration. -/// -/// [crate-level documentation]: crate -pub trait TypeData: Downcast + Send + Sync { - fn clone_type_data(&self) -> Box; -} -impl_downcast!(TypeData); - -impl TypeData for T -where - T: Clone, -{ - fn clone_type_data(&self) -> Box { - Box::new(self.clone()) - } -} - -/// Trait used to generate [`TypeData`] for trait reflection. -/// -/// This is used by the `#[derive(Reflect)]` macro to generate an implementation -/// of [`TypeData`] to pass to [`TypeRegistration::insert`]. -pub trait FromType { - fn from_type() -> Self; -} - /// A struct used to serialize reflected instances of a type. /// /// A `ReflectSerialize` for type `T` can be obtained via -/// [`FromType::from_type`]. +/// [`CreateTypeData::create_type_data`]. #[derive(Clone)] pub struct ReflectSerialize { get_serializable: fn(value: &dyn Reflect) -> Serializable, } -impl FromType for ReflectSerialize { - fn from_type() -> Self { +impl CreateTypeData for ReflectSerialize { + fn create_type_data(_input: ()) -> Self { ReflectSerialize { get_serializable: |value| { value @@ -743,7 +735,7 @@ impl ReflectSerialize { /// A struct used to deserialize reflected instances of a type. /// /// A `ReflectDeserialize` for type `T` can be obtained via -/// [`FromType::from_type`]. +/// [`CreateTypeData::create_type_data`]. #[derive(Clone)] pub struct ReflectDeserialize { pub func: fn( @@ -767,8 +759,8 @@ impl ReflectDeserialize { } } -impl Deserialize<'a> + Reflect> FromType for ReflectDeserialize { - fn from_type() -> Self { +impl Deserialize<'a> + Reflect> CreateTypeData for ReflectDeserialize { + fn create_type_data(_input: ()) -> Self { ReflectDeserialize { func: |deserializer| Ok(Box::new(T::deserialize(deserializer)?)), } @@ -871,8 +863,8 @@ impl ReflectFromPtr { unsafe_code, reason = "We must interact with pointers here, which are inherently unsafe." )] -impl FromType for ReflectFromPtr { - fn from_type() -> Self { +impl CreateTypeData for ReflectFromPtr { + fn create_type_data(_input: ()) -> Self { ReflectFromPtr { type_id: TypeId::of::(), from_ptr: |ptr| { diff --git a/crates/bevy_state/src/reflect.rs b/crates/bevy_state/src/reflect.rs index 0ff09c3759d21..8e5442991ceca 100644 --- a/crates/bevy_state/src/reflect.rs +++ b/crates/bevy_state/src/reflect.rs @@ -1,7 +1,7 @@ use crate::state::{FreelyMutableState, NextState, State, States}; use bevy_ecs::{reflect::from_reflect_with_fallback, world::World}; -use bevy_reflect::{FromType, Reflect, TypePath, TypeRegistry}; +use bevy_reflect::{CreateTypeData, Reflect, TypePath, TypeRegistry}; /// A struct used to operate on the reflected [`States`] trait of a type. /// @@ -19,12 +19,12 @@ pub struct ReflectStateFns { impl ReflectStateFns { /// Get the default set of [`ReflectStateFns`] for a specific component type using its - /// [`FromType`] implementation. + /// [`CreateTypeData`] implementation. /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. pub fn new() -> Self { - >::from_type().0 + >::create_type_data(()).0 } } @@ -35,8 +35,8 @@ impl ReflectState { } } -impl FromType for ReflectState { - fn from_type() -> Self { +impl CreateTypeData for ReflectState { + fn create_type_data(_input: ()) -> Self { ReflectState(ReflectStateFns { reflect: |world| { world @@ -63,12 +63,12 @@ pub struct ReflectFreelyMutableStateFns { impl ReflectFreelyMutableStateFns { /// Get the default set of [`ReflectFreelyMutableStateFns`] for a specific component type using its - /// [`FromType`] implementation. + /// [`CreateTypeData`] implementation. /// /// This is useful if you want to start with the default implementation before overriding some /// of the functions to create a custom implementation. pub fn new() -> Self { - >::from_type().0 + >::create_type_data(()).0 } } @@ -79,8 +79,8 @@ impl ReflectFreelyMutableState { } } -impl FromType for ReflectFreelyMutableState { - fn from_type() -> Self { +impl CreateTypeData for ReflectFreelyMutableState { + fn create_type_data(_input: ()) -> Self { ReflectFreelyMutableState(ReflectFreelyMutableStateFns { set_next_state: |world, reflected_state, registry| { let new_state: S = from_reflect_with_fallback( diff --git a/examples/reflection/type_data.rs b/examples/reflection/type_data.rs index 56dafa8522d6e..745c6e24006df 100644 --- a/examples/reflection/type_data.rs +++ b/examples/reflection/type_data.rs @@ -2,7 +2,7 @@ use bevy::{ prelude::*, - reflect::{FromType, TypeRegistry}, + reflect::{CreateTypeData, TypeRegistry}, }; // It's recommended to read this example from top to bottom. @@ -52,10 +52,10 @@ fn main() { damage: fn(&mut dyn Reflect, damage: Box), } - // Now, we can create a blanket implementation of the `FromType` trait to construct our type data + // Now, we can create a blanket implementation of the `CreateTypeData` trait to construct our type data // for any type that implements `Reflect` and `Damageable`. - impl> FromType for ReflectDamageable { - fn from_type() -> Self { + impl> CreateTypeData for ReflectDamageable { + fn create_type_data(_input: ()) -> Self { Self { damage: |reflect, damage| { // This requires that `reflect` is `T` and not a dynamic representation like `DynamicStruct`. From c8ef9b39e4939ac232ce512337018cf2c6a458b0 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 6 Jun 2024 14:11:38 -0700 Subject: [PATCH 2/3] Add compile-fail tests --- .../tests/reflect_derive/type_data_fail.rs | 21 +++++++++++ .../tests/reflect_derive/type_data_pass.rs | 35 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_fail.rs create mode 100644 crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_pass.rs 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 new file mode 100644 index 0000000000000..1d2155ade740c --- /dev/null +++ b/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_fail.rs @@ -0,0 +1,21 @@ +//@no-rustfix +use bevy_reflect::{CreateTypeData, Reflect}; + +#[derive(Clone)] +struct ReflectMyTrait; + +impl CreateTypeData for ReflectMyTrait { + fn create_type_data(_: f32) -> Self { + todo!() + } +} + +#[derive(Reflect)] +//~^ ERROR: mismatched types +#[reflect(MyTrait)] +struct RequiredArgs; + +#[derive(Reflect)] +#[reflect(MyTrait(123))] +//~^ ERROR: mismatched types +struct WrongArgs; 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 new file mode 100644 index 0000000000000..db2c8c67e3f53 --- /dev/null +++ b/crates/bevy_reflect/compile_fail/tests/reflect_derive/type_data_pass.rs @@ -0,0 +1,35 @@ +//@check-pass +use bevy_reflect::{CreateTypeData, Reflect}; + +#[derive(Clone)] +struct ReflectMyTrait; + +impl CreateTypeData for ReflectMyTrait { + fn create_type_data(_: ()) -> Self { + todo!() + } +} + +impl CreateTypeData for ReflectMyTrait { + fn create_type_data(_: i32) -> Self { + todo!() + } +} + +impl CreateTypeData for ReflectMyTrait { + fn create_type_data(_: (i32, i32)) -> Self { + todo!() + } +} + +#[derive(Reflect)] +#[reflect(MyTrait)] +struct NoArgs; + +#[derive(Reflect)] +#[reflect(MyTrait(1 + 2))] +struct OneArg; + +#[derive(Reflect)] +#[reflect(MyTrait(1 + 2, 3 + 4))] +struct TwoArgs; From d4466a16003e5ac397304df192c80b7fc6033c99 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 6 Jun 2024 14:17:41 -0700 Subject: [PATCH 3/3] Set span for empty type data parameters in macro --- .../tests/reflect_derive/type_data_fail.rs | 2 +- crates/bevy_reflect/derive/src/registration.rs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) 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 1d2155ade740c..dcad3bc56a84f 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 @@ -11,8 +11,8 @@ impl CreateTypeData for ReflectMyTrait { } #[derive(Reflect)] -//~^ ERROR: mismatched types #[reflect(MyTrait)] +//~^ ERROR: mismatched types struct RequiredArgs; #[derive(Reflect)] diff --git a/crates/bevy_reflect/derive/src/registration.rs b/crates/bevy_reflect/derive/src/registration.rs index 00b5eee634245..c6f606e05c4cc 100644 --- a/crates/bevy_reflect/derive/src/registration.rs +++ b/crates/bevy_reflect/derive/src/registration.rs @@ -4,7 +4,7 @@ use crate::{ derive_data::ReflectMeta, serialization::SerializationDataDef, where_clause_options::WhereClauseOptions, }; -use quote::quote; +use quote::{quote, quote_spanned}; use syn::Type; /// Creates the `GetTypeRegistration` impl for the given type data. @@ -50,8 +50,16 @@ pub(crate) fn impl_get_type_registration<'a>( let reflect_ident = data.reflect_ident(); let args = data.args(); + let args = if args.is_empty() { + // Set the span so that we get pointed to the correct identifier + let span = reflect_ident.span(); + quote_spanned!(span => ()) + } else { + quote!((#args)) + }; + quote! { - <#reflect_ident as #bevy_reflect_path::CreateTypeData>::create_type_data((#args)) + <#reflect_ident as #bevy_reflect_path::CreateTypeData>::create_type_data(#args) } });