mirror of
https://github.com/bevyengine/bevy
synced 2025-01-08 03:08:55 +00:00
cbb4c26cad
# Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
42 lines
1.9 KiB
Rust
42 lines
1.9 KiB
Rust
//! Contains code related specifically to Bevy's type registration.
|
|
|
|
use crate::utility::{extend_where_clause, WhereClauseOptions};
|
|
use bit_set::BitSet;
|
|
use proc_macro2::Ident;
|
|
use quote::quote;
|
|
use syn::{Generics, Path};
|
|
|
|
/// Creates the `GetTypeRegistration` impl for the given type data.
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(crate) fn impl_get_type_registration(
|
|
type_name: &Ident,
|
|
bevy_reflect_path: &Path,
|
|
registration_data: &[Ident],
|
|
generics: &Generics,
|
|
where_clause_options: &WhereClauseOptions,
|
|
serialization_denylist: Option<&BitSet<u32>>,
|
|
) -> proc_macro2::TokenStream {
|
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
|
let serialization_data = serialization_denylist.map(|denylist| {
|
|
let denylist = denylist.into_iter();
|
|
quote! {
|
|
let ignored_indices = ::core::iter::IntoIterator::into_iter([#(#denylist),*]);
|
|
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#bevy_reflect_path::serde::SerializationData::new(ignored_indices));
|
|
}
|
|
});
|
|
|
|
let where_reflect_clause = extend_where_clause(where_clause, where_clause_options);
|
|
|
|
quote! {
|
|
#[allow(unused_mut)]
|
|
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_clause {
|
|
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
|
|
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
|
|
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
|
|
#serialization_data
|
|
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
|
|
registration
|
|
}
|
|
}
|
|
}
|
|
}
|