mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Enable deriving Reflect on structs with generic types (#7364)
# 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)
This commit is contained in:
parent
9ffba9bb1a
commit
cbb4c26cad
14 changed files with 312 additions and 44 deletions
|
@ -1,6 +1,7 @@
|
|||
use crate::container_attributes::ReflectTraits;
|
||||
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
|
||||
use crate::utility::members_to_serialization_denylist;
|
||||
use crate::fq_std::{FQAny, FQDefault, FQSend, FQSync};
|
||||
use crate::utility::{members_to_serialization_denylist, WhereClauseOptions};
|
||||
use bit_set::BitSet;
|
||||
use quote::quote;
|
||||
|
||||
|
@ -316,12 +317,16 @@ impl<'a> ReflectMeta<'a> {
|
|||
}
|
||||
|
||||
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
|
||||
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
|
||||
pub fn get_type_registration(
|
||||
&self,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
) -> proc_macro2::TokenStream {
|
||||
crate::registration::impl_get_type_registration(
|
||||
self.type_name,
|
||||
&self.bevy_reflect_path,
|
||||
self.traits.idents(),
|
||||
self.generics,
|
||||
where_clause_options,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
@ -350,7 +355,10 @@ impl<'a> ReflectStruct<'a> {
|
|||
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
|
||||
///
|
||||
/// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method
|
||||
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
|
||||
pub fn get_type_registration(
|
||||
&self,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let reflect_path = self.meta.bevy_reflect_path();
|
||||
|
||||
crate::registration::impl_get_type_registration(
|
||||
|
@ -358,31 +366,37 @@ impl<'a> ReflectStruct<'a> {
|
|||
reflect_path,
|
||||
self.meta.traits().idents(),
|
||||
self.meta.generics(),
|
||||
where_clause_options,
|
||||
Some(&self.serialization_denylist),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a collection of types which are exposed to the reflection API
|
||||
pub fn active_types(&self) -> Vec<syn::Type> {
|
||||
self.fields
|
||||
.iter()
|
||||
.filter(move |field| field.attrs.ignore.is_active())
|
||||
self.active_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are exposed to the reflection API
|
||||
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields
|
||||
.iter()
|
||||
.filter(move |field| field.attrs.ignore.is_active())
|
||||
.filter(|field| field.attrs.ignore.is_active())
|
||||
}
|
||||
|
||||
/// Get a collection of types which are ignored by the reflection API
|
||||
pub fn ignored_types(&self) -> Vec<syn::Type> {
|
||||
self.ignored_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are ignored by the reflection API
|
||||
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields
|
||||
.iter()
|
||||
.filter(move |field| field.attrs.ignore.is_ignored())
|
||||
.filter(|field| field.attrs.ignore.is_ignored())
|
||||
}
|
||||
|
||||
/// The complete set of fields in this struct.
|
||||
|
@ -390,6 +404,16 @@ impl<'a> ReflectStruct<'a> {
|
|||
pub fn fields(&self) -> &[StructField<'a>] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
pub fn where_clause_options(&self) -> WhereClauseOptions {
|
||||
let bevy_reflect_path = &self.meta().bevy_reflect_path;
|
||||
WhereClauseOptions {
|
||||
active_types: self.active_types().into(),
|
||||
active_trait_bounds: quote! { #bevy_reflect_path::Reflect },
|
||||
ignored_types: self.ignored_types().into(),
|
||||
ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ReflectEnum<'a> {
|
||||
|
@ -410,4 +434,69 @@ impl<'a> ReflectEnum<'a> {
|
|||
pub fn variants(&self) -> &[EnumVariant<'a>] {
|
||||
&self.variants
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are exposed to the reflection API
|
||||
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.variants()
|
||||
.iter()
|
||||
.flat_map(|variant| variant.active_fields())
|
||||
}
|
||||
|
||||
/// Get a collection of types which are exposed to the reflection API
|
||||
pub fn active_types(&self) -> Vec<syn::Type> {
|
||||
self.active_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are ignored by the reflection API
|
||||
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.variants()
|
||||
.iter()
|
||||
.flat_map(|variant| variant.ignored_fields())
|
||||
}
|
||||
|
||||
/// Get a collection of types which are ignored to the reflection API
|
||||
pub fn ignored_types(&self) -> Vec<syn::Type> {
|
||||
self.ignored_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn where_clause_options(&self) -> WhereClauseOptions {
|
||||
let bevy_reflect_path = &self.meta().bevy_reflect_path;
|
||||
WhereClauseOptions {
|
||||
active_types: self.active_types().into(),
|
||||
active_trait_bounds: quote! { #bevy_reflect_path::FromReflect },
|
||||
ignored_types: self.ignored_types().into(),
|
||||
ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync + #FQDefault },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EnumVariant<'a> {
|
||||
/// Get an iterator of fields which are exposed to the reflection API
|
||||
#[allow(dead_code)]
|
||||
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields()
|
||||
.iter()
|
||||
.filter(|field| field.attrs.ignore.is_active())
|
||||
}
|
||||
|
||||
/// Get an iterator of fields which are ignored by the reflection API
|
||||
#[allow(dead_code)]
|
||||
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields()
|
||||
.iter()
|
||||
.filter(|field| field.attrs.ignore.is_ignored())
|
||||
}
|
||||
|
||||
/// The complete set of fields in this variant.
|
||||
#[allow(dead_code)]
|
||||
pub fn fields(&self) -> &[StructField<'a>] {
|
||||
match &self.fields {
|
||||
EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,
|
||||
EnumVariantFields::Unit => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ pub(crate) struct FQClone;
|
|||
pub(crate) struct FQDefault;
|
||||
pub(crate) struct FQOption;
|
||||
pub(crate) struct FQResult;
|
||||
pub(crate) struct FQSend;
|
||||
pub(crate) struct FQSync;
|
||||
|
||||
impl ToTokens for FQAny {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
|
@ -75,3 +77,15 @@ impl ToTokens for FQResult {
|
|||
quote!(::core::result::Result).to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FQSend {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
quote!(::core::marker::Send).to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FQSync {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
quote!(::core::marker::Sync).to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructFiel
|
|||
use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors};
|
||||
use crate::fq_std::{FQAny, FQBox, FQOption, FQResult};
|
||||
use crate::impls::impl_typed;
|
||||
use crate::utility::extend_where_clause;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
|
@ -15,6 +16,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
|||
let ref_index = Ident::new("__index_param", Span::call_site());
|
||||
let ref_value = Ident::new("__value_param", Span::call_site());
|
||||
|
||||
let where_clause_options = reflect_enum.where_clause_options();
|
||||
|
||||
let EnumImpls {
|
||||
variant_info,
|
||||
enum_field,
|
||||
|
@ -76,6 +79,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
|||
let typed_impl = impl_typed(
|
||||
enum_name,
|
||||
reflect_enum.meta().generics(),
|
||||
&where_clause_options,
|
||||
quote! {
|
||||
let variants = [#(#variant_info),*];
|
||||
let info = #info_generator;
|
||||
|
@ -84,16 +88,20 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
|||
bevy_reflect_path,
|
||||
);
|
||||
|
||||
let get_type_registration_impl = reflect_enum.meta().get_type_registration();
|
||||
let get_type_registration_impl = reflect_enum
|
||||
.meta()
|
||||
.get_type_registration(&where_clause_options);
|
||||
let (impl_generics, ty_generics, where_clause) =
|
||||
reflect_enum.meta().generics().split_for_impl();
|
||||
|
||||
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#get_type_registration_impl
|
||||
|
||||
#typed_impl
|
||||
|
||||
impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_reflect_clause {
|
||||
fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
|
||||
match self {
|
||||
#(#enum_field,)*
|
||||
|
@ -177,7 +185,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_reflect_clause {
|
||||
#[inline]
|
||||
fn type_name(&self) -> &str {
|
||||
::core::any::type_name::<Self>()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult};
|
||||
use crate::impls::impl_typed;
|
||||
use crate::utility::extend_where_clause;
|
||||
use crate::ReflectStruct;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
|
@ -88,9 +89,11 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
}
|
||||
};
|
||||
|
||||
let where_clause_options = reflect_struct.where_clause_options();
|
||||
let typed_impl = impl_typed(
|
||||
struct_name,
|
||||
reflect_struct.meta().generics(),
|
||||
&where_clause_options,
|
||||
quote! {
|
||||
let fields = [#field_generator];
|
||||
let info = #info_generator;
|
||||
|
@ -99,16 +102,18 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
bevy_reflect_path,
|
||||
);
|
||||
|
||||
let get_type_registration_impl = reflect_struct.get_type_registration();
|
||||
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
|
||||
let (impl_generics, ty_generics, where_clause) =
|
||||
reflect_struct.meta().generics().split_for_impl();
|
||||
|
||||
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#get_type_registration_impl
|
||||
|
||||
#typed_impl
|
||||
|
||||
impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_reflect_clause {
|
||||
fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
|
||||
match name {
|
||||
#(#field_names => #fqoption::Some(&self.#field_idents),)*
|
||||
|
@ -160,7 +165,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause {
|
||||
#[inline]
|
||||
fn type_name(&self) -> &str {
|
||||
::core::any::type_name::<Self>()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult};
|
||||
use crate::impls::impl_typed;
|
||||
use crate::utility::extend_where_clause;
|
||||
use crate::ReflectStruct;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
|
@ -11,7 +12,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
|
||||
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
|
||||
let struct_name = reflect_struct.meta().type_name();
|
||||
let get_type_registration_impl = reflect_struct.get_type_registration();
|
||||
|
||||
let field_idents = reflect_struct
|
||||
.active_fields()
|
||||
|
@ -21,6 +21,9 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
let field_count = field_idents.len();
|
||||
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
||||
|
||||
let where_clause_options = reflect_struct.where_clause_options();
|
||||
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
|
||||
|
||||
let hash_fn = reflect_struct
|
||||
.meta()
|
||||
.traits()
|
||||
|
@ -75,6 +78,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
let typed_impl = impl_typed(
|
||||
struct_name,
|
||||
reflect_struct.meta().generics(),
|
||||
&where_clause_options,
|
||||
quote! {
|
||||
let fields = [#field_generator];
|
||||
let info = #info_generator;
|
||||
|
@ -86,12 +90,14 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
let (impl_generics, ty_generics, where_clause) =
|
||||
reflect_struct.meta().generics().split_for_impl();
|
||||
|
||||
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#get_type_registration_impl
|
||||
|
||||
#typed_impl
|
||||
|
||||
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_reflect_clause {
|
||||
fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
|
||||
match index {
|
||||
#(#field_indices => #fqoption::Some(&self.#field_idents),)*
|
||||
|
@ -122,7 +128,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause {
|
||||
#[inline]
|
||||
fn type_name(&self) -> &str {
|
||||
::core::any::type_name::<Self>()
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::utility::{extend_where_clause, WhereClauseOptions};
|
||||
use proc_macro2::Ident;
|
||||
use quote::quote;
|
||||
use syn::{Generics, Path};
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn impl_typed(
|
||||
type_name: &Ident,
|
||||
generics: &Generics,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
generator: proc_macro2::TokenStream,
|
||||
bevy_reflect_path: &Path,
|
||||
) -> proc_macro2::TokenStream {
|
||||
|
@ -28,8 +31,11 @@ pub(crate) fn impl_typed(
|
|||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
// Add Typed bound for each active field
|
||||
let where_reflect_clause = extend_where_clause(where_clause, where_clause_options);
|
||||
|
||||
quote! {
|
||||
impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause {
|
||||
impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_reflect_clause {
|
||||
fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
|
||||
#static_generator
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult};
|
||||
use crate::impls::impl_typed;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::ReflectMeta;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
@ -21,9 +22,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
|
|||
#[cfg(not(feature = "documentation"))]
|
||||
let with_docs: Option<proc_macro2::TokenStream> = None;
|
||||
|
||||
let where_clause_options = WhereClauseOptions::default();
|
||||
let typed_impl = impl_typed(
|
||||
type_name,
|
||||
meta.generics(),
|
||||
&where_clause_options,
|
||||
quote! {
|
||||
let info = #bevy_reflect_path::ValueInfo::new::<Self>() #with_docs;
|
||||
#bevy_reflect_path::TypeInfo::Value(info)
|
||||
|
@ -32,7 +35,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
|
|||
);
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl();
|
||||
let get_type_registration_impl = meta.get_type_registration();
|
||||
let get_type_registration_impl = meta.get_type_registration(&where_clause_options);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#get_type_registration_impl
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
//! 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();
|
||||
|
@ -22,9 +25,11 @@ pub(crate) fn impl_get_type_registration(
|
|||
}
|
||||
});
|
||||
|
||||
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_clause {
|
||||
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());
|
||||
|
|
|
@ -4,7 +4,8 @@ use crate::field_attributes::ReflectIgnoreBehavior;
|
|||
use bevy_macro_utils::BevyManifest;
|
||||
use bit_set::BitSet;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use syn::{Member, Path};
|
||||
use quote::quote;
|
||||
use syn::{Member, Path, Type, WhereClause};
|
||||
|
||||
/// Returns the correct path for `bevy_reflect`.
|
||||
pub(crate) fn get_bevy_reflect_path() -> Path {
|
||||
|
@ -59,6 +60,84 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member {
|
|||
)
|
||||
}
|
||||
|
||||
/// Options defining how to extend the `where` clause in reflection with any additional bounds needed.
|
||||
pub(crate) struct WhereClauseOptions {
|
||||
/// Any types that will be reflected and need an extra trait bound
|
||||
pub(crate) active_types: Box<[Type]>,
|
||||
/// Trait bounds to add to the active types
|
||||
pub(crate) active_trait_bounds: proc_macro2::TokenStream,
|
||||
/// Any types that won't be reflected and need an extra trait bound
|
||||
pub(crate) ignored_types: Box<[Type]>,
|
||||
/// Trait bounds to add to the ignored types
|
||||
pub(crate) ignored_trait_bounds: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
impl Default for WhereClauseOptions {
|
||||
/// By default, don't add any additional bounds to the `where` clause
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active_types: Box::new([]),
|
||||
ignored_types: Box::new([]),
|
||||
active_trait_bounds: quote! {},
|
||||
ignored_trait_bounds: quote! {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the `where` clause in reflection with any additional bounds needed.
|
||||
///
|
||||
/// This is mostly used to add additional bounds to reflected objects with generic types.
|
||||
/// For reflection purposes, we usually have:
|
||||
/// * `active_trait_bounds: Reflect`
|
||||
/// * `ignored_trait_bounds: Any + Send + Sync`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `where_clause`: existing `where` clause present on the object to be derived
|
||||
/// * `where_clause_options`: additional paramters defining which trait bounds to add to the `where` clause
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// The struct:
|
||||
/// ```ignore
|
||||
/// #[derive(Reflect)]
|
||||
/// struct Foo<T, U> {
|
||||
/// a: T,
|
||||
/// #[reflect(ignore)]
|
||||
/// b: U
|
||||
/// }
|
||||
/// ```
|
||||
/// will have active types: `[T]` and ignored types: `[U]`
|
||||
///
|
||||
/// The `extend_where_clause` function will yield the following `where` clause:
|
||||
/// ```ignore
|
||||
/// where
|
||||
/// T: Reflect, // active_trait_bounds
|
||||
/// U: Any + Send + Sync, // ignored_trait_bounds
|
||||
/// ```
|
||||
pub(crate) fn extend_where_clause(
|
||||
where_clause: Option<&WhereClause>,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let active_types = &where_clause_options.active_types;
|
||||
let ignored_types = &where_clause_options.ignored_types;
|
||||
let active_trait_bounds = &where_clause_options.active_trait_bounds;
|
||||
let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds;
|
||||
|
||||
let mut generic_where_clause = if where_clause.is_some() {
|
||||
quote! {#where_clause}
|
||||
} else if !(active_types.is_empty() && ignored_types.is_empty()) {
|
||||
quote! {where}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
generic_where_clause.extend(quote! {
|
||||
#(#active_types: #active_trait_bounds,)*
|
||||
#(#ignored_types: #ignored_trait_bounds,)*
|
||||
});
|
||||
generic_where_clause
|
||||
}
|
||||
|
||||
impl<T> Default for ResultSifter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use bevy_reflect::Reflect;
|
||||
|
||||
#[derive(Reflect)]
|
||||
struct Foo<T> {
|
||||
a: T,
|
||||
}
|
||||
|
||||
// Type that doesn't implement Reflect
|
||||
struct NoReflect(f32);
|
||||
|
||||
fn main() {
|
||||
let mut foo: Box<dyn Reflect> = 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();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
error[E0599]: no method named `get_field` found for struct `Box<(dyn Reflect + 'static)>` in the current scope
|
||||
--> tests/reflect_derive/generics.fail.rs:14:9
|
||||
|
|
||||
14 | foo.get_field::<NoReflect>("a").unwrap();
|
||||
| ^^^^^^^^^ method not found in `Box<(dyn Reflect + 'static)>`
|
||||
|
||||
error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied
|
||||
--> tests/reflect_derive/generics.fail.rs:12:37
|
||||
|
|
||||
12 | let mut foo: Box<dyn Reflect> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect`
|
||||
|
|
||||
= help: the following other types implement trait `Reflect`:
|
||||
&'static Path
|
||||
()
|
||||
(A, B)
|
||||
(A, B, C)
|
||||
(A, B, C, D)
|
||||
(A, B, C, D, E)
|
||||
(A, B, C, D, E, F)
|
||||
(A, B, C, D, E, F, G)
|
||||
and $N others
|
||||
note: required for `Foo<NoReflect>` to implement `Reflect`
|
||||
--> tests/reflect_derive/generics.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(Reflect)]
|
||||
| ^^^^^^^
|
||||
4 | struct Foo<T> {
|
||||
| ^^^^^^
|
||||
= note: required for the cast from `Foo<NoReflect>` to the object type `dyn Reflect`
|
||||
= note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,29 @@
|
|||
use bevy_reflect::{Reflect, GetField};
|
||||
|
||||
#[derive(Reflect)]
|
||||
struct Foo<T, U, S> {
|
||||
a: T,
|
||||
#[reflect(ignore)]
|
||||
_b: U,
|
||||
|
||||
// check that duplicate types don't cause any compile errors
|
||||
_c: T,
|
||||
// check that when a type is both an active and inactive type, both trait bounds are used
|
||||
_d: U,
|
||||
|
||||
#[reflect(ignore)]
|
||||
_e: S,
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let foo = Foo::<u32, usize, f32> {
|
||||
a: 1,
|
||||
_b: 2,
|
||||
_c: 3,
|
||||
_d: 4,
|
||||
_e: 5.0,
|
||||
};
|
||||
|
||||
let _ = *foo.get_field::<u32>("a").unwrap();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
use bevy_reflect::Reflect;
|
||||
|
||||
#[derive(Reflect)]
|
||||
struct Foo<'a> {
|
||||
#[reflect(ignore)]
|
||||
value: &'a str,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -1,13 +0,0 @@
|
|||
error[E0478]: lifetime bound not satisfied
|
||||
--> tests/reflect_derive/lifetimes.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(Reflect)]
|
||||
| ^^^^^^^
|
||||
|
|
||||
note: lifetime parameter instantiated with the lifetime `'a` as defined here
|
||||
--> tests/reflect_derive/lifetimes.fail.rs:4:12
|
||||
|
|
||||
4 | struct Foo<'a> {
|
||||
| ^^
|
||||
= note: but lifetime parameter must outlive the static lifetime
|
||||
= note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info)
|
Loading…
Reference in a new issue