mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
bevy_reflect: Recursive registration (#5781)
# Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
d90cb57c2e
commit
ccb9d0500f
12 changed files with 505 additions and 129 deletions
|
@ -470,7 +470,12 @@ impl<'a> ReflectMeta<'a> {
|
|||
&self,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
) -> proc_macro2::TokenStream {
|
||||
crate::registration::impl_get_type_registration(self, where_clause_options, None)
|
||||
crate::registration::impl_get_type_registration(
|
||||
self,
|
||||
where_clause_options,
|
||||
None,
|
||||
Option::<std::iter::Empty<&Type>>::None,
|
||||
)
|
||||
}
|
||||
|
||||
/// The collection of docstrings for this type, if any.
|
||||
|
@ -502,6 +507,7 @@ impl<'a> ReflectStruct<'a> {
|
|||
self.meta(),
|
||||
where_clause_options,
|
||||
self.serialization_data(),
|
||||
Some(self.active_types().iter()),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -512,22 +518,21 @@ impl<'a> ReflectStruct<'a> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are exposed to the reflection API
|
||||
/// Get an iterator of fields which are exposed to the reflection API.
|
||||
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields
|
||||
self.fields()
|
||||
.iter()
|
||||
.filter(|field| field.attrs.ignore.is_active())
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are ignored by the reflection API
|
||||
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields
|
||||
self.fields()
|
||||
.iter()
|
||||
.filter(|field| field.attrs.ignore.is_ignored())
|
||||
}
|
||||
|
||||
/// The complete set of fields in this struct.
|
||||
#[allow(dead_code)]
|
||||
pub fn fields(&self) -> &[StructField<'a>] {
|
||||
&self.fields
|
||||
}
|
||||
|
@ -573,6 +578,21 @@ impl<'a> ReflectEnum<'a> {
|
|||
pub fn where_clause_options(&self) -> WhereClauseOptions {
|
||||
WhereClauseOptions::new_with_fields(self.meta(), self.active_types().into_boxed_slice())
|
||||
}
|
||||
|
||||
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
|
||||
///
|
||||
/// Returns a specific implementation for enums and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method
|
||||
pub fn get_type_registration(
|
||||
&self,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
) -> proc_macro2::TokenStream {
|
||||
crate::registration::impl_get_type_registration(
|
||||
self.meta(),
|
||||
where_clause_options,
|
||||
None,
|
||||
Some(self.active_fields().map(|field| &field.data.ty)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EnumVariant<'a> {
|
||||
|
|
|
@ -84,9 +84,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
|
|||
|
||||
let type_path_impl = impl_type_path(reflect_enum.meta());
|
||||
|
||||
let get_type_registration_impl = reflect_enum
|
||||
.meta()
|
||||
.get_type_registration(&where_clause_options);
|
||||
let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options);
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) =
|
||||
reflect_enum.meta().type_path().generics().split_for_impl();
|
||||
|
|
|
@ -4,17 +4,29 @@ use crate::derive_data::ReflectMeta;
|
|||
use crate::serialization::SerializationDataDef;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use quote::quote;
|
||||
use syn::Type;
|
||||
|
||||
/// Creates the `GetTypeRegistration` impl for the given type data.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn impl_get_type_registration(
|
||||
pub(crate) fn impl_get_type_registration<'a>(
|
||||
meta: &ReflectMeta,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
serialization_data: Option<&SerializationDataDef>,
|
||||
type_dependencies: Option<impl Iterator<Item = &'a Type>>,
|
||||
) -> 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! {
|
||||
#[inline(never)]
|
||||
fn register_type_dependencies(registry: &mut #bevy_reflect_path::TypeRegistry) {
|
||||
#(<#deps as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register(registry);)*
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
|
||||
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
|
||||
|
||||
|
@ -44,6 +56,8 @@ pub(crate) fn impl_get_type_registration(
|
|||
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<Self>::from_type());)*
|
||||
registration
|
||||
}
|
||||
|
||||
#type_deps_fn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,11 +217,15 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
|||
|
||||
// `TypePath` is always required for active fields since they are used to
|
||||
// construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
|
||||
Some(
|
||||
self.active_fields
|
||||
.iter()
|
||||
.map(move |ty| quote!(#ty : #reflect_bound + #bevy_reflect_path::TypePath)),
|
||||
)
|
||||
// Likewise, `GetTypeRegistration` is always required for active fields since
|
||||
// they are used to register the type's dependencies.
|
||||
Some(self.active_fields.iter().map(move |ty| {
|
||||
quote!(
|
||||
#ty : #reflect_bound
|
||||
+ #bevy_reflect_path::TypePath
|
||||
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::std_traits::ReflectDefault;
|
||||
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, ReflectOwned};
|
||||
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, ReflectOwned, TypeRegistry};
|
||||
use crate::{
|
||||
impl_type_path, map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum,
|
||||
DynamicMap, Enum, EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo,
|
||||
|
@ -221,7 +221,7 @@ impl_reflect_value!(::std::ffi::OsString(Debug, Hash, PartialEq));
|
|||
|
||||
macro_rules! impl_reflect_for_veclike {
|
||||
($ty:path, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => {
|
||||
impl<T: FromReflect + TypePath> List for $ty {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> List for $ty {
|
||||
#[inline]
|
||||
fn get(&self, index: usize) -> Option<&dyn Reflect> {
|
||||
<$sub>::get(self, index).map(|value| value as &dyn Reflect)
|
||||
|
@ -280,7 +280,7 @@ macro_rules! impl_reflect_for_veclike {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> Reflect for $ty {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> Reflect for $ty {
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
Some(<Self as Typed>::type_info())
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ macro_rules! impl_reflect_for_veclike {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> Typed for $ty {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> Typed for $ty {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::List(ListInfo::new::<Self, T>()))
|
||||
|
@ -356,15 +356,19 @@ macro_rules! impl_reflect_for_veclike {
|
|||
|
||||
impl_type_path!($ty);
|
||||
|
||||
impl<T: FromReflect + TypePath> GetTypeRegistration for $ty {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> GetTypeRegistration for $ty {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
let mut registration = TypeRegistration::of::<$ty>();
|
||||
registration.insert::<ReflectFromPtr>(FromType::<$ty>::from_type());
|
||||
registration
|
||||
}
|
||||
|
||||
fn register_type_dependencies(registry: &mut TypeRegistry) {
|
||||
registry.register::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> FromReflect for $ty {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> FromReflect for $ty {
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
|
||||
let mut new_list = Self::with_capacity(ref_list.len());
|
||||
|
@ -401,8 +405,8 @@ macro_rules! impl_reflect_for_hashmap {
|
|||
($ty:path) => {
|
||||
impl<K, V, S> Map for $ty
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Hash,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
S: TypePath + BuildHasher + Send + Sync,
|
||||
{
|
||||
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
|
||||
|
@ -498,8 +502,8 @@ macro_rules! impl_reflect_for_hashmap {
|
|||
|
||||
impl<K, V, S> Reflect for $ty
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Hash,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
S: TypePath + BuildHasher + Send + Sync,
|
||||
{
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
|
@ -567,8 +571,8 @@ macro_rules! impl_reflect_for_hashmap {
|
|||
|
||||
impl<K, V, S> Typed for $ty
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Hash,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
S: TypePath + BuildHasher + Send + Sync,
|
||||
{
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
|
@ -579,8 +583,8 @@ macro_rules! impl_reflect_for_hashmap {
|
|||
|
||||
impl<K, V, S> GetTypeRegistration for $ty
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Hash,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
S: TypePath + BuildHasher + Send + Sync,
|
||||
{
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
|
@ -588,12 +592,17 @@ macro_rules! impl_reflect_for_hashmap {
|
|||
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
|
||||
registration
|
||||
}
|
||||
|
||||
fn register_type_dependencies(registry: &mut TypeRegistry) {
|
||||
registry.register::<K>();
|
||||
registry.register::<V>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> FromReflect for $ty
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Hash,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
S: TypePath + BuildHasher + Default + Send + Sync,
|
||||
{
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
|
@ -624,8 +633,8 @@ impl_type_path!(::bevy_utils::hashbrown::HashMap<K, V, S>);
|
|||
|
||||
impl<K, V> Map for ::std::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Ord,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
{
|
||||
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
|
||||
key.downcast_ref::<K>()
|
||||
|
@ -720,8 +729,8 @@ where
|
|||
|
||||
impl<K, V> Reflect for ::std::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Ord,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
{
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
Some(<Self as Typed>::type_info())
|
||||
|
@ -788,8 +797,8 @@ where
|
|||
|
||||
impl<K, V> Typed for ::std::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Ord,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
{
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
|
@ -799,8 +808,8 @@ where
|
|||
|
||||
impl<K, V> GetTypeRegistration for ::std::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Ord,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
{
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
let mut registration = TypeRegistration::of::<Self>();
|
||||
|
@ -811,8 +820,8 @@ where
|
|||
|
||||
impl<K, V> FromReflect for ::std::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: FromReflect + TypePath + Eq + Ord,
|
||||
V: FromReflect + TypePath,
|
||||
K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord,
|
||||
V: FromReflect + TypePath + GetTypeRegistration,
|
||||
{
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
if let ReflectRef::Map(ref_map) = reflect.reflect_ref() {
|
||||
|
@ -831,7 +840,7 @@ where
|
|||
|
||||
impl_type_path!(::std::collections::BTreeMap<K, V>);
|
||||
|
||||
impl<T: Reflect + TypePath, const N: usize> Array for [T; N] {
|
||||
impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> Array for [T; N] {
|
||||
#[inline]
|
||||
fn get(&self, index: usize) -> Option<&dyn Reflect> {
|
||||
<[T]>::get(self, index).map(|value| value as &dyn Reflect)
|
||||
|
@ -860,7 +869,7 @@ impl<T: Reflect + TypePath, const N: usize> Array for [T; N] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + TypePath, const N: usize> Reflect for [T; N] {
|
||||
impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> Reflect for [T; N] {
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
Some(<Self as Typed>::type_info())
|
||||
}
|
||||
|
@ -942,7 +951,7 @@ impl<T: Reflect + TypePath, const N: usize> Reflect for [T; N] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath, const N: usize> FromReflect for [T; N] {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration, const N: usize> FromReflect for [T; N] {
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
if let ReflectRef::Array(ref_array) = reflect.reflect_ref() {
|
||||
let mut temp_vec = Vec::with_capacity(ref_array.len());
|
||||
|
@ -956,7 +965,7 @@ impl<T: FromReflect + TypePath, const N: usize> FromReflect for [T; N] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + TypePath, const N: usize> Typed for [T; N] {
|
||||
impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> Typed for [T; N] {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::Array(ArrayInfo::new::<Self, T>(N)))
|
||||
|
@ -975,37 +984,27 @@ impl<T: TypePath, const N: usize> TypePath for [T; N] {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// `FromType::from_type` requires `Deserialize<'de>` to be implemented for `T`.
|
||||
// Currently serde only supports `Deserialize<'de>` for arrays up to size 32.
|
||||
// This can be changed to use const generics once serde utilizes const generics for arrays.
|
||||
// Tracking issue: https://github.com/serde-rs/serde/issues/1937
|
||||
macro_rules! impl_array_get_type_registration {
|
||||
($($N:expr)+) => {
|
||||
$(
|
||||
impl<T: Reflect + TypePath> GetTypeRegistration for [T; $N] {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
TypeRegistration::of::<[T; $N]>()
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_array_get_type_registration! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> GetTypeRegistration for Option<T> {
|
||||
impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> GetTypeRegistration for [T; N] {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
TypeRegistration::of::<Option<T>>()
|
||||
TypeRegistration::of::<[T; N]>()
|
||||
}
|
||||
|
||||
fn register_type_dependencies(registry: &mut TypeRegistry) {
|
||||
registry.register::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> Enum for Option<T> {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> GetTypeRegistration for Option<T> {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
TypeRegistration::of::<Option<T>>()
|
||||
}
|
||||
|
||||
fn register_type_dependencies(registry: &mut TypeRegistry) {
|
||||
registry.register::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> Enum for Option<T> {
|
||||
fn field(&self, _name: &str) -> Option<&dyn Reflect> {
|
||||
None
|
||||
}
|
||||
|
@ -1076,7 +1075,7 @@ impl<T: FromReflect + TypePath> Enum for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> Reflect for Option<T> {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> Reflect for Option<T> {
|
||||
#[inline]
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
Some(<Self as Typed>::type_info())
|
||||
|
@ -1189,7 +1188,7 @@ impl<T: FromReflect + TypePath> Reflect for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> FromReflect for Option<T> {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> FromReflect for Option<T> {
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() {
|
||||
match dyn_enum.variant_name() {
|
||||
|
@ -1227,7 +1226,7 @@ impl<T: FromReflect + TypePath> FromReflect for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + TypePath> Typed for Option<T> {
|
||||
impl<T: FromReflect + TypePath + GetTypeRegistration> Typed for Option<T> {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
|
@ -1392,7 +1391,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + Clone + TypePath> List for Cow<'static, [T]> {
|
||||
impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> List for Cow<'static, [T]> {
|
||||
fn get(&self, index: usize) -> Option<&dyn Reflect> {
|
||||
self.as_ref().get(index).map(|x| x as &dyn Reflect)
|
||||
}
|
||||
|
@ -1451,7 +1450,7 @@ impl<T: FromReflect + Clone + TypePath> List for Cow<'static, [T]> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + Clone + TypePath> Reflect for Cow<'static, [T]> {
|
||||
impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> Reflect for Cow<'static, [T]> {
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
Some(<Self as Typed>::type_info())
|
||||
}
|
||||
|
@ -1518,20 +1517,26 @@ impl<T: FromReflect + Clone + TypePath> Reflect for Cow<'static, [T]> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + Clone + TypePath> Typed for Cow<'static, [T]> {
|
||||
impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> Typed for Cow<'static, [T]> {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::List(ListInfo::new::<Self, T>()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + Clone + TypePath> GetTypeRegistration for Cow<'static, [T]> {
|
||||
impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> GetTypeRegistration
|
||||
for Cow<'static, [T]>
|
||||
{
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
TypeRegistration::of::<Cow<'static, [T]>>()
|
||||
}
|
||||
|
||||
fn register_type_dependencies(registry: &mut TypeRegistry) {
|
||||
registry.register::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromReflect + Clone + TypePath> FromReflect for Cow<'static, [T]> {
|
||||
impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> FromReflect for Cow<'static, [T]> {
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
|
||||
let mut temp_vec = Vec::with_capacity(ref_list.len());
|
||||
|
|
|
@ -481,9 +481,11 @@ mod tuple_struct;
|
|||
mod type_info;
|
||||
mod type_path;
|
||||
mod type_registry;
|
||||
|
||||
mod impls {
|
||||
#[cfg(feature = "glam")]
|
||||
mod glam;
|
||||
|
||||
#[cfg(feature = "bevy_math")]
|
||||
mod math {
|
||||
mod direction;
|
||||
|
@ -491,6 +493,7 @@ mod impls {
|
|||
mod primitives3d;
|
||||
mod rect;
|
||||
}
|
||||
|
||||
#[cfg(feature = "smallvec")]
|
||||
mod smallvec;
|
||||
#[cfg(feature = "smol_str")]
|
||||
|
@ -535,9 +538,48 @@ pub use erased_serde;
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
/// Exports used by the reflection macros.
|
||||
///
|
||||
/// These are not meant to be used directly and are subject to breaking changes.
|
||||
#[doc(hidden)]
|
||||
pub mod __macro_exports {
|
||||
pub use bevy_utils::uuid::generate_composite_uuid;
|
||||
use crate::{
|
||||
DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
|
||||
DynamicTupleStruct, GetTypeRegistration, TypeRegistry,
|
||||
};
|
||||
|
||||
/// A wrapper trait around [`GetTypeRegistration`].
|
||||
///
|
||||
/// This trait is used by the derive macro to recursively register all type dependencies.
|
||||
/// It's used instead of `GetTypeRegistration` directly to avoid making dynamic types also
|
||||
/// implement `GetTypeRegistration` in order to be used as active fields.
|
||||
///
|
||||
/// This trait has a blanket implementation for all types that implement `GetTypeRegistration`
|
||||
/// and manual implementations for all dynamic types (which simply do nothing).
|
||||
pub trait RegisterForReflection {
|
||||
#[allow(unused_variables)]
|
||||
fn __register(registry: &mut TypeRegistry) {}
|
||||
}
|
||||
|
||||
impl<T: GetTypeRegistration> RegisterForReflection for T {
|
||||
fn __register(registry: &mut TypeRegistry) {
|
||||
registry.register::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterForReflection for DynamicEnum {}
|
||||
|
||||
impl RegisterForReflection for DynamicTupleStruct {}
|
||||
|
||||
impl RegisterForReflection for DynamicStruct {}
|
||||
|
||||
impl RegisterForReflection for DynamicMap {}
|
||||
|
||||
impl RegisterForReflection for DynamicList {}
|
||||
|
||||
impl RegisterForReflection for DynamicArray {}
|
||||
|
||||
impl RegisterForReflection for DynamicTuple {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1002,6 +1044,124 @@ mod tests {
|
|||
assert_eq!(new_foo, expected_new_foo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_auto_register_fields() {
|
||||
#[derive(Reflect)]
|
||||
struct Foo {
|
||||
bar: Bar,
|
||||
}
|
||||
|
||||
#[derive(Reflect)]
|
||||
enum Bar {
|
||||
Variant(Baz),
|
||||
}
|
||||
|
||||
#[derive(Reflect)]
|
||||
struct Baz(usize);
|
||||
|
||||
// === Basic === //
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<Foo>();
|
||||
|
||||
assert!(
|
||||
registry.contains(TypeId::of::<Bar>()),
|
||||
"registry should contain auto-registered `Bar` from `Foo`"
|
||||
);
|
||||
|
||||
// === Option === //
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<Option<Foo>>();
|
||||
|
||||
assert!(
|
||||
registry.contains(TypeId::of::<Bar>()),
|
||||
"registry should contain auto-registered `Bar` from `Option<Foo>`"
|
||||
);
|
||||
|
||||
// === Tuple === //
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<(Foo, Foo)>();
|
||||
|
||||
assert!(
|
||||
registry.contains(TypeId::of::<Bar>()),
|
||||
"registry should contain auto-registered `Bar` from `(Foo, Foo)`"
|
||||
);
|
||||
|
||||
// === Array === //
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<[Foo; 3]>();
|
||||
|
||||
assert!(
|
||||
registry.contains(TypeId::of::<Bar>()),
|
||||
"registry should contain auto-registered `Bar` from `[Foo; 3]`"
|
||||
);
|
||||
|
||||
// === Vec === //
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<Vec<Foo>>();
|
||||
|
||||
assert!(
|
||||
registry.contains(TypeId::of::<Bar>()),
|
||||
"registry should contain auto-registered `Bar` from `Vec<Foo>`"
|
||||
);
|
||||
|
||||
// === HashMap === //
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<HashMap<i32, Foo>>();
|
||||
|
||||
assert!(
|
||||
registry.contains(TypeId::of::<Bar>()),
|
||||
"registry should contain auto-registered `Bar` from `HashMap<i32, Foo>`"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_dynamic_fields() {
|
||||
#[derive(Reflect)]
|
||||
#[reflect(from_reflect = false)]
|
||||
struct MyStruct(
|
||||
DynamicEnum,
|
||||
DynamicTupleStruct,
|
||||
DynamicStruct,
|
||||
DynamicMap,
|
||||
DynamicList,
|
||||
DynamicArray,
|
||||
DynamicTuple,
|
||||
i32,
|
||||
);
|
||||
|
||||
assert_impl_all!(MyStruct: Reflect, GetTypeRegistration);
|
||||
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<MyStruct>();
|
||||
|
||||
assert_eq!(2, registry.iter().count());
|
||||
assert!(registry.contains(TypeId::of::<MyStruct>()));
|
||||
assert!(registry.contains(TypeId::of::<i32>()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_auto_register_existing_types() {
|
||||
#[derive(Reflect)]
|
||||
struct Foo {
|
||||
bar: Bar,
|
||||
}
|
||||
|
||||
#[derive(Reflect, Default)]
|
||||
struct Bar(usize);
|
||||
|
||||
let mut registry = TypeRegistry::empty();
|
||||
registry.register::<Bar>();
|
||||
registry.register_type_data::<Bar, ReflectDefault>();
|
||||
registry.register::<Foo>();
|
||||
|
||||
assert!(
|
||||
registry
|
||||
.get_type_data::<ReflectDefault>(TypeId::of::<Bar>())
|
||||
.is_some(),
|
||||
"registry should contain existing registration for `Bar`"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reflect_serialize() {
|
||||
#[derive(Reflect)]
|
||||
|
@ -1981,6 +2141,39 @@ bevy_reflect::tests::Test {
|
|||
let _ = <RecurseB as TypePath>::type_path();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_registration_does_not_hang() {
|
||||
#[derive(Reflect)]
|
||||
struct Recurse<T>(T);
|
||||
|
||||
let mut registry = TypeRegistry::empty();
|
||||
|
||||
registry.register::<Recurse<Recurse<()>>>();
|
||||
|
||||
#[derive(Reflect)]
|
||||
#[reflect(no_field_bounds)]
|
||||
struct SelfRecurse {
|
||||
recurse: Vec<SelfRecurse>,
|
||||
}
|
||||
|
||||
registry.register::<SelfRecurse>();
|
||||
|
||||
#[derive(Reflect)]
|
||||
#[reflect(no_field_bounds)]
|
||||
enum RecurseA {
|
||||
Recurse(RecurseB),
|
||||
}
|
||||
|
||||
#[derive(Reflect)]
|
||||
struct RecurseB {
|
||||
vector: Vec<RecurseA>,
|
||||
}
|
||||
|
||||
registry.register::<RecurseA>();
|
||||
assert!(registry.contains(TypeId::of::<RecurseA>()));
|
||||
assert!(registry.contains(TypeId::of::<RecurseB>()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_opt_out_type_path() {
|
||||
#[derive(Reflect)]
|
||||
|
|
|
@ -3,8 +3,8 @@ use bevy_utils::all_tuples;
|
|||
|
||||
use crate::{
|
||||
self as bevy_reflect, utility::GenericTypePathCell, FromReflect, GetTypeRegistration, Reflect,
|
||||
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypeRegistration, Typed,
|
||||
UnnamedField,
|
||||
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypeRegistration, TypeRegistry,
|
||||
Typed, UnnamedField,
|
||||
};
|
||||
use crate::{ReflectKind, TypePathTable};
|
||||
use std::any::{Any, TypeId};
|
||||
|
@ -461,7 +461,7 @@ pub fn tuple_debug(dyn_tuple: &dyn Tuple, f: &mut Formatter<'_>) -> std::fmt::Re
|
|||
|
||||
macro_rules! impl_reflect_tuple {
|
||||
{$($index:tt : $name:tt),*} => {
|
||||
impl<$($name: Reflect + TypePath),*> Tuple for ($($name,)*) {
|
||||
impl<$($name: Reflect + TypePath + GetTypeRegistration),*> Tuple for ($($name,)*) {
|
||||
#[inline]
|
||||
fn field(&self, index: usize) -> Option<&dyn Reflect> {
|
||||
match index {
|
||||
|
@ -512,7 +512,7 @@ macro_rules! impl_reflect_tuple {
|
|||
}
|
||||
}
|
||||
|
||||
impl<$($name: Reflect + TypePath),*> Reflect for ($($name,)*) {
|
||||
impl<$($name: Reflect + TypePath + GetTypeRegistration),*> Reflect for ($($name,)*) {
|
||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||
Some(<Self as Typed>::type_info())
|
||||
}
|
||||
|
@ -575,7 +575,7 @@ macro_rules! impl_reflect_tuple {
|
|||
}
|
||||
}
|
||||
|
||||
impl <$($name: Reflect + TypePath),*> Typed for ($($name,)*) {
|
||||
impl <$($name: Reflect + TypePath + GetTypeRegistration),*> Typed for ($($name,)*) {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: $crate::utility::GenericTypeInfoCell = $crate::utility::GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
|
@ -588,14 +588,17 @@ macro_rules! impl_reflect_tuple {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl<$($name: Reflect + TypePath),*> GetTypeRegistration for ($($name,)*) {
|
||||
impl<$($name: Reflect + TypePath + GetTypeRegistration),*> GetTypeRegistration for ($($name,)*) {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
TypeRegistration::of::<($($name,)*)>()
|
||||
}
|
||||
|
||||
fn register_type_dependencies(_registry: &mut TypeRegistry) {
|
||||
$(_registry.register::<$name>();)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($name: FromReflect + TypePath),*> FromReflect for ($($name,)*)
|
||||
impl<$($name: FromReflect + TypePath + GetTypeRegistration),*> FromReflect for ($($name,)*)
|
||||
{
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() {
|
||||
|
|
|
@ -56,8 +56,15 @@ impl Debug for TypeRegistryArc {
|
|||
/// See the [crate-level documentation] for more information on type registration.
|
||||
///
|
||||
/// [crate-level documentation]: crate
|
||||
pub trait GetTypeRegistration {
|
||||
pub trait GetTypeRegistration: 'static {
|
||||
/// Returns the default [`TypeRegistration`] for this type.
|
||||
fn get_type_registration() -> TypeRegistration;
|
||||
/// Registers other types needed by this type.
|
||||
///
|
||||
/// This method is called by [`TypeRegistry::register`] to register any other required types.
|
||||
/// Often, this is done for fields of structs and enum variants to ensure all types are properly registered.
|
||||
#[allow(unused_variables)]
|
||||
fn register_type_dependencies(registry: &mut TypeRegistry) {}
|
||||
}
|
||||
|
||||
impl Default for TypeRegistry {
|
||||
|
@ -100,41 +107,134 @@ impl TypeRegistry {
|
|||
registry
|
||||
}
|
||||
|
||||
/// Registers the type `T`, adding reflect data as specified in the [`Reflect`] derive:
|
||||
/// ```ignore (Neither bevy_ecs nor serde "derive" are available.)
|
||||
/// #[derive(Component, serde::Serialize, serde::Deserialize, Reflect)]
|
||||
/// #[reflect(Component, Serialize, Deserialize)] // will register ReflectComponent, ReflectSerialize, ReflectDeserialize
|
||||
/// Attempts to register the type `T` if it has not yet been registered already.
|
||||
///
|
||||
/// This will also recursively register any type dependencies as specified by [`GetTypeRegistration::register_type_dependencies`].
|
||||
/// When deriving `Reflect`, this will generally be all the fields of the struct or enum variant.
|
||||
/// As with any type registration, these type dependencies will not be registered more than once.
|
||||
///
|
||||
/// If the registration for type `T` already exists, it will not be registered again and neither will its type dependencies.
|
||||
/// To register the type, overwriting any existing registration, use [register](Self::overwrite_registration) instead.
|
||||
///
|
||||
/// Additionally, this will add any reflect [type data](TypeData) as specified in the [`Reflect`] derive.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use std::any::TypeId;
|
||||
/// # use bevy_reflect::{Reflect, TypeRegistry, std_traits::ReflectDefault};
|
||||
/// #[derive(Reflect, Default)]
|
||||
/// #[reflect(Default)]
|
||||
/// struct Foo {
|
||||
/// name: Option<String>,
|
||||
/// value: i32
|
||||
/// }
|
||||
///
|
||||
/// let mut type_registry = TypeRegistry::default();
|
||||
///
|
||||
/// type_registry.register::<Foo>();
|
||||
///
|
||||
/// // The main type
|
||||
/// assert!(type_registry.contains(TypeId::of::<Foo>()));
|
||||
///
|
||||
/// // Its type dependencies
|
||||
/// assert!(type_registry.contains(TypeId::of::<Option<String>>()));
|
||||
/// assert!(type_registry.contains(TypeId::of::<i32>()));
|
||||
///
|
||||
/// // Its type data
|
||||
/// assert!(type_registry.get_type_data::<ReflectDefault>(TypeId::of::<Foo>()).is_some());
|
||||
/// ```
|
||||
pub fn register<T>(&mut self)
|
||||
where
|
||||
T: GetTypeRegistration,
|
||||
{
|
||||
self.add_registration(T::get_type_registration());
|
||||
if self.register_internal(TypeId::of::<T>(), T::get_type_registration) {
|
||||
T::register_type_dependencies(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to register the type described by `registration`.
|
||||
///
|
||||
/// If the registration for the type already exists, it will not be registered again.
|
||||
///
|
||||
/// To forcibly register the type, overwriting any existing registration, use the
|
||||
/// [`overwrite_registration`](Self::overwrite_registration) method instead.
|
||||
///
|
||||
/// This method will _not_ register type dependencies.
|
||||
/// Use [`register`](Self::register) to register a type with its dependencies.
|
||||
///
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Registers the type described by `registration`.
|
||||
pub fn add_registration(&mut self, registration: TypeRegistration) {
|
||||
if self.registrations.contains_key(®istration.type_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let short_name = registration.type_info().type_path_table().short_path();
|
||||
if self.short_path_to_id.contains_key(short_name)
|
||||
|| self.ambiguous_names.contains(short_name)
|
||||
{
|
||||
// name is ambiguous. fall back to long names for all ambiguous types
|
||||
self.short_path_to_id.remove(short_name);
|
||||
self.ambiguous_names.insert(short_name);
|
||||
} else {
|
||||
self.short_path_to_id
|
||||
.insert(short_name, registration.type_id());
|
||||
}
|
||||
self.type_path_to_id
|
||||
.insert(registration.type_info().type_path(), registration.type_id());
|
||||
///
|
||||
/// If the registration for the type already exists, it will be overwritten.
|
||||
///
|
||||
/// To avoid overwriting existing registrations, it's recommended to use the
|
||||
/// [`register`](Self::register) or [`add_registration`](Self::add_registration) methods instead.
|
||||
///
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// Internal method to register a type with a given [`TypeId`] and [`TypeRegistration`].
|
||||
///
|
||||
/// By using this method, we are able to reduce the number of `TypeId` hashes and lookups needed
|
||||
/// to register a type.
|
||||
///
|
||||
/// This method is internal to prevent users from accidentally registering a type with a `TypeId`
|
||||
/// that does not match the type in the `TypeRegistration`.
|
||||
fn register_internal(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
get_registration: impl FnOnce() -> TypeRegistration,
|
||||
) -> bool {
|
||||
match self.registrations.entry(type_id) {
|
||||
bevy_utils::hashbrown::hash_map::Entry::Occupied(_) => false,
|
||||
bevy_utils::hashbrown::hash_map::Entry::Vacant(entry) => {
|
||||
let registration = get_registration();
|
||||
Self::update_registration_indices(
|
||||
®istration,
|
||||
&mut self.short_path_to_id,
|
||||
&mut self.type_path_to_id,
|
||||
&mut self.ambiguous_names,
|
||||
);
|
||||
entry.insert(registration);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal method to register additional lookups for a given [`TypeRegistration`].
|
||||
fn update_registration_indices(
|
||||
registration: &TypeRegistration,
|
||||
short_path_to_id: &mut HashMap<&'static str, TypeId>,
|
||||
type_path_to_id: &mut HashMap<&'static str, TypeId>,
|
||||
ambiguous_names: &mut HashSet<&'static str>,
|
||||
) {
|
||||
let short_name = registration.type_info().type_path_table().short_path();
|
||||
if short_path_to_id.contains_key(short_name) || ambiguous_names.contains(short_name) {
|
||||
// name is ambiguous. fall back to long names for all ambiguous types
|
||||
short_path_to_id.remove(short_name);
|
||||
ambiguous_names.insert(short_name);
|
||||
} else {
|
||||
short_path_to_id.insert(short_name, registration.type_id());
|
||||
}
|
||||
type_path_to_id.insert(registration.type_info().type_path(), registration.type_id());
|
||||
}
|
||||
|
||||
/// Registers the type data `D` for type `T`.
|
||||
///
|
||||
/// Most of the time [`TypeRegistry::register`] can be used instead to register a type you derived [`Reflect`] for.
|
||||
|
@ -162,6 +262,10 @@ impl TypeRegistry {
|
|||
data.insert(D::from_type());
|
||||
}
|
||||
|
||||
pub fn contains(&self, type_id: TypeId) -> bool {
|
||||
self.registrations.contains_key(&type_id)
|
||||
}
|
||||
|
||||
/// Returns a reference to the [`TypeRegistration`] of the type with the
|
||||
/// given [`TypeId`].
|
||||
///
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use bevy_reflect::{Reflect, TypePath};
|
||||
use bevy_reflect::{GetField, Reflect, Struct, TypePath};
|
||||
|
||||
#[derive(Reflect)]
|
||||
#[reflect(from_reflect = false)]
|
||||
|
@ -11,7 +11,7 @@ struct Foo<T> {
|
|||
struct NoReflect(f32);
|
||||
|
||||
fn main() {
|
||||
let mut foo: Box<dyn Reflect> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
|
||||
let mut foo: Box<dyn Struct> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
|
||||
// foo doesn't implement Reflect because NoReflect doesn't implement Reflect
|
||||
foo.get_field::<NoReflect>("a").unwrap();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,34 @@
|
|||
error[E0599]: no method named `get_field` found for struct `Box<(dyn Reflect + 'static)>` in the current scope
|
||||
--> tests/reflect_derive/generics.fail.rs:16:9
|
||||
|
|
||||
16 | foo.get_field::<NoReflect>("a").unwrap();
|
||||
| ^^^^^^^^^ method not found in `Box<dyn Reflect>`
|
||||
|
||||
error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied
|
||||
--> tests/reflect_derive/generics.fail.rs:14:37
|
||||
--> tests/reflect_derive/generics.fail.rs:16:21
|
||||
|
|
||||
16 | foo.get_field::<NoReflect>("a").unwrap();
|
||||
| --------- ^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following other types implement trait `Reflect`:
|
||||
bool
|
||||
char
|
||||
isize
|
||||
i8
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
i128
|
||||
and $N others
|
||||
note: required by a bound in `bevy_reflect::GetField::get_field`
|
||||
--> /home/runner/work/bevy/bevy/crates/bevy_reflect/src/struct_trait.rs:242:21
|
||||
|
|
||||
242 | fn get_field<T: Reflect>(&self, name: &str) -> Option<&T>;
|
||||
| ^^^^^^^ required by this bound in `GetField::get_field`
|
||||
|
||||
error[E0277]: the trait bound `NoReflect: GetTypeRegistration` is not satisfied
|
||||
--> tests/reflect_derive/generics.fail.rs:14:36
|
||||
|
|
||||
14 | let mut foo: Box<dyn Reflect> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect`
|
||||
14 | let mut foo: Box<dyn Struct> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GetTypeRegistration` is not implemented for `NoReflect`
|
||||
|
|
||||
= help: the following other types implement trait `Reflect`:
|
||||
= help: the following other types implement trait `GetTypeRegistration`:
|
||||
bool
|
||||
char
|
||||
isize
|
||||
|
@ -20,7 +38,8 @@ error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied
|
|||
i64
|
||||
i128
|
||||
and $N others
|
||||
note: required for `Foo<NoReflect>` to implement `Reflect`
|
||||
= note: required for `NoReflect` to implement `RegisterForReflection`
|
||||
note: required for `Foo<NoReflect>` to implement `bevy_reflect::Struct`
|
||||
--> tests/reflect_derive/generics.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(Reflect)]
|
||||
|
@ -28,5 +47,5 @@ note: required for `Foo<NoReflect>` to implement `Reflect`
|
|||
4 | #[reflect(from_reflect = false)]
|
||||
5 | struct Foo<T> {
|
||||
| ^^^^^^
|
||||
= note: required for the cast from `Box<Foo<NoReflect>>` to `Box<(dyn Reflect + 'static)>`
|
||||
= note: required for the cast from `Box<Foo<NoReflect>>` to `Box<(dyn bevy_reflect::Struct + 'static)>`
|
||||
= note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -7,10 +7,12 @@ use bevy_ecs::{
|
|||
system::{StaticSystemParam, SystemParam, SystemParamItem, SystemState},
|
||||
world::{FromWorld, Mut},
|
||||
};
|
||||
use bevy_reflect::std_traits::ReflectDefault;
|
||||
use bevy_reflect::{
|
||||
utility::{reflect_hasher, NonGenericTypeInfoCell},
|
||||
FromReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath,
|
||||
Typed, ValueInfo,
|
||||
FromReflect, FromType, GetTypeRegistration, Reflect, ReflectDeserialize, ReflectFromPtr,
|
||||
ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize,
|
||||
TypeInfo, TypePath, TypeRegistration, Typed, ValueInfo,
|
||||
};
|
||||
use bevy_utils::{thiserror::Error, HashMap, HashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -162,6 +164,18 @@ impl Reflect for RenderAssetUsages {
|
|||
}
|
||||
}
|
||||
|
||||
impl GetTypeRegistration for RenderAssetUsages {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
let mut registration = TypeRegistration::of::<Self>();
|
||||
registration.insert::<ReflectSerialize>(FromType::<Self>::from_type());
|
||||
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
|
||||
registration.insert::<ReflectDefault>(FromType::<Self>::from_type());
|
||||
registration.insert::<ReflectFromReflect>(FromType::<Self>::from_type());
|
||||
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
|
||||
registration
|
||||
}
|
||||
}
|
||||
|
||||
impl FromReflect for RenderAssetUsages {
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
let raw_value = *reflect.as_any().downcast_ref::<u8>()?;
|
||||
|
|
|
@ -12,8 +12,10 @@ fn main() {
|
|||
.run();
|
||||
}
|
||||
|
||||
/// The `#[derive(Reflect)]` macro will automatically add any required bounds to `T`,
|
||||
/// such as `Reflect` and `GetTypeRegistration`.
|
||||
#[derive(Reflect)]
|
||||
struct MyType<T: Reflect> {
|
||||
struct MyType<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue