Skip to content

Commit 1e3ecec

Browse files
add Reflect and Typed bounds when reflecting on a struct that has generic types
1 parent c3a4682 commit 1e3ecec

File tree

8 files changed

+108
-12
lines changed

8 files changed

+108
-12
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use quote::quote;
77
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
88
use syn::punctuated::Punctuated;
99
use syn::spanned::Spanned;
10-
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant};
10+
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant};
1111

1212
pub(crate) enum ReflectDerive<'a> {
1313
Struct(ReflectStruct<'a>),
@@ -322,6 +322,7 @@ impl<'a> ReflectMeta<'a> {
322322
&self.bevy_reflect_path,
323323
self.traits.idents(),
324324
self.generics,
325+
&Vec::default(),
325326
None,
326327
)
327328
}
@@ -350,14 +351,15 @@ impl<'a> ReflectStruct<'a> {
350351
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
351352
///
352353
/// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method
353-
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
354+
pub fn get_type_registration(&self, fields: &Vec<Type>) -> proc_macro2::TokenStream {
354355
let reflect_path = self.meta.bevy_reflect_path();
355356

356357
crate::registration::impl_get_type_registration(
357358
self.meta.type_name(),
358359
reflect_path,
359360
self.meta.traits().idents(),
360361
self.meta.generics(),
362+
fields,
361363
Some(&self.serialization_denylist),
362364
)
363365
}

crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
7676
let typed_impl = impl_typed(
7777
enum_name,
7878
reflect_enum.meta().generics(),
79+
&Vec::default(),
7980
quote! {
8081
let variants = [#(#variant_info),*];
8182
let info = #info_generator;

crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
9191
let typed_impl = impl_typed(
9292
struct_name,
9393
reflect_struct.meta().generics(),
94+
&field_types,
9495
quote! {
9596
let fields = [#field_generator];
9697
let info = #info_generator;
@@ -99,16 +100,34 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
99100
bevy_reflect_path,
100101
);
101102

102-
let get_type_registration_impl = reflect_struct.get_type_registration();
103+
let get_type_registration_impl = reflect_struct.get_type_registration(&field_types);
103104
let (impl_generics, ty_generics, where_clause) =
104105
reflect_struct.meta().generics().split_for_impl();
105106

107+
// Add Reflect bound for each active field
108+
let mut where_reflect_clause = if where_clause.is_some() {
109+
quote! {#where_clause}
110+
} else if !field_types.is_empty() {
111+
quote! {where}
112+
} else {
113+
quote! {}
114+
};
115+
where_reflect_clause.extend(quote! {
116+
// TODO: had to add #bevy_reflect_path::Typed to get the test to compile,
117+
// presumably because of this:
118+
// #[inline]
119+
// fn get_type_info(&self) -> &'static bevy_reflect::TypeInfo {
120+
// <Self as bevy_reflect::Typed>::type_info()
121+
// }
122+
#(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)*
123+
});
124+
106125
TokenStream::from(quote! {
107126
#get_type_registration_impl
108127

109128
#typed_impl
110129

111-
impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause {
130+
impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_reflect_clause {
112131
fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
113132
match name {
114133
#(#field_names => #fqoption::Some(&self.#field_idents),)*
@@ -160,7 +179,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
160179
}
161180
}
162181

163-
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause {
182+
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause {
164183
#[inline]
165184
fn type_name(&self) -> &str {
166185
::core::any::type_name::<Self>()

crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
1111

1212
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
1313
let struct_name = reflect_struct.meta().type_name();
14-
let get_type_registration_impl = reflect_struct.get_type_registration();
1514

1615
let field_idents = reflect_struct
1716
.active_fields()
@@ -21,6 +20,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
2120
let field_count = field_idents.len();
2221
let field_indices = (0..field_count).collect::<Vec<usize>>();
2322

23+
let get_type_registration_impl = reflect_struct.get_type_registration(&field_types);
24+
2425
let hash_fn = reflect_struct
2526
.meta()
2627
.traits()
@@ -75,6 +76,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
7576
let typed_impl = impl_typed(
7677
struct_name,
7778
reflect_struct.meta().generics(),
79+
&field_types,
7880
quote! {
7981
let fields = [#field_generator];
8082
let info = #info_generator;
@@ -86,12 +88,30 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
8688
let (impl_generics, ty_generics, where_clause) =
8789
reflect_struct.meta().generics().split_for_impl();
8890

91+
// Add Reflect bound for each active field
92+
let mut where_reflect_clause = if where_clause.is_some() {
93+
quote! {#where_clause}
94+
} else if !field_types.is_empty() {
95+
quote! {where}
96+
} else {
97+
quote! {}
98+
};
99+
where_reflect_clause.extend(quote! {
100+
// TODO: had to add #bevy_reflect_path::Typed to get the test to compile,
101+
// presumably because of this:
102+
// #[inline]
103+
// fn get_type_info(&self) -> &'static bevy_reflect::TypeInfo {
104+
// <Self as bevy_reflect::Typed>::type_info()
105+
// }
106+
#(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)*
107+
});
108+
89109
TokenStream::from(quote! {
90110
#get_type_registration_impl
91111

92112
#typed_impl
93113

94-
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause {
114+
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_reflect_clause {
95115
fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
96116
match index {
97117
#(#field_indices => #fqoption::Some(&self.#field_idents),)*
@@ -122,7 +142,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
122142
}
123143
}
124144

125-
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause {
145+
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause {
126146
#[inline]
127147
fn type_name(&self) -> &str {
128148
::core::any::type_name::<Self>()

crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use proc_macro2::Ident;
22
use quote::quote;
3-
use syn::{Generics, Path};
3+
use syn::{Generics, Path, Type};
44

55
pub(crate) fn impl_typed(
66
type_name: &Ident,
77
generics: &Generics,
8+
field_types: &Vec<Type>,
89
generator: proc_macro2::TokenStream,
910
bevy_reflect_path: &Path,
1011
) -> proc_macro2::TokenStream {
@@ -28,8 +29,20 @@ pub(crate) fn impl_typed(
2829

2930
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
3031

32+
// Add Typed bound for each active field
33+
let mut where_typed_clause = if where_clause.is_some() {
34+
quote! {#where_clause}
35+
} else if !field_types.is_empty() {
36+
quote! {where}
37+
} else {
38+
quote! {}
39+
};
40+
where_typed_clause.extend(quote! {
41+
#(#field_types: #bevy_reflect_path::Typed,)*
42+
});
43+
3144
quote! {
32-
impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause {
45+
impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_typed_clause {
3346
fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
3447
#static_generator
3548
}

crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
2121
#[cfg(not(feature = "documentation"))]
2222
let with_docs: Option<proc_macro2::TokenStream> = None;
2323

24+
let field_types = Vec::default();
2425
let typed_impl = impl_typed(
2526
type_name,
2627
meta.generics(),
28+
&field_types,
2729
quote! {
2830
let info = #bevy_reflect_path::ValueInfo::new::<Self>() #with_docs;
2931
#bevy_reflect_path::TypeInfo::Value(info)

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
use bit_set::BitSet;
44
use proc_macro2::Ident;
55
use quote::quote;
6-
use syn::{Generics, Path};
6+
use syn::{Generics, Path, Type};
77

88
/// Creates the `GetTypeRegistration` impl for the given type data.
99
pub(crate) fn impl_get_type_registration(
1010
type_name: &Ident,
1111
bevy_reflect_path: &Path,
1212
registration_data: &[Ident],
1313
generics: &Generics,
14+
field_types: &Vec<Type>,
1415
serialization_denylist: Option<&BitSet<u32>>,
1516
) -> proc_macro2::TokenStream {
1617
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@@ -22,9 +23,21 @@ pub(crate) fn impl_get_type_registration(
2223
}
2324
});
2425

26+
// Add Typed bound for each active field
27+
let mut where_reflect_typed_clause = if where_clause.is_some() {
28+
quote! {#where_clause}
29+
} else if !field_types.is_empty() {
30+
quote! {where}
31+
} else {
32+
quote! {}
33+
};
34+
where_reflect_typed_clause.extend(quote! {
35+
#(#field_types: #bevy_reflect_path::Reflect + #bevy_reflect_path::Typed,)*
36+
});
37+
2538
quote! {
2639
#[allow(unused_mut)]
27-
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
40+
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_typed_clause {
2841
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
2942
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
3043
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());

crates/bevy_reflect/src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,32 @@ mod tests {
158158
assert_eq!(foo.a, 123);
159159
}
160160

161+
#[test]
162+
fn reflect_struct_generics() {
163+
#[derive(Reflect)]
164+
struct Foo<T> {
165+
a: T,
166+
}
167+
168+
let mut foo = Foo::<u32>{
169+
a: 42,
170+
};
171+
172+
let a = *foo.get_field::<u32>("a").unwrap();
173+
assert_eq!(a, 42);
174+
175+
*foo.get_field_mut::<u32>("a").unwrap() += 1;
176+
assert_eq!(foo.a, 43);
177+
178+
// patch Foo with a dynamic struct
179+
let mut dynamic_struct = DynamicStruct::default();
180+
dynamic_struct.insert("a", 123u32);
181+
dynamic_struct.insert("should_be_ignored", 456);
182+
183+
foo.apply(&dynamic_struct);
184+
assert_eq!(foo.a, 123);
185+
}
186+
161187
#[test]
162188
fn reflect_map() {
163189
#[derive(Reflect, Hash)]

0 commit comments

Comments
 (0)