Skip to content

Commit 5c17beb

Browse files
committed
Drop the FetchedItem trait, generate QueryItem structs
1 parent 2156d0f commit 5c17beb

File tree

4 files changed

+114
-164
lines changed

4 files changed

+114
-164
lines changed

crates/bevy_ecs/macros/src/fetch.rs

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use proc_macro2::{Ident, Span};
33
use quote::{quote, ToTokens};
44
use syn::{
55
parse::{Parse, ParseStream},
6-
parse_quote,
76
punctuated::Punctuated,
87
Attribute, Data, DataStruct, DeriveInput, Field, Fields, GenericArgument, GenericParam,
98
ImplGenerics, Lifetime, LifetimeDef, Path, PathArguments, ReturnType, Token, Type,
@@ -15,12 +14,12 @@ use crate::bevy_ecs_path;
1514
#[derive(Default)]
1615
struct FetchStructAttributes {
1716
pub mutable: bool,
18-
pub read_only_derive_args: Punctuated<syn::NestedMeta, syn::token::Comma>,
17+
pub derive_args: Punctuated<syn::NestedMeta, syn::token::Comma>,
1918
}
2019

2120
pub static FILTER_ATTRIBUTE_NAME: &str = "filter";
2221
static MUTABLE_ATTRIBUTE_NAME: &str = "mutable";
23-
static READ_ONLY_DERIVE_ATTRIBUTE_NAME: &str = "read_only_derive";
22+
static DERIVE_ATTRIBUTE_NAME: &str = "derive";
2423

2524
mod field_attr_keywords {
2625
syn::custom_keyword!(ignore);
@@ -52,15 +51,15 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
5251
MUTABLE_ATTRIBUTE_NAME
5352
);
5453
}
55-
} else if ident.map_or(false, |ident| ident == READ_ONLY_DERIVE_ATTRIBUTE_NAME) {
54+
} else if ident.map_or(false, |ident| ident == DERIVE_ATTRIBUTE_NAME) {
5655
if let syn::Meta::List(meta_list) = meta {
5756
fetch_struct_attributes
58-
.read_only_derive_args
57+
.derive_args
5958
.extend(meta_list.nested.iter().cloned());
6059
} else {
6160
panic!(
6261
"Expected a structured list within the `{}` attribute",
63-
READ_ONLY_DERIVE_ATTRIBUTE_NAME
62+
DERIVE_ATTRIBUTE_NAME
6463
);
6564
}
6665
} else {
@@ -77,7 +76,8 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
7776

7877
let FetchImplTokens {
7978
struct_name,
80-
struct_name_read_only,
79+
item_struct_name,
80+
read_only_item_struct_name,
8181
fetch_struct_name,
8282
state_struct_name,
8383
read_only_fetch_struct_name,
@@ -109,16 +109,10 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
109109
panic!("Expected a struct with a lifetime");
110110
}
111111

112-
let read_only_derive_macro_call = if fetch_struct_attributes.read_only_derive_args.is_empty() {
112+
let derive_macro_call = if fetch_struct_attributes.derive_args.is_empty() {
113113
quote! {}
114114
} else {
115-
if !fetch_struct_attributes.mutable {
116-
panic!(
117-
"Attribute `{}` can only be be used for a struct marked with the `{}` attribute",
118-
READ_ONLY_DERIVE_ATTRIBUTE_NAME, MUTABLE_ATTRIBUTE_NAME
119-
);
120-
}
121-
let derive_args = &fetch_struct_attributes.read_only_derive_args;
115+
let derive_args = &fetch_struct_attributes.derive_args;
122116
quote! { #[derive(#derive_args)] }
123117
};
124118

@@ -137,11 +131,11 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
137131

138132
let struct_read_only_declaration = if fetch_struct_attributes.mutable {
139133
quote! {
140-
// TODO: it would be great to be able to dedup this by just deriving `Fetch` again
134+
// TODO: it would be great to be able to dedup this by just deriving `WorldQuery` again
141135
// without the `mutable` attribute, but we'd need a way to avoid creating a redundant
142136
// `State` struct.
143-
#read_only_derive_macro_call
144-
struct #struct_name_read_only #impl_generics #where_clause {
137+
#derive_macro_call
138+
struct #read_only_item_struct_name #impl_generics #where_clause {
145139
#(#(#field_attrs)* #field_visibilities #field_idents: <<#query_types as #path::query::WorldQuery>::ReadOnlyFetch as #path::query::Fetch<#world_lifetime, #world_lifetime>>::Item,)*
146140
#(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
147141
}
@@ -152,7 +146,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
152146
}
153147

154148
impl #fetch_impl_generics #path::query::Fetch<#fetch_trait_punctuated_lifetimes> for #read_only_fetch_struct_name #fetch_ty_generics #where_clause {
155-
type Item = #struct_name_read_only #ty_generics;
149+
type Item = #read_only_item_struct_name #ty_generics;
156150
type State = #state_struct_name #fetch_ty_generics;
157151

158152
unsafe fn init(_world: &#path::world::World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self {
@@ -179,7 +173,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
179173
/// SAFETY: we call `table_fetch` for each member that implements `Fetch`.
180174
#[inline]
181175
unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item {
182-
#struct_name_read_only {
176+
Self::Item {
183177
#(#field_idents: self.#field_idents.table_fetch(_table_row),)*
184178
#(#ignored_field_idents: Default::default(),)*
185179
}
@@ -188,22 +182,18 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
188182
/// SAFETY: we call `archetype_fetch` for each member that implements `Fetch`.
189183
#[inline]
190184
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item {
191-
#struct_name_read_only {
185+
Self::Item {
192186
#(#field_idents: self.#field_idents.archetype_fetch(_archetype_index),)*
193187
#(#ignored_field_idents: Default::default(),)*
194188
}
195189
}
196190
}
197191

198-
impl #impl_generics #path::query::WorldQuery for #struct_name_read_only #ty_generics #where_clause {
192+
impl #impl_generics #path::query::WorldQuery for #read_only_item_struct_name #ty_generics #where_clause {
199193
type Fetch = #read_only_fetch_struct_name #ty_generics;
200194
type State = #state_struct_name #ty_generics;
201195
type ReadOnlyFetch = #read_only_fetch_struct_name #ty_generics;
202196
}
203-
204-
impl #impl_generics #path::query::FetchedItem for #struct_name_read_only #ty_generics #where_clause {
205-
type Query = Self;
206-
}
207197
}
208198
} else {
209199
quote! {
@@ -223,6 +213,12 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
223213
};
224214

225215
let tokens = TokenStream::from(quote! {
216+
#derive_macro_call
217+
struct #item_struct_name #impl_generics #where_clause {
218+
#(#(#field_attrs)* #field_visibilities #field_idents: <<#query_types as #path::query::WorldQuery>::Fetch as #path::query::Fetch<#world_lifetime, #world_lifetime>>::Item,)*
219+
#(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
220+
}
221+
226222
struct #fetch_struct_name #impl_generics #where_clause {
227223
#(#field_idents: <#query_types as #path::query::WorldQuery>::Fetch,)*
228224
#(#ignored_field_idents: #ignored_field_types,)*
@@ -234,11 +230,11 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
234230
}
235231

236232
impl #fetch_impl_generics #path::query::Fetch<#fetch_trait_punctuated_lifetimes> for #fetch_struct_name #fetch_ty_generics #where_clause {
237-
type Item = #struct_name #ty_generics;
233+
type Item = #item_struct_name #ty_generics;
238234
type State = #state_struct_name #fetch_ty_generics;
239235

240236
unsafe fn init(_world: &#path::world::World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self {
241-
#fetch_struct_name {
237+
Self {
242238
#(#field_idents: <#fetch_init_types as #path::query::WorldQuery>::Fetch::init(_world, &state.#field_idents, _last_change_tick, _change_tick),)*
243239
#(#ignored_field_idents: Default::default(),)*
244240
}
@@ -261,7 +257,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
261257
/// SAFETY: we call `table_fetch` for each member that implements `Fetch`.
262258
#[inline]
263259
unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item {
264-
#struct_name {
260+
Self::Item {
265261
#(#field_idents: self.#field_idents.table_fetch(_table_row),)*
266262
#(#ignored_field_idents: Default::default(),)*
267263
}
@@ -270,7 +266,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
270266
/// SAFETY: we call `archetype_fetch` for each member that implements `Fetch`.
271267
#[inline]
272268
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item {
273-
#struct_name {
269+
Self::Item {
274270
#(#field_idents: self.#field_idents.archetype_fetch(_archetype_index),)*
275271
#(#ignored_field_idents: Default::default(),)*
276272
}
@@ -311,20 +307,28 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
311307
type ReadOnlyFetch = #read_only_fetch_struct_name #ty_generics;
312308
}
313309

314-
impl #impl_generics #path::query::FetchedItem for #struct_name #ty_generics #where_clause {
315-
type Query = Self;
316-
}
317-
318310
/// SAFETY: each item in the struct is read only
319311
unsafe impl #impl_generics #path::query::ReadOnlyFetch for #read_only_fetch_struct_name #ty_generics #where_clause {}
312+
313+
// The original struct will most likely be left unused. As we don't want our users having
314+
// to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed
315+
// workaround.
316+
#[allow(dead_code)]
317+
const _: () = {
318+
fn dead_code_workaround #impl_generics (q: #struct_name #ty_generics) #where_clause {
319+
#(q.#field_idents;)*
320+
#(q.#ignored_field_idents;)*
321+
}
322+
};
320323
});
321324
tokens
322325
}
323326

324327
pub fn derive_world_query_filter_impl(ast: DeriveInput) -> TokenStream {
325328
let FetchImplTokens {
326329
struct_name,
327-
struct_name_read_only: _,
330+
item_struct_name: _,
331+
read_only_item_struct_name: _,
328332
fetch_struct_name,
329333
state_struct_name,
330334
read_only_fetch_struct_name: _,
@@ -446,11 +450,12 @@ pub fn derive_world_query_filter_impl(ast: DeriveInput) -> TokenStream {
446450
tokens
447451
}
448452

449-
// This struct is used to share common tokens between `Fetch` and `FilterFetch` implementations.
453+
// This struct is used to share common tokens between query and filter implementations.
450454
struct FetchImplTokens<'a> {
451455
struct_name: Ident,
452-
// Equals `struct_name` if `has_mutable_attr` is false.
453-
struct_name_read_only: Ident,
456+
item_struct_name: Ident,
457+
// Equals `item_struct_name` if `has_mutable_attr` is false.
458+
read_only_item_struct_name: Ident,
454459
fetch_struct_name: Ident,
455460
state_struct_name: Ident,
456461
read_only_fetch_struct_name: Ident,
@@ -505,10 +510,11 @@ fn fetch_impl_tokens<'a>(
505510
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
506511

507512
let struct_name = ast.ident.clone();
508-
let struct_name_read_only = if has_mutable_attr {
509-
Ident::new(&format!("{}ReadOnly", struct_name), Span::call_site())
513+
let item_struct_name = Ident::new(&format!("{}Item", struct_name), Span::call_site());
514+
let read_only_item_struct_name = if has_mutable_attr {
515+
Ident::new(&format!("{}ReadOnlyItem", struct_name), Span::call_site())
510516
} else {
511-
ast.ident.clone()
517+
item_struct_name.clone()
512518
};
513519
let fetch_struct_name = Ident::new(&format!("{}Fetch", struct_name), Span::call_site());
514520
let state_struct_name = Ident::new(&format!("{}State", struct_name), Span::call_site());
@@ -569,7 +575,8 @@ fn fetch_impl_tokens<'a>(
569575

570576
FetchImplTokens {
571577
struct_name,
572-
struct_name_read_only,
578+
item_struct_name,
579+
read_only_item_struct_name,
573580
fetch_struct_name,
574581
state_struct_name,
575582
read_only_fetch_struct_name,
@@ -595,14 +602,13 @@ fn fetch_impl_tokens<'a>(
595602
}
596603

597604
struct WorldQueryFieldInfo {
598-
/// We convert `Mut<T>` to `&mut T` (because this is the type that implements `WorldQuery`)
599-
/// and store it here.
605+
/// The original field type.
600606
query_type: Type,
601607
/// The same as `query_type` but with `'fetch` lifetime.
602608
fetch_init_type: Type,
603609
/// Has `#[fetch(ignore)]` or `#[filter_fetch(ignore)]` attribute.
604610
is_ignored: bool,
605-
/// All field attributes except for `fetch` or `filter_fetch`.
611+
/// All field attributes except for `world_query` ones.
606612
attrs: Vec<Attribute>,
607613
}
608614

@@ -612,8 +618,6 @@ fn read_world_query_field_info(
612618
world_lifetime: &Lifetime,
613619
fetch_lifetime: &Lifetime,
614620
) -> WorldQueryFieldInfo {
615-
let path = bevy_ecs_path();
616-
617621
let is_ignored = field
618622
.attrs
619623
.iter()
@@ -649,8 +653,7 @@ fn read_world_query_field_info(
649653
})
650654
.collect();
651655

652-
let ty = &field.ty;
653-
let query_type: Type = parse_quote!(<#ty as #path::query::FetchedItem>::Query);
656+
let query_type: Type = field.ty.clone();
654657
let mut fetch_init_type: Type = query_type.clone();
655658

656659
replace_lifetime_for_type(&mut fetch_init_type, world_lifetime, fetch_lifetime);

0 commit comments

Comments
 (0)