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:
Charles Bournhonesque 2023-01-28 00:12:06 +00:00
parent 9ffba9bb1a
commit cbb4c26cad
14 changed files with 312 additions and 44 deletions

View file

@ -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 => &[],
}
}
}

View file

@ -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);
}
}

View file

@ -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>()

View file

@ -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>()

View file

@ -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>()

View file

@ -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
}

View file

@ -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

View file

@ -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());

View file

@ -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 {

View file

@ -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();
}

View file

@ -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)

View file

@ -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();
}

View file

@ -1,9 +0,0 @@
use bevy_reflect::Reflect;
#[derive(Reflect)]
struct Foo<'a> {
#[reflect(ignore)]
value: &'a str,
}
fn main() {}

View file

@ -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)