1
1
use crate :: derive_data:: ReflectMeta ;
2
2
use bevy_macro_utils:: fq_std:: { FQAny , FQSend , FQSync } ;
3
- use proc_macro2:: TokenStream ;
3
+ use proc_macro2:: { TokenStream , TokenTree } ;
4
4
use quote:: { quote, ToTokens } ;
5
- use syn:: { punctuated:: Punctuated , Token , Type , WhereClause } ;
5
+ use syn:: { punctuated:: Punctuated , Ident , Token , Type , WhereClause } ;
6
6
7
7
/// Options defining how to extend the `where` clause for reflection.
8
8
pub ( crate ) struct WhereClauseOptions < ' a , ' b > {
@@ -29,15 +29,24 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
29
29
self . meta
30
30
}
31
31
32
- /// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
32
+ /// Extends the `where` clause for a type with additional bounds needed for the reflection
33
+ /// impls.
33
34
///
34
35
/// The default bounds added are as follows:
35
- /// - `Self` has the bounds `Any + Send + Sync`
36
- /// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
37
- /// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
38
- /// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
36
+ /// - `Self` has:
37
+ /// - `Any + Send + Sync` bounds, if generic over types
38
+ /// - An `Any` bound, if generic over lifetimes but not types
39
+ /// - No bounds, if generic over neither types nor lifetimes
40
+ /// - Any given bounds in a `where` clause on the type
41
+ /// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is
42
+ /// present
43
+ /// - Active fields with non-generic types have the bounds `TypePath`, either `PartialReflect`
44
+ /// if `#[reflect(from_reflect = false)]` is present or `FromReflect` otherwise,
45
+ /// `MaybeTyped`, and `RegisterForReflection` (or no bounds at all if
46
+ /// `#[reflect(no_field_bounds)]` is present)
39
47
///
40
- /// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
48
+ /// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are
49
+ /// added as well.
41
50
///
42
51
/// # Example
43
52
///
@@ -55,57 +64,69 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
55
64
/// ```ignore (bevy_reflect is not accessible from this crate)
56
65
/// where
57
66
/// // `Self` bounds:
58
- /// Self : Any + Send + Sync,
67
+ /// Foo<T, U> : Any + Send + Sync,
59
68
/// // Type parameter bounds:
60
69
/// T: TypePath,
61
70
/// U: TypePath,
62
- /// // Field bounds
63
- /// T: FromReflect + TypePath,
71
+ /// // Active non-generic field bounds
72
+ /// T: FromReflect + TypePath + MaybeTyped + RegisterForReflection,
73
+ ///
64
74
/// ```
65
75
///
66
- /// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate :
76
+ /// If we add various things to the type:
67
77
///
68
78
/// ```ignore (bevy_reflect is not accessible from this crate)
69
- /// where
70
- /// // `Self` bounds:
71
- /// Self: Any + Send + Sync,
72
- /// // Type parameter bounds:
73
- /// T: TypePath,
74
- /// U: TypePath,
75
- /// // Field bounds
76
- /// T: FromReflect + TypePath,
77
- /// // Custom bounds
78
- /// T: MyTrait,
79
+ /// #[derive(Reflect)]
80
+ /// #[reflect(where T: MyTrait)]
81
+ /// #[reflect(no_field_bounds)]
82
+ /// struct Foo<T, U>
83
+ /// where T: Clone
84
+ /// {
85
+ /// a: T,
86
+ /// #[reflect(ignore)]
87
+ /// b: U
88
+ /// }
79
89
/// ```
80
90
///
81
- /// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate :
91
+ /// It will instead generate the following where clause :
82
92
///
83
93
/// ```ignore (bevy_reflect is not accessible from this crate)
84
94
/// where
85
95
/// // `Self` bounds:
86
- /// Self: Any + Send + Sync,
96
+ /// Foo<T, U>: Any + Send + Sync,
97
+ /// // Given bounds:
98
+ /// T: Clone,
87
99
/// // Type parameter bounds:
88
100
/// T: TypePath,
89
101
/// U: TypePath,
102
+ /// // No active non-generic field bounds
90
103
/// // Custom bounds
91
104
/// T: MyTrait,
92
105
/// ```
93
106
pub fn extend_where_clause ( & self , where_clause : Option < & WhereClause > ) -> TokenStream {
94
- // We would normally just use `Self`, but that won't work for generating things like assertion functions
95
- // and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
96
- let this = self . meta . type_path ( ) . true_type ( ) ;
107
+ let mut generic_where_clause = quote ! { where } ;
97
108
98
- let required_bounds = self . required_bounds ( ) ;
109
+ // Bounds on `Self`. We would normally just use `Self`, but that won't work for generating
110
+ // things like assertion functions and trait impls for a type's reference (e.g. `impl
111
+ // FromArg for &MyType`).
112
+ let generics = self . meta . type_path ( ) . generics ( ) ;
113
+ if generics. type_params ( ) . next ( ) . is_some ( ) {
114
+ // Generic over types? We need `Any + Send + Sync`.
115
+ let this = self . meta . type_path ( ) . true_type ( ) ;
116
+ generic_where_clause. extend ( quote ! { #this: #FQAny + #FQSend + #FQSync , } ) ;
117
+ } else if generics. lifetimes ( ) . next ( ) . is_some ( ) {
118
+ // Generic only over lifetimes? We need `'static`.
119
+ let this = self . meta . type_path ( ) . true_type ( ) ;
120
+ generic_where_clause. extend ( quote ! { #this: ' static , } ) ;
121
+ }
99
122
100
- // Maintain existing where clause, if any.
101
- let mut generic_where_clause = if let Some ( where_clause) = where_clause {
123
+ // Maintain existing where clause bounds , if any.
124
+ if let Some ( where_clause) = where_clause {
102
125
let predicates = where_clause. predicates . iter ( ) ;
103
- quote ! { where #this: #required_bounds, #( #predicates, ) * }
104
- } else {
105
- quote ! ( where #this: #required_bounds, )
106
- } ;
126
+ generic_where_clause. extend ( quote ! { #( #predicates, ) * } ) ;
127
+ }
107
128
108
- // Add additional reflection trait bounds
129
+ // Add additional reflection trait bounds.
109
130
let predicates = self . predicates ( ) ;
110
131
generic_where_clause. extend ( quote ! {
111
132
#predicates
@@ -157,19 +178,57 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
157
178
let bevy_reflect_path = self . meta . bevy_reflect_path ( ) ;
158
179
let reflect_bound = self . reflect_bound ( ) ;
159
180
160
- // `TypePath` is always required for active fields since they are used to
161
- // construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
162
- // Likewise, `GetTypeRegistration` is always required for active fields since
163
- // they are used to register the type's dependencies.
164
- Some ( self . active_fields . iter ( ) . map ( move |ty| {
165
- quote ! (
166
- #ty : #reflect_bound
167
- + #bevy_reflect_path:: TypePath
168
- // Needed for `Typed` impls
169
- + #bevy_reflect_path:: MaybeTyped
170
- // Needed for `GetTypeRegistration` impls
171
- + #bevy_reflect_path:: __macro_exports:: RegisterForReflection
172
- )
181
+ // Get the identifiers of all type parameters.
182
+ let type_param_idents = self
183
+ . meta
184
+ . type_path ( )
185
+ . generics ( )
186
+ . type_params ( )
187
+ . map ( |type_param| type_param. ident . clone ( ) )
188
+ . collect :: < Vec < Ident > > ( ) ;
189
+
190
+ // Do any of the identifiers in `idents` appear in `token_stream`?
191
+ fn is_any_ident_in_token_stream ( idents : & [ Ident ] , token_stream : TokenStream ) -> bool {
192
+ for token_tree in token_stream {
193
+ match token_tree {
194
+ TokenTree :: Ident ( ident) => {
195
+ if idents. contains ( & ident) {
196
+ return true ;
197
+ }
198
+ }
199
+ TokenTree :: Group ( group) => {
200
+ if is_any_ident_in_token_stream ( idents, group. stream ( ) ) {
201
+ return true ;
202
+ }
203
+ }
204
+ TokenTree :: Punct ( _) | TokenTree :: Literal ( _) => { }
205
+ }
206
+ }
207
+ false
208
+ }
209
+
210
+ Some ( self . active_fields . iter ( ) . filter_map ( move |ty| {
211
+ // Field type bounds are only required if `ty` is generic. How to determine that?
212
+ // Search `ty`s token stream for identifiers that match the identifiers from the
213
+ // function's type params. E.g. if `T` and `U` are the type param identifiers and
214
+ // `ty` is `Vec<[T; 4]>` then the `T` identifiers match. This is a bit hacky, but
215
+ // it works.
216
+ let is_generic =
217
+ is_any_ident_in_token_stream ( & type_param_idents, ty. to_token_stream ( ) ) ;
218
+
219
+ is_generic. then ( || {
220
+ quote ! (
221
+ #ty: #reflect_bound
222
+ // Needed to construct `NamedField` and `UnnamedField` instances for
223
+ // the `Typed` impl.
224
+ + #bevy_reflect_path:: TypePath
225
+ // Needed for `Typed` impls
226
+ + #bevy_reflect_path:: MaybeTyped
227
+ // Needed for registering type dependencies in the
228
+ // `GetTypeRegistration` impl.
229
+ + #bevy_reflect_path:: __macro_exports:: RegisterForReflection
230
+ )
231
+ } )
173
232
} ) )
174
233
}
175
234
}
@@ -194,9 +253,4 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
194
253
None
195
254
}
196
255
}
197
-
198
- /// The minimum required bounds for a type to be reflected.
199
- fn required_bounds ( & self ) -> TokenStream {
200
- quote ! ( #FQAny + #FQSend + #FQSync )
201
- }
202
256
}
0 commit comments