diff --git a/crates/bevy_core/src/name.rs b/crates/bevy_core/src/name.rs index 9ab3f2ab16..41307cd7ae 100644 --- a/crates/bevy_core/src/name.rs +++ b/crates/bevy_core/src/name.rs @@ -1,6 +1,6 @@ -use bevy_ecs::{ - component::Component, entity::Entity, query::WorldQuery, reflect::ReflectComponent, -}; +use bevy_ecs::query::WorldQueryData; +use bevy_ecs::{component::Component, entity::Entity, reflect::ReflectComponent}; + use bevy_reflect::std_traits::ReflectDefault; use bevy_reflect::Reflect; use bevy_utils::AHasher; @@ -102,7 +102,7 @@ impl std::fmt::Debug for Name { /// } /// # bevy_ecs::system::assert_is_system(increment_score); /// ``` -#[derive(WorldQuery)] +#[derive(WorldQueryData)] pub struct DebugName { /// A [`Name`] that the entity might have that is displayed if available. pub name: Option<&'static Name>, diff --git a/crates/bevy_ecs/macros/src/fetch.rs b/crates/bevy_ecs/macros/src/fetch.rs deleted file mode 100644 index e28d33475b..0000000000 --- a/crates/bevy_ecs/macros/src/fetch.rs +++ /dev/null @@ -1,483 +0,0 @@ -use bevy_macro_utils::ensure_no_collision; -use proc_macro::TokenStream; -use proc_macro2::{Ident, Span}; -use quote::{format_ident, quote, ToTokens}; -use syn::{ - parse::{Parse, ParseStream}, - parse_macro_input, parse_quote, - punctuated::Punctuated, - token::Comma, - Attribute, Data, DataStruct, DeriveInput, Field, Index, Meta, -}; - -use crate::bevy_ecs_path; - -#[derive(Default)] -struct FetchStructAttributes { - pub is_mutable: bool, - pub derive_args: Punctuated, -} - -static MUTABLE_ATTRIBUTE_NAME: &str = "mutable"; -static DERIVE_ATTRIBUTE_NAME: &str = "derive"; - -mod field_attr_keywords { - syn::custom_keyword!(ignore); -} - -pub static WORLD_QUERY_ATTRIBUTE_NAME: &str = "world_query"; - -pub fn derive_world_query_impl(input: TokenStream) -> TokenStream { - let tokens = input.clone(); - - let ast = parse_macro_input!(input as DeriveInput); - let visibility = ast.vis; - - let mut fetch_struct_attributes = FetchStructAttributes::default(); - for attr in &ast.attrs { - if !attr - .path() - .get_ident() - .map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME) - { - continue; - } - - attr.parse_args_with(|input: ParseStream| { - let meta = input.parse_terminated(syn::Meta::parse, Comma)?; - for meta in meta { - let ident = meta.path().get_ident().unwrap_or_else(|| { - panic!( - "Unrecognized attribute: `{}`", - meta.path().to_token_stream() - ) - }); - if ident == MUTABLE_ATTRIBUTE_NAME { - if let syn::Meta::Path(_) = meta { - fetch_struct_attributes.is_mutable = true; - } else { - panic!( - "The `{MUTABLE_ATTRIBUTE_NAME}` attribute is expected to have no value or arguments", - ); - } - } else if ident == DERIVE_ATTRIBUTE_NAME { - if let syn::Meta::List(meta_list) = meta { - meta_list.parse_nested_meta(|meta| { - fetch_struct_attributes.derive_args.push(Meta::Path(meta.path)); - Ok(()) - })?; - } else { - panic!( - "Expected a structured list within the `{DERIVE_ATTRIBUTE_NAME}` attribute", - ); - } - } else { - panic!( - "Unrecognized attribute: `{}`", - meta.path().to_token_stream() - ); - } - } - Ok(()) - }) - .unwrap_or_else(|_| panic!("Invalid `{WORLD_QUERY_ATTRIBUTE_NAME}` attribute format")); - } - - let path = bevy_ecs_path(); - - let user_generics = ast.generics.clone(); - let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl(); - let user_generics_with_world = { - let mut generics = ast.generics; - generics.params.insert(0, parse_quote!('__w)); - generics - }; - let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) = - user_generics_with_world.split_for_impl(); - - let struct_name = ast.ident; - let read_only_struct_name = if fetch_struct_attributes.is_mutable { - Ident::new(&format!("{struct_name}ReadOnly"), Span::call_site()) - } else { - #[allow(clippy::redundant_clone)] - struct_name.clone() - }; - - let item_struct_name = Ident::new(&format!("{struct_name}Item"), Span::call_site()); - let read_only_item_struct_name = if fetch_struct_attributes.is_mutable { - Ident::new(&format!("{struct_name}ReadOnlyItem"), Span::call_site()) - } else { - #[allow(clippy::redundant_clone)] - item_struct_name.clone() - }; - - let fetch_struct_name = Ident::new(&format!("{struct_name}Fetch"), Span::call_site()); - let fetch_struct_name = ensure_no_collision(fetch_struct_name, tokens.clone()); - let read_only_fetch_struct_name = if fetch_struct_attributes.is_mutable { - let new_ident = Ident::new(&format!("{struct_name}ReadOnlyFetch"), Span::call_site()); - ensure_no_collision(new_ident, tokens.clone()) - } else { - #[allow(clippy::redundant_clone)] - fetch_struct_name.clone() - }; - - let marker_name = - ensure_no_collision(format_ident!("_world_query_derive_marker"), tokens.clone()); - - // Generate a name for the state struct that doesn't conflict - // with the struct definition. - let state_struct_name = Ident::new(&format!("{struct_name}State"), Span::call_site()); - let state_struct_name = ensure_no_collision(state_struct_name, tokens); - - let Data::Struct(DataStruct { fields, .. }) = &ast.data else { - return syn::Error::new( - Span::call_site(), - "#[derive(WorldQuery)]` only supports structs", - ) - .into_compile_error() - .into(); - }; - - let mut field_attrs = Vec::new(); - let mut field_visibilities = Vec::new(); - let mut field_idents = Vec::new(); - let mut named_field_idents = Vec::new(); - let mut field_types = Vec::new(); - let mut read_only_field_types = Vec::new(); - for (i, field) in fields.iter().enumerate() { - let attrs = match read_world_query_field_info(field) { - Ok(WorldQueryFieldInfo { attrs }) => attrs, - Err(e) => return e.into_compile_error().into(), - }; - - let named_field_ident = field - .ident - .as_ref() - .cloned() - .unwrap_or_else(|| format_ident!("f{i}")); - let i = Index::from(i); - let field_ident = field - .ident - .as_ref() - .map_or(quote! { #i }, |i| quote! { #i }); - field_idents.push(field_ident); - named_field_idents.push(named_field_ident); - field_attrs.push(attrs); - field_visibilities.push(field.vis.clone()); - let field_ty = field.ty.clone(); - field_types.push(quote!(#field_ty)); - read_only_field_types.push(quote!(<#field_ty as #path::query::WorldQuery>::ReadOnly)); - } - - let derive_args = &fetch_struct_attributes.derive_args; - // `#[derive()]` is valid syntax - let derive_macro_call = quote! { #[derive(#derive_args)] }; - - let impl_fetch = |is_readonly: bool| { - let struct_name = if is_readonly { - &read_only_struct_name - } else { - &struct_name - }; - let item_struct_name = if is_readonly { - &read_only_item_struct_name - } else { - &item_struct_name - }; - let fetch_struct_name = if is_readonly { - &read_only_fetch_struct_name - } else { - &fetch_struct_name - }; - - let field_types = if is_readonly { - &read_only_field_types - } else { - &field_types - }; - - let item_struct = match fields { - syn::Fields::Named(_) => quote! { - #derive_macro_call - #[doc = "Automatically generated [`WorldQuery`] item type for [`"] - #[doc = stringify!(#struct_name)] - #[doc = "`], returned when iterating over query results."] - #[automatically_derived] - #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { - #(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::WorldQuery>::Item<'__w>,)* - } - }, - syn::Fields::Unnamed(_) => quote! { - #derive_macro_call - #[doc = "Automatically generated [`WorldQuery`] item type for [`"] - #[doc = stringify!(#struct_name)] - #[doc = "`], returned when iterating over query results."] - #[automatically_derived] - #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world( - #( #field_visibilities <#field_types as #path::query::WorldQuery>::Item<'__w>, )* - ); - }, - syn::Fields::Unit => quote! { - #[doc = "Automatically generated [`WorldQuery`] item type for [`"] - #[doc = stringify!(#struct_name)] - #[doc = "`], returned when iterating over query results."] - #[automatically_derived] - #visibility type #item_struct_name #user_ty_generics_with_world = #struct_name #user_ty_generics; - }, - }; - - let query_impl = quote! { - #[doc(hidden)] - #[doc = "Automatically generated internal [`WorldQuery`] fetch type for [`"] - #[doc = stringify!(#struct_name)] - #[doc = "`], used to define the world data accessed by this query."] - #[automatically_derived] - #visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { - #(#named_field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)* - #marker_name: &'__w (), - } - - impl #user_impl_generics_with_world Clone for #fetch_struct_name #user_ty_generics_with_world - #user_where_clauses_with_world { - fn clone(&self) -> Self { - Self { - #(#named_field_idents: self.#named_field_idents.clone(),)* - #marker_name: &(), - } - } - } - - // SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field - unsafe impl #user_impl_generics #path::query::WorldQuery - for #struct_name #user_ty_generics #user_where_clauses { - - type Item<'__w> = #item_struct_name #user_ty_generics_with_world; - type Fetch<'__w> = #fetch_struct_name #user_ty_generics_with_world; - type ReadOnly = #read_only_struct_name #user_ty_generics; - type State = #state_struct_name #user_ty_generics; - - fn shrink<'__wlong: '__wshort, '__wshort>( - item: <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wlong> - ) -> <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wshort> { - #item_struct_name { - #( - #field_idents: <#field_types>::shrink(item.#field_idents), - )* - } - } - - unsafe fn init_fetch<'__w>( - _world: #path::world::unsafe_world_cell::UnsafeWorldCell<'__w>, - state: &Self::State, - _last_run: #path::component::Tick, - _this_run: #path::component::Tick, - ) -> ::Fetch<'__w> { - #fetch_struct_name { - #(#named_field_idents: - <#field_types>::init_fetch( - _world, - &state.#named_field_idents, - _last_run, - _this_run, - ), - )* - #marker_name: &(), - } - } - - const IS_DENSE: bool = true #(&& <#field_types>::IS_DENSE)*; - - const IS_ARCHETYPAL: bool = true #(&& <#field_types>::IS_ARCHETYPAL)*; - - /// SAFETY: we call `set_archetype` for each member that implements `Fetch` - #[inline] - unsafe fn set_archetype<'__w>( - _fetch: &mut ::Fetch<'__w>, - _state: &Self::State, - _archetype: &'__w #path::archetype::Archetype, - _table: &'__w #path::storage::Table - ) { - #(<#field_types>::set_archetype(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _archetype, _table);)* - } - - /// SAFETY: we call `set_table` for each member that implements `Fetch` - #[inline] - unsafe fn set_table<'__w>( - _fetch: &mut ::Fetch<'__w>, - _state: &Self::State, - _table: &'__w #path::storage::Table - ) { - #(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table);)* - } - - /// SAFETY: we call `fetch` for each member that implements `Fetch`. - #[inline(always)] - unsafe fn fetch<'__w>( - _fetch: &mut ::Fetch<'__w>, - _entity: #path::entity::Entity, - _table_row: #path::storage::TableRow, - ) -> ::Item<'__w> { - Self::Item { - #(#field_idents: <#field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)* - } - } - - #[allow(unused_variables)] - #[inline(always)] - unsafe fn filter_fetch<'__w>( - _fetch: &mut ::Fetch<'__w>, - _entity: #path::entity::Entity, - _table_row: #path::storage::TableRow, - ) -> bool { - true #(&& <#field_types>::filter_fetch(&mut _fetch.#named_field_idents, _entity, _table_row))* - } - - fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) { - #( <#field_types>::update_component_access(&state.#named_field_idents, _access); )* - } - - fn update_archetype_component_access( - state: &Self::State, - _archetype: &#path::archetype::Archetype, - _access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId> - ) { - #( - <#field_types>::update_archetype_component_access(&state.#named_field_idents, _archetype, _access); - )* - } - - fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics { - #state_struct_name { - #(#named_field_idents: <#field_types>::init_state(world),)* - } - } - - fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool { - true #(&& <#field_types>::matches_component_set(&state.#named_field_idents, _set_contains_id))* - } - } - }; - (item_struct, query_impl) - }; - - let (mutable_struct, mutable_impl) = impl_fetch(false); - let (read_only_struct, read_only_impl) = if fetch_struct_attributes.is_mutable { - let (readonly_state, read_only_impl) = impl_fetch(true); - let read_only_structs = quote! { - #[doc = "Automatically generated [`WorldQuery`] type for a read-only variant of [`"] - #[doc = stringify!(#struct_name)] - #[doc = "`]."] - #[automatically_derived] - #visibility struct #read_only_struct_name #user_impl_generics #user_where_clauses { - #( - #[doc = "Automatically generated read-only field for accessing `"] - #[doc = stringify!(#field_types)] - #[doc = "`."] - #field_visibilities #named_field_idents: #read_only_field_types, - )* - } - - #readonly_state - }; - (read_only_structs, read_only_impl) - } else { - (quote! {}, quote! {}) - }; - - let read_only_asserts = if fetch_struct_attributes.is_mutable { - quote! { - // Double-check that the data fetched by `<_ as WorldQuery>::ReadOnly` is read-only. - // This is technically unnecessary as `<_ as WorldQuery>::ReadOnly: ReadOnlyWorldQuery` - // but to protect against future mistakes we assert the assoc type implements `ReadOnlyWorldQuery` anyway - #( assert_readonly::<#read_only_field_types>(); )* - } - } else { - quote! { - // Statically checks that the safety guarantee of `ReadOnlyWorldQuery` for `$fetch_struct_name` actually holds true. - // We need this to make sure that we don't compile `ReadOnlyWorldQuery` if our struct contains nested `WorldQuery` - // members that don't implement it. I.e.: - // ``` - // #[derive(WorldQuery)] - // pub struct Foo { a: &'static mut MyComponent } - // ``` - #( assert_readonly::<#field_types>(); )* - } - }; - - TokenStream::from(quote! { - #mutable_struct - - #read_only_struct - - /// SAFETY: we assert fields are readonly below - unsafe impl #user_impl_generics #path::query::ReadOnlyWorldQuery - for #read_only_struct_name #user_ty_generics #user_where_clauses {} - - const _: () = { - #[doc(hidden)] - #[doc = "Automatically generated internal [`WorldQuery`] state type for [`"] - #[doc = stringify!(#struct_name)] - #[doc = "`], used for caching."] - #[automatically_derived] - #visibility struct #state_struct_name #user_impl_generics #user_where_clauses { - #(#named_field_idents: <#field_types as #path::query::WorldQuery>::State,)* - } - - #mutable_impl - - #read_only_impl - }; - - #[allow(dead_code)] - const _: () = { - fn assert_readonly() - where - T: #path::query::ReadOnlyWorldQuery, - { - } - - // We generate a readonly assertion for every struct member. - fn assert_all #user_impl_generics_with_world () #user_where_clauses_with_world { - #read_only_asserts - } - }; - - // The original struct will most likely be left unused. As we don't want our users having - // to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed - // workaround. - #[allow(dead_code)] - const _: () = { - fn dead_code_workaround #user_impl_generics ( - q: #struct_name #user_ty_generics, - q2: #read_only_struct_name #user_ty_generics - ) #user_where_clauses { - #(q.#field_idents;)* - #(q2.#field_idents;)* - } - }; - }) -} - -struct WorldQueryFieldInfo { - /// All field attributes except for `world_query` ones. - attrs: Vec, -} - -fn read_world_query_field_info(field: &Field) -> syn::Result { - let mut attrs = Vec::new(); - for attr in &field.attrs { - if attr - .path() - .get_ident() - .map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME) - { - return Err(syn::Error::new_spanned( - attr, - "#[derive(WorldQuery)] does not support field attributes.", - )); - } - attrs.push(attr.clone()); - } - - Ok(WorldQueryFieldInfo { attrs }) -} diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index ecb3a57c9c..a27480b5b1 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -1,10 +1,15 @@ extern crate proc_macro; mod component; -mod fetch; mod states; +mod world_query; +mod world_query_data; +mod world_query_filter; -use crate::fetch::derive_world_query_impl; +use crate::{ + world_query_data::derive_world_query_data_impl, + world_query_filter::derive_world_query_filter_impl, +}; use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest}; use proc_macro::TokenStream; use proc_macro2::Span; @@ -445,10 +450,16 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { }) } -/// Implement `WorldQuery` to use a struct as a parameter in a query -#[proc_macro_derive(WorldQuery, attributes(world_query))] -pub fn derive_world_query(input: TokenStream) -> TokenStream { - derive_world_query_impl(input) +/// Implement `WorldQueryData` to use a struct as a data parameter in a query +#[proc_macro_derive(WorldQueryData, attributes(world_query_data))] +pub fn derive_world_query_data(input: TokenStream) -> TokenStream { + derive_world_query_data_impl(input) +} + +/// Implement `WorldQueryFilter` to use a struct as a filter parameter in a query +#[proc_macro_derive(WorldQueryFilter, attributes(world_query_filter))] +pub fn derive_world_query_filter(input: TokenStream) -> TokenStream { + derive_world_query_filter_impl(input) } /// Derive macro generating an impl of the trait `ScheduleLabel`. diff --git a/crates/bevy_ecs/macros/src/world_query.rs b/crates/bevy_ecs/macros/src/world_query.rs new file mode 100644 index 0000000000..1e9e3f847a --- /dev/null +++ b/crates/bevy_ecs/macros/src/world_query.rs @@ -0,0 +1,189 @@ +use proc_macro2::Ident; +use quote::quote; +use syn::{Attribute, Fields, ImplGenerics, TypeGenerics, Visibility, WhereClause}; + +#[allow(clippy::too_many_arguments)] +pub(crate) fn item_struct( + path: &syn::Path, + fields: &Fields, + derive_macro_call: &proc_macro2::TokenStream, + struct_name: &Ident, + visibility: &Visibility, + item_struct_name: &Ident, + field_types: &Vec, + user_impl_generics_with_world: &ImplGenerics, + field_attrs: &Vec>, + field_visibilities: &Vec, + field_idents: &Vec, + user_ty_generics: &TypeGenerics, + user_ty_generics_with_world: &TypeGenerics, + user_where_clauses_with_world: Option<&WhereClause>, +) -> proc_macro2::TokenStream { + let item_attrs = quote!( + #[doc = "Automatically generated [`WorldQuery`] item type for [`"] + #[doc = stringify!(#struct_name)] + #[doc = "`], returned when iterating over query results."] + #[automatically_derived] + ); + + match fields { + syn::Fields::Named(_) => quote! { + #derive_macro_call + #item_attrs + #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { + #(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::WorldQuery>::Item<'__w>,)* + } + }, + syn::Fields::Unnamed(_) => quote! { + #derive_macro_call + #item_attrs + #[automatically_derived] + #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world( + #( #field_visibilities <#field_types as #path::query::WorldQuery>::Item<'__w>, )* + ); + }, + syn::Fields::Unit => quote! { + #item_attrs + #visibility type #item_struct_name #user_ty_generics_with_world = #struct_name #user_ty_generics; + }, + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn world_query_impl( + path: &syn::Path, + struct_name: &Ident, + visibility: &Visibility, + item_struct_name: &Ident, + fetch_struct_name: &Ident, + field_types: &Vec, + user_impl_generics: &ImplGenerics, + user_impl_generics_with_world: &ImplGenerics, + field_idents: &Vec, + user_ty_generics: &TypeGenerics, + user_ty_generics_with_world: &TypeGenerics, + named_field_idents: &Vec, + marker_name: &Ident, + state_struct_name: &Ident, + user_where_clauses: Option<&WhereClause>, + user_where_clauses_with_world: Option<&WhereClause>, +) -> proc_macro2::TokenStream { + quote! { + #[doc(hidden)] + #[doc = "Automatically generated internal [`WorldQuery`] fetch type for [`"] + #[doc = stringify!(#struct_name)] + #[doc = "`], used to define the world data accessed by this query."] + #[automatically_derived] + #visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { + #(#named_field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)* + #marker_name: &'__w (), + } + + impl #user_impl_generics_with_world Clone for #fetch_struct_name #user_ty_generics_with_world + #user_where_clauses_with_world { + fn clone(&self) -> Self { + Self { + #(#named_field_idents: self.#named_field_idents.clone(),)* + #marker_name: &(), + } + } + } + + // SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field + unsafe impl #user_impl_generics #path::query::WorldQuery + for #struct_name #user_ty_generics #user_where_clauses { + + type Item<'__w> = #item_struct_name #user_ty_generics_with_world; + type Fetch<'__w> = #fetch_struct_name #user_ty_generics_with_world; + type State = #state_struct_name #user_ty_generics; + + fn shrink<'__wlong: '__wshort, '__wshort>( + item: <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wlong> + ) -> <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wshort> { + #item_struct_name { + #( + #field_idents: <#field_types>::shrink(item.#field_idents), + )* + } + } + + unsafe fn init_fetch<'__w>( + _world: #path::world::unsafe_world_cell::UnsafeWorldCell<'__w>, + state: &Self::State, + _last_run: #path::component::Tick, + _this_run: #path::component::Tick, + ) -> ::Fetch<'__w> { + #fetch_struct_name { + #(#named_field_idents: + <#field_types>::init_fetch( + _world, + &state.#named_field_idents, + _last_run, + _this_run, + ), + )* + #marker_name: &(), + } + } + + const IS_DENSE: bool = true #(&& <#field_types>::IS_DENSE)*; + + /// SAFETY: we call `set_archetype` for each member that implements `Fetch` + #[inline] + unsafe fn set_archetype<'__w>( + _fetch: &mut ::Fetch<'__w>, + _state: &Self::State, + _archetype: &'__w #path::archetype::Archetype, + _table: &'__w #path::storage::Table + ) { + #(<#field_types>::set_archetype(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _archetype, _table);)* + } + + /// SAFETY: we call `set_table` for each member that implements `Fetch` + #[inline] + unsafe fn set_table<'__w>( + _fetch: &mut ::Fetch<'__w>, + _state: &Self::State, + _table: &'__w #path::storage::Table + ) { + #(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table);)* + } + + /// SAFETY: we call `fetch` for each member that implements `Fetch`. + #[inline(always)] + unsafe fn fetch<'__w>( + _fetch: &mut ::Fetch<'__w>, + _entity: #path::entity::Entity, + _table_row: #path::storage::TableRow, + ) -> ::Item<'__w> { + Self::Item { + #(#field_idents: <#field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)* + } + } + + fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) { + #( <#field_types>::update_component_access(&state.#named_field_idents, _access); )* + } + + fn update_archetype_component_access( + state: &Self::State, + _archetype: &#path::archetype::Archetype, + _access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId> + ) { + #( + <#field_types>::update_archetype_component_access(&state.#named_field_idents, _archetype, _access); + )* + } + + fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics { + #state_struct_name { + #(#named_field_idents: <#field_types>::init_state(world),)* + } + } + + fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool { + true #(&& <#field_types>::matches_component_set(&state.#named_field_idents, _set_contains_id))* + } + } + } +} diff --git a/crates/bevy_ecs/macros/src/world_query_data.rs b/crates/bevy_ecs/macros/src/world_query_data.rs new file mode 100644 index 0000000000..c1bd078389 --- /dev/null +++ b/crates/bevy_ecs/macros/src/world_query_data.rs @@ -0,0 +1,410 @@ +use bevy_macro_utils::ensure_no_collision; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::{format_ident, quote, ToTokens}; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, parse_quote, + punctuated::Punctuated, + token::Comma, + Attribute, Data, DataStruct, DeriveInput, Field, Index, Meta, +}; + +use crate::{ + bevy_ecs_path, + world_query::{item_struct, world_query_impl}, +}; + +#[derive(Default)] +struct WorldQueryDataAttributes { + pub is_mutable: bool, + + pub derive_args: Punctuated, +} + +static MUTABLE_ATTRIBUTE_NAME: &str = "mutable"; +static DERIVE_ATTRIBUTE_NAME: &str = "derive"; + +mod field_attr_keywords { + syn::custom_keyword!(ignore); +} + +pub static WORLD_QUERY_DATA_ATTRIBUTE_NAME: &str = "world_query_data"; + +pub fn derive_world_query_data_impl(input: TokenStream) -> TokenStream { + let tokens = input.clone(); + + let ast = parse_macro_input!(input as DeriveInput); + let visibility = ast.vis; + + let mut attributes = WorldQueryDataAttributes::default(); + for attr in &ast.attrs { + if !attr + .path() + .get_ident() + .map_or(false, |ident| ident == WORLD_QUERY_DATA_ATTRIBUTE_NAME) + { + continue; + } + + attr.parse_args_with(|input: ParseStream| { + let meta = input.parse_terminated(syn::Meta::parse, Comma)?; + for meta in meta { + let ident = meta.path().get_ident().unwrap_or_else(|| { + panic!( + "Unrecognized attribute: `{}`", + meta.path().to_token_stream() + ) + }); + if ident == MUTABLE_ATTRIBUTE_NAME { + if let syn::Meta::Path(_) = meta { + attributes.is_mutable = true; + } else { + panic!( + "The `{MUTABLE_ATTRIBUTE_NAME}` attribute is expected to have no value or arguments", + ); + } + } + else if ident == DERIVE_ATTRIBUTE_NAME { + if let syn::Meta::List(meta_list) = meta { + meta_list.parse_nested_meta(|meta| { + attributes.derive_args.push(Meta::Path(meta.path)); + Ok(()) + })?; + } else { + panic!( + "Expected a structured list within the `{DERIVE_ATTRIBUTE_NAME}` attribute", + ); + } + } else { + panic!( + "Unrecognized attribute: `{}`", + meta.path().to_token_stream() + ); + } + } + Ok(()) + }) + .unwrap_or_else(|_| panic!("Invalid `{WORLD_QUERY_DATA_ATTRIBUTE_NAME}` attribute format")); + } + + let path = bevy_ecs_path(); + + let user_generics = ast.generics.clone(); + let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl(); + let user_generics_with_world = { + let mut generics = ast.generics; + generics.params.insert(0, parse_quote!('__w)); + generics + }; + let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) = + user_generics_with_world.split_for_impl(); + + let struct_name = ast.ident; + let read_only_struct_name = if attributes.is_mutable { + Ident::new(&format!("{struct_name}ReadOnly"), Span::call_site()) + } else { + #[allow(clippy::redundant_clone)] + struct_name.clone() + }; + + let item_struct_name = Ident::new(&format!("{struct_name}Item"), Span::call_site()); + let read_only_item_struct_name = if attributes.is_mutable { + Ident::new(&format!("{struct_name}ReadOnlyItem"), Span::call_site()) + } else { + #[allow(clippy::redundant_clone)] + item_struct_name.clone() + }; + + let fetch_struct_name = Ident::new(&format!("{struct_name}Fetch"), Span::call_site()); + let fetch_struct_name = ensure_no_collision(fetch_struct_name, tokens.clone()); + let read_only_fetch_struct_name = if attributes.is_mutable { + let new_ident = Ident::new(&format!("{struct_name}ReadOnlyFetch"), Span::call_site()); + ensure_no_collision(new_ident, tokens.clone()) + } else { + #[allow(clippy::redundant_clone)] + fetch_struct_name.clone() + }; + + let marker_name = + ensure_no_collision(format_ident!("_world_query_derive_marker"), tokens.clone()); + + // Generate a name for the state struct that doesn't conflict + // with the struct definition. + let state_struct_name = Ident::new(&format!("{struct_name}State"), Span::call_site()); + let state_struct_name = ensure_no_collision(state_struct_name, tokens); + + let Data::Struct(DataStruct { fields, .. }) = &ast.data else { + return syn::Error::new( + Span::call_site(), + "#[derive(WorldQueryData)]` only supports structs", + ) + .into_compile_error() + .into(); + }; + + let mut field_attrs = Vec::new(); + let mut field_visibilities = Vec::new(); + let mut field_idents = Vec::new(); + let mut named_field_idents = Vec::new(); + let mut field_types = Vec::new(); + let mut read_only_field_types = Vec::new(); + for (i, field) in fields.iter().enumerate() { + let attrs = match read_world_query_field_info(field) { + Ok(WorldQueryDataFieldInfo { attrs }) => attrs, + Err(e) => return e.into_compile_error().into(), + }; + + let named_field_ident = field + .ident + .as_ref() + .cloned() + .unwrap_or_else(|| format_ident!("f{i}")); + let i = Index::from(i); + let field_ident = field + .ident + .as_ref() + .map_or(quote! { #i }, |i| quote! { #i }); + field_idents.push(field_ident); + named_field_idents.push(named_field_ident); + field_attrs.push(attrs); + field_visibilities.push(field.vis.clone()); + let field_ty = field.ty.clone(); + field_types.push(quote!(#field_ty)); + read_only_field_types.push(quote!(<#field_ty as #path::query::WorldQueryData>::ReadOnly)); + } + + let derive_args = &attributes.derive_args; + // `#[derive()]` is valid syntax + let derive_macro_call = quote! { #[derive(#derive_args)] }; + + let mutable_item_struct = item_struct( + &path, + fields, + &derive_macro_call, + &struct_name, + &visibility, + &item_struct_name, + &field_types, + &user_impl_generics_with_world, + &field_attrs, + &field_visibilities, + &field_idents, + &user_ty_generics, + &user_ty_generics_with_world, + user_where_clauses_with_world, + ); + let mutable_world_query_impl = world_query_impl( + &path, + &struct_name, + &visibility, + &item_struct_name, + &fetch_struct_name, + &field_types, + &user_impl_generics, + &user_impl_generics_with_world, + &field_idents, + &user_ty_generics, + &user_ty_generics_with_world, + &named_field_idents, + &marker_name, + &state_struct_name, + user_where_clauses, + user_where_clauses_with_world, + ); + + let (read_only_struct, read_only_impl) = if attributes.is_mutable { + // If the query is mutable, we need to generate a separate readonly version of some things + let readonly_item_struct = item_struct( + &path, + fields, + &derive_macro_call, + &read_only_struct_name, + &visibility, + &read_only_item_struct_name, + &read_only_field_types, + &user_impl_generics_with_world, + &field_attrs, + &field_visibilities, + &field_idents, + &user_ty_generics, + &user_ty_generics_with_world, + user_where_clauses_with_world, + ); + let readonly_world_query_impl = world_query_impl( + &path, + &read_only_struct_name, + &visibility, + &read_only_item_struct_name, + &read_only_fetch_struct_name, + &read_only_field_types, + &user_impl_generics, + &user_impl_generics_with_world, + &field_idents, + &user_ty_generics, + &user_ty_generics_with_world, + &named_field_idents, + &marker_name, + &state_struct_name, + user_where_clauses, + user_where_clauses_with_world, + ); + let read_only_structs = quote! { + #[doc = "Automatically generated [`WorldQuery`] type for a read-only variant of [`"] + #[doc = stringify!(#struct_name)] + #[doc = "`]."] + #[automatically_derived] + #visibility struct #read_only_struct_name #user_impl_generics #user_where_clauses { + #( + #[doc = "Automatically generated read-only field for accessing `"] + #[doc = stringify!(#field_types)] + #[doc = "`."] + #field_visibilities #named_field_idents: #read_only_field_types, + )* + } + + #readonly_item_struct + }; + (read_only_structs, readonly_world_query_impl) + } else { + (quote! {}, quote! {}) + }; + + let data_impl = { + let read_only_data_impl = if attributes.is_mutable { + quote! { + /// SAFETY: we assert fields are readonly below + unsafe impl #user_impl_generics #path::query::WorldQueryData + for #read_only_struct_name #user_ty_generics #user_where_clauses { + type ReadOnly = #read_only_struct_name #user_ty_generics; + } + } + } else { + quote! {} + }; + + quote! { + /// SAFETY: we assert fields are readonly below + unsafe impl #user_impl_generics #path::query::WorldQueryData + for #struct_name #user_ty_generics #user_where_clauses { + type ReadOnly = #read_only_struct_name #user_ty_generics; + } + + #read_only_data_impl + } + }; + + let read_only_data_impl = quote! { + /// SAFETY: we assert fields are readonly below + unsafe impl #user_impl_generics #path::query::ReadOnlyWorldQueryData + for #read_only_struct_name #user_ty_generics #user_where_clauses {} + }; + + let read_only_asserts = if attributes.is_mutable { + quote! { + // Double-check that the data fetched by `<_ as WorldQuery>::ReadOnly` is read-only. + // This is technically unnecessary as `<_ as WorldQuery>::ReadOnly: ReadOnlyWorldQueryData` + // but to protect against future mistakes we assert the assoc type implements `ReadOnlyWorldQueryData` anyway + #( assert_readonly::<#read_only_field_types>(); )* + } + } else { + quote! { + // Statically checks that the safety guarantee of `ReadOnlyWorldQueryData` for `$fetch_struct_name` actually holds true. + // We need this to make sure that we don't compile `ReadOnlyWorldQueryData` if our struct contains nested `WorldQueryData` + // members that don't implement it. I.e.: + // ``` + // #[derive(WorldQueryData)] + // pub struct Foo { a: &'static mut MyComponent } + // ``` + #( assert_readonly::<#field_types>(); )* + } + }; + + let data_asserts = quote! { + #( assert_data::<#field_types>(); )* + }; + + TokenStream::from(quote! { + #mutable_item_struct + + #read_only_struct + + const _: () = { + #[doc(hidden)] + #[doc = "Automatically generated internal [`WorldQuery`] state type for [`"] + #[doc = stringify!(#struct_name)] + #[doc = "`], used for caching."] + #[automatically_derived] + #visibility struct #state_struct_name #user_impl_generics #user_where_clauses { + #(#named_field_idents: <#field_types as #path::query::WorldQuery>::State,)* + } + + #mutable_world_query_impl + + #read_only_impl + + #data_impl + + #read_only_data_impl + }; + + #[allow(dead_code)] + const _: () = { + fn assert_readonly() + where + T: #path::query::ReadOnlyWorldQueryData, + { + } + + fn assert_data() + where + T: #path::query::WorldQueryData, + { + } + + // We generate a readonly assertion for every struct member. + fn assert_all #user_impl_generics_with_world () #user_where_clauses_with_world { + #read_only_asserts + #data_asserts + } + }; + + // The original struct will most likely be left unused. As we don't want our users having + // to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed + // workaround. + #[allow(dead_code)] + const _: () = { + fn dead_code_workaround #user_impl_generics ( + q: #struct_name #user_ty_generics, + q2: #read_only_struct_name #user_ty_generics + ) #user_where_clauses { + #(q.#field_idents;)* + #(q2.#field_idents;)* + } + }; + }) +} + +struct WorldQueryDataFieldInfo { + /// All field attributes except for `world_query_data` ones. + attrs: Vec, +} + +fn read_world_query_field_info(field: &Field) -> syn::Result { + let mut attrs = Vec::new(); + for attr in &field.attrs { + if attr + .path() + .get_ident() + .map_or(false, |ident| ident == WORLD_QUERY_DATA_ATTRIBUTE_NAME) + { + return Err(syn::Error::new_spanned( + attr, + "#[derive(WorldQueryData)] does not support field attributes.", + )); + } + attrs.push(attr.clone()); + } + + Ok(WorldQueryDataFieldInfo { attrs }) +} diff --git a/crates/bevy_ecs/macros/src/world_query_filter.rs b/crates/bevy_ecs/macros/src/world_query_filter.rs new file mode 100644 index 0000000000..b10cac09a0 --- /dev/null +++ b/crates/bevy_ecs/macros/src/world_query_filter.rs @@ -0,0 +1,190 @@ +use bevy_macro_utils::ensure_no_collision; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, parse_quote, Data, DataStruct, DeriveInput, Index}; + +use crate::{ + bevy_ecs_path, + world_query::{item_struct, world_query_impl}, +}; + +mod field_attr_keywords { + syn::custom_keyword!(ignore); +} + +pub fn derive_world_query_filter_impl(input: TokenStream) -> TokenStream { + let tokens = input.clone(); + + let ast = parse_macro_input!(input as DeriveInput); + let visibility = ast.vis; + + let path = bevy_ecs_path(); + + let user_generics = ast.generics.clone(); + let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl(); + let user_generics_with_world = { + let mut generics = ast.generics; + generics.params.insert(0, parse_quote!('__w)); + generics + }; + let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) = + user_generics_with_world.split_for_impl(); + + let struct_name = ast.ident; + + let item_struct_name = Ident::new(&format!("{struct_name}Item"), Span::call_site()); + + let fetch_struct_name = Ident::new(&format!("{struct_name}Fetch"), Span::call_site()); + let fetch_struct_name = ensure_no_collision(fetch_struct_name, tokens.clone()); + + let marker_name = + ensure_no_collision(format_ident!("_world_query_derive_marker"), tokens.clone()); + + // Generate a name for the state struct that doesn't conflict + // with the struct definition. + let state_struct_name = Ident::new(&format!("{struct_name}State"), Span::call_site()); + let state_struct_name = ensure_no_collision(state_struct_name, tokens); + + let Data::Struct(DataStruct { fields, .. }) = &ast.data else { + return syn::Error::new( + Span::call_site(), + "#[derive(WorldQuery)]` only supports structs", + ) + .into_compile_error() + .into(); + }; + + let mut field_attrs = Vec::new(); + let mut field_visibilities = Vec::new(); + let mut field_idents = Vec::new(); + let mut named_field_idents = Vec::new(); + let mut field_types = Vec::new(); + for (i, field) in fields.iter().enumerate() { + let attrs = field.attrs.clone(); + + let named_field_ident = field + .ident + .as_ref() + .cloned() + .unwrap_or_else(|| format_ident!("f{i}")); + let i = Index::from(i); + let field_ident = field + .ident + .as_ref() + .map_or(quote! { #i }, |i| quote! { #i }); + field_idents.push(field_ident); + named_field_idents.push(named_field_ident); + field_attrs.push(attrs); + field_visibilities.push(field.vis.clone()); + let field_ty = field.ty.clone(); + field_types.push(quote!(#field_ty)); + } + + let derive_macro_call = quote!(); + + let item_struct = item_struct( + &path, + fields, + &derive_macro_call, + &struct_name, + &visibility, + &item_struct_name, + &field_types, + &user_impl_generics_with_world, + &field_attrs, + &field_visibilities, + &field_idents, + &user_ty_generics, + &user_ty_generics_with_world, + user_where_clauses_with_world, + ); + + let world_query_impl = world_query_impl( + &path, + &struct_name, + &visibility, + &item_struct_name, + &fetch_struct_name, + &field_types, + &user_impl_generics, + &user_impl_generics_with_world, + &field_idents, + &user_ty_generics, + &user_ty_generics_with_world, + &named_field_idents, + &marker_name, + &state_struct_name, + user_where_clauses, + user_where_clauses_with_world, + ); + + let filter_impl = quote! { + impl #user_impl_generics #path::query::WorldQueryFilter + for #struct_name #user_ty_generics #user_where_clauses { + const IS_ARCHETYPAL: bool = true #(&& <#field_types>::IS_ARCHETYPAL)*; + + #[allow(unused_variables)] + #[inline(always)] + unsafe fn filter_fetch<'__w>( + _fetch: &mut ::Fetch<'__w>, + _entity: #path::entity::Entity, + _table_row: #path::storage::TableRow, + ) -> bool { + true #(&& <#field_types>::filter_fetch(&mut _fetch.#named_field_idents, _entity, _table_row))* + } + } + }; + + let filter_asserts = quote! { + #( assert_filter::<#field_types>(); )* + }; + + TokenStream::from(quote! { + #item_struct + + const _: () = { + #[doc(hidden)] + #[doc = "Automatically generated internal [`WorldQuery`] state type for [`"] + #[doc = stringify!(#struct_name)] + #[doc = "`], used for caching."] + #[automatically_derived] + #visibility struct #state_struct_name #user_impl_generics #user_where_clauses { + #(#named_field_idents: <#field_types as #path::query::WorldQuery>::State,)* + } + + #world_query_impl + + #filter_impl + }; + + #[allow(dead_code)] + const _: () = { + + fn assert_filter() + where + T: #path::query::WorldQueryFilter, + { + } + + // We generate a filter assertion for every struct member. + fn assert_all #user_impl_generics_with_world () #user_where_clauses_with_world { + #filter_asserts + } + }; + + // The original struct will most likely be left unused. As we don't want our users having + // to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed + // workaround. + #[allow(dead_code)] + const _: () = { + fn dead_code_workaround #user_impl_generics ( + q: #struct_name #user_ty_generics, + q2: #struct_name #user_ty_generics + ) #user_where_clauses { + #(q.#field_idents;)* + #(q2.#field_idents;)* + } + }; + }) +} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index ad8ba9b39a..69ce32e8a8 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -64,7 +64,7 @@ mod tests { change_detection::Ref, component::{Component, ComponentId}, entity::Entity, - query::{Added, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without}, + query::{Added, Changed, FilteredAccess, With, Without, WorldQueryFilter}, system::Resource, world::{EntityRef, Mut, World}, }; @@ -903,7 +903,7 @@ mod tests { } } - fn get_filtered(world: &mut World) -> Vec { + fn get_filtered(world: &mut World) -> Vec { world .query_filtered::() .iter(world) @@ -986,7 +986,7 @@ mod tests { } } - fn get_filtered(world: &mut World) -> Vec { + fn get_filtered(world: &mut World) -> Vec { world .query_filtered::() .iter(world) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 1f7fe29ec7..f322c5d145 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -3,11 +3,10 @@ use crate::{ change_detection::{Ticks, TicksMut}, component::{Component, ComponentId, ComponentStorage, StorageType, Tick}, entity::Entity, - query::{Access, DebugCheckedUnwrap, FilteredAccess}, + query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, TableRow}, world::{unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityRef, Mut, Ref, World}, }; -pub use bevy_ecs_macros::WorldQuery; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; use bevy_utils::all_tuples; use std::{cell::UnsafeCell, marker::PhantomData}; @@ -18,18 +17,11 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// /// - **Component references.** /// Fetches a component by reference (immutably or mutably). -/// - **`WorldQuery` tuples.** -/// If every element of a tuple implements `WorldQuery`, then the tuple itself also implements the same trait. -/// This enables a single `Query` to access multiple components and filter over multiple conditions. +/// - **`WorldQueryData` tuples.** +/// If every element of a tuple implements `WorldQueryData`, then the tuple itself also implements the same trait. +/// This enables a single `Query` to access multiple components. /// Due to the current lack of variadic generics in Rust, the trait has been implemented for tuples from 0 to 15 elements, /// but nesting of tuples allows infinite `WorldQuery`s. -/// - **Component filters.** -/// [`With`] and [`Without`] filters can be applied to check if the queried entity contains or not a particular component. -/// - **Change detection filters.** -/// [`Added`] and [`Changed`] filters can be applied to detect component changes to an entity. -/// - **Filter disjunction operator.** -/// By default, tuples compose query filters in such a way that all conditions must be satisfied to generate a query item for a given entity. -/// Wrapping a tuple inside an [`Or`] operator will relax the requirement to just one condition. /// - **[`Entity`].** /// Gets the identifier of the queried entity. /// - **[`Option`].** @@ -40,13 +32,15 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// - **[`Ref`].** /// Similar to change detection filters but it is used as a query fetch parameter. /// It exposes methods to check for changes to the wrapped component. +/// - **[`Has`].** +/// Returns a bool indicating whether the entity has the specified component. /// /// Implementing the trait manually can allow for a fundamentally new type of behavior. /// /// # Trait derivation /// -/// Query design can be easily structured by deriving `WorldQuery` for custom types. -/// Despite the added complexity, this approach has several advantages over using `WorldQuery` tuples. +/// Query design can be easily structured by deriving `WorldQueryData` for custom types. +/// Despite the added complexity, this approach has several advantages over using `WorldQueryData` tuples. /// The most relevant improvements are: /// /// - Reusability across multiple systems. @@ -55,18 +49,18 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// - Methods can be implemented for the query items. /// - There is no hardcoded limit on the number of elements. /// -/// This trait can only be derived for structs, if each field also implements `WorldQuery`. +/// This trait can only be derived for structs, if each field also implements `WorldQueryData`. /// /// ``` /// # use bevy_ecs::prelude::*; -/// use bevy_ecs::query::WorldQuery; +/// use bevy_ecs::query::WorldQueryData; /// # /// # #[derive(Component)] /// # struct ComponentA; /// # #[derive(Component)] /// # struct ComponentB; /// -/// #[derive(WorldQuery)] +/// #[derive(WorldQueryData)] /// struct MyQuery { /// entity: Entity, /// // It is required that all reference lifetimes are explicitly annotated, just like in any @@ -96,33 +90,33 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// /// ## Adding mutable references /// -/// Simply adding mutable references to a derived `WorldQuery` will result in a compilation error: +/// Simply adding mutable references to a derived `WorldQueryData` will result in a compilation error: /// /// ```compile_fail /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::query::WorldQuery; +/// # use bevy_ecs::query::WorldQueryData; /// # /// # #[derive(Component)] /// # struct ComponentA; /// # -/// #[derive(WorldQuery)] +/// #[derive(WorldQueryData)] /// struct CustomQuery { /// component_a: &'static mut ComponentA, /// } /// ``` /// -/// To grant mutable access to components, the struct must be marked with the `#[world_query(mutable)]` attribute. +/// To grant mutable access to components, the struct must be marked with the `#[world_query_data(mutable)]` attribute. /// This will also create three more structs that will be used for accessing the query immutably (see table above). /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::query::WorldQuery; +/// # use bevy_ecs::query::WorldQueryData; /// # /// # #[derive(Component)] /// # struct ComponentA; /// # -/// #[derive(WorldQuery)] -/// #[world_query(mutable)] +/// #[derive(WorldQueryData)] +/// #[world_query_data(mutable)] /// struct CustomQuery { /// component_a: &'static mut ComponentA, /// } @@ -136,7 +130,7 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::query::WorldQuery; +/// # use bevy_ecs::query::WorldQueryData; /// # /// #[derive(Component)] /// struct Health(f32); @@ -144,8 +138,8 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// #[derive(Component)] /// struct Buff(f32); /// -/// #[derive(WorldQuery)] -/// #[world_query(mutable)] +/// #[derive(WorldQueryData)] +/// #[world_query_data(mutable)] /// struct HealthQuery { /// health: &'static mut Health, /// buff: Option<&'static mut Buff>, @@ -185,19 +179,19 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// /// ## Deriving traits for query items /// -/// The `WorldQuery` derive macro does not automatically implement the traits of the struct to the query item types. -/// Something similar can be done by using the `#[world_query(derive(...))]` attribute. +/// The `WorldQueryData` derive macro does not automatically implement the traits of the struct to the query item types. +/// Something similar can be done by using the `#[world_query_data(derive(...))]` attribute. /// This will apply the listed derivable traits to the query item structs. /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::query::WorldQuery; +/// # use bevy_ecs::query::WorldQueryData; /// # /// # #[derive(Component, Debug)] /// # struct ComponentA; /// # -/// #[derive(WorldQuery)] -/// #[world_query(mutable, derive(Debug))] +/// #[derive(WorldQueryData)] +/// #[world_query_data(mutable, derive(Debug))] /// struct CustomQuery { /// component_a: &'static ComponentA, /// } @@ -211,12 +205,12 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// /// ## Query composition /// -/// It is possible to use any `WorldQuery` as a field of another one. -/// This means that a `WorldQuery` can also be used as a subquery, potentially in multiple places. +/// It is possible to use any `WorldQueryData` as a field of another one. +/// This means that a `WorldQueryData` can also be used as a subquery, potentially in multiple places. /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::query::WorldQuery; +/// # use bevy_ecs::query::WorldQueryData; /// # /// # #[derive(Component)] /// # struct ComponentA; @@ -225,62 +219,29 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// # #[derive(Component)] /// # struct ComponentC; /// # -/// #[derive(WorldQuery)] +/// #[derive(WorldQueryData)] /// struct SubQuery { /// component_a: &'static ComponentA, /// component_b: &'static ComponentB, /// } /// -/// #[derive(WorldQuery)] +/// #[derive(WorldQueryData)] /// struct MyQuery { /// subquery: SubQuery, /// component_c: &'static ComponentC, /// } /// ``` /// -/// ## Filters -/// -/// Since the query filter type parameter is `WorldQuery`, it is also possible to use this macro to create filters. -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::{query::WorldQuery, component::Component}; -/// # -/// # #[derive(Component)] -/// # struct ComponentA; -/// # #[derive(Component)] -/// # struct ComponentB; -/// # #[derive(Component)] -/// # struct ComponentC; -/// # #[derive(Component)] -/// # struct ComponentD; -/// # #[derive(Component)] -/// # struct ComponentE; -/// # -/// #[derive(WorldQuery)] -/// struct MyFilter { -/// // Field names are not relevant, since they are never manually accessed. -/// with_a: With, -/// or_filter: Or<(With, Added)>, -/// generic_tuple: (With, Without

), -/// } -/// -/// fn my_system(query: Query>) { -/// // ... -/// } -/// # bevy_ecs::system::assert_is_system(my_system); -/// ``` -/// /// # Generic Queries /// /// When writing generic code, it is often necessary to use [`PhantomData`] -/// to constrain type parameters. Since `WorldQuery` is implemented for all +/// to constrain type parameters. Since `WorldQueryData` is implemented for all /// `PhantomData` types, this pattern can be used with this macro. /// /// ``` -/// # use bevy_ecs::{prelude::*, query::WorldQuery}; +/// # use bevy_ecs::{prelude::*, query::WorldQueryData}; /// # use std::marker::PhantomData; -/// #[derive(WorldQuery)] +/// #[derive(WorldQueryData)] /// pub struct GenericQuery { /// id: Entity, /// marker: PhantomData, @@ -294,176 +255,31 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// Component access of `Self::ReadOnly` must be a subset of `Self` /// and `Self::ReadOnly` must match exactly the same archetypes/tables as `Self` /// -/// Implementor must ensure that -/// [`update_component_access`] and [`update_archetype_component_access`] -/// exactly reflects the results of the following methods: -/// -/// - [`matches_component_set`] -/// - [`fetch`] -/// -/// [`Added`]: crate::query::Added -/// [`fetch`]: Self::fetch -/// [`Changed`]: crate::query::Changed -/// [`matches_component_set`]: Self::matches_component_set -/// [`Or`]: crate::query::Or /// [`Query`]: crate::system::Query /// [`ReadOnly`]: Self::ReadOnly -/// [`State`]: Self::State -/// [`update_archetype_component_access`]: Self::update_archetype_component_access -/// [`update_component_access`]: Self::update_component_access -/// [`With`]: crate::query::With -/// [`Without`]: crate::query::Without -pub unsafe trait WorldQuery { - /// The item returned by this [`WorldQuery`] - type Item<'a>; - - /// Per archetype/table state used by this [`WorldQuery`] to fetch [`Self::Item`](crate::query::WorldQuery::Item) - type Fetch<'a>: Clone; - - /// The read-only variant of this [`WorldQuery`], which satisfies the [`ReadOnlyWorldQuery`] trait. - type ReadOnly: ReadOnlyWorldQuery; - - /// State used to construct a [`Self::Fetch`](crate::query::WorldQuery::Fetch). This will be cached inside [`QueryState`](crate::query::QueryState), - /// so it is best to move as much data / computation here as possible to reduce the cost of - /// constructing [`Self::Fetch`](crate::query::WorldQuery::Fetch). - type State: Send + Sync + Sized; - - /// This function manually implements subtyping for the query items. - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort>; - - /// Creates a new instance of this fetch. - /// - /// # Safety - /// - /// - `world` must have permission to access any of the components specified in `Self::update_archetype_component_access`. - /// - `state` must have been initialized (via [`WorldQuery::init_state`]) using the same `world` passed - /// in to this function. - unsafe fn init_fetch<'w>( - world: UnsafeWorldCell<'w>, - state: &Self::State, - last_run: Tick, - this_run: Tick, - ) -> Self::Fetch<'w>; - - /// Returns true if (and only if) every table of every archetype matched by this fetch contains - /// all of the matched components. This is used to select a more efficient "table iterator" - /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before - /// [`WorldQuery::fetch`] can be called for iterators. If this returns false, - /// [`WorldQuery::set_archetype`] must be used before [`WorldQuery::fetch`] can be called for - /// iterators. - const IS_DENSE: bool; - - /// Returns true if (and only if) this Fetch relies strictly on archetypes to limit which - /// components are accessed by the Query. - /// - /// This enables optimizations for [`crate::query::QueryIter`] that rely on knowing exactly how - /// many elements are being iterated (such as `Iterator::collect()`). - const IS_ARCHETYPAL: bool; - - /// Adjusts internal state to account for the next [`Archetype`]. This will always be called on - /// archetypes that match this [`WorldQuery`]. - /// - /// # Safety - /// - /// - `archetype` and `tables` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. - /// - [`Self::update_archetype_component_access`] must have been previously called with `archetype`. - /// - `table` must correspond to `archetype`. - /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. - unsafe fn set_archetype<'w>( - fetch: &mut Self::Fetch<'w>, - state: &Self::State, - archetype: &'w Archetype, - table: &'w Table, - ); - - /// Adjusts internal state to account for the next [`Table`]. This will always be called on tables - /// that match this [`WorldQuery`]. - /// - /// # Safety - /// - /// - `table` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. - /// - `table` must belong to an archetype that was previously registered with - /// [`Self::update_archetype_component_access`]. - /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table); - - /// Fetch [`Self::Item`](`WorldQuery::Item`) for either the given `entity` in the current [`Table`], - /// or for the given `entity` in the current [`Archetype`]. This must always be called after - /// [`WorldQuery::set_table`] with a `table_row` in the range of the current [`Table`] or after - /// [`WorldQuery::set_archetype`] with a `entity` in the current archetype. - /// - /// # Safety - /// - /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and - /// `table_row` must be in the range of the current table and archetype. - /// - /// If `update_component_access` includes any mutable accesses, then the caller must ensure - /// that `fetch` is called no more than once for each `entity`/`table_row` in each archetype. - /// If `Self` implements [`ReadOnlyWorldQuery`], then this can safely be called multiple times. - unsafe fn fetch<'w>( - fetch: &mut Self::Fetch<'w>, - entity: Entity, - table_row: TableRow, - ) -> Self::Item<'w>; - - /// # Safety - /// - /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and - /// `table_row` must be in the range of the current table and archetype. - /// - /// If this includes any mutable access, then this should never be called - /// while the return value of [`WorldQuery::fetch`] for the same entity is live. - #[allow(unused_variables)] - #[inline(always)] - unsafe fn filter_fetch( - fetch: &mut Self::Fetch<'_>, - entity: Entity, - table_row: TableRow, - ) -> bool { - true - } - - /// Adds any component accesses used by this [`WorldQuery`] to `access`. - // This does not have a default body of `{}` because 99% of cases need to add accesses - // and forgetting to do so would be unsound. - fn update_component_access(state: &Self::State, access: &mut FilteredAccess); - - /// For the given `archetype`, adds any component accessed used by this [`WorldQuery`] to `access`. - // This does not have a default body of `{}` because 99% of cases need to add accesses - // and forgetting to do so would be unsound. - fn update_archetype_component_access( - state: &Self::State, - archetype: &Archetype, - access: &mut Access, - ); - - /// Creates and initializes a [`State`](WorldQuery::State) for this [`WorldQuery`] type. - fn init_state(world: &mut World) -> Self::State; - - /// Returns `true` if this query matches a set of components. Otherwise, returns `false`. - fn matches_component_set( - state: &Self::State, - set_contains_id: &impl Fn(ComponentId) -> bool, - ) -> bool; +pub unsafe trait WorldQueryData: WorldQuery { + /// The read-only variant of this [`WorldQueryData`], which satisfies the [`ReadOnlyWorldQueryData`] trait. + type ReadOnly: ReadOnlyWorldQueryData::State>; } -/// A world query that is read only. +/// A [`WorldQueryData`] that is read only. /// /// # Safety /// -/// This must only be implemented for read-only [`WorldQuery`]'s. -pub unsafe trait ReadOnlyWorldQuery: WorldQuery {} +/// This must only be implemented for read-only [`WorldQueryData`]'s. +pub unsafe trait ReadOnlyWorldQueryData: WorldQueryData {} /// The item type returned when a [`WorldQuery`] is iterated over pub type QueryItem<'w, Q> = ::Item<'w>; -/// The read-only variant of the item type returned when a [`WorldQuery`] is iterated over immutably -pub type ROQueryItem<'w, Q> = QueryItem<'w, ::ReadOnly>; +/// The read-only variant of the item type returned when a [`WorldQueryData`] is iterated over immutably +pub type ROQueryItem<'w, Q> = QueryItem<'w, ::ReadOnly>; -/// SAFETY: no component or archetype access +/// SAFETY: +/// `update_component_access` and `update_archetype_component_access` do nothing. +/// This is sound because `fetch` does not access components. unsafe impl WorldQuery for Entity { type Fetch<'w> = (); type Item<'w> = Entity; - type ReadOnly = Self; type State = (); fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -472,8 +288,6 @@ unsafe impl WorldQuery for Entity { const IS_DENSE: bool = true; - const IS_ARCHETYPAL: bool = true; - unsafe fn init_fetch<'w>( _world: UnsafeWorldCell<'w>, _state: &Self::State, @@ -523,14 +337,21 @@ unsafe impl WorldQuery for Entity { } } -/// SAFETY: access is read only -unsafe impl ReadOnlyWorldQuery for Entity {} - /// SAFETY: `Self` is the same as `Self::ReadOnly` -unsafe impl WorldQuery for EntityRef<'_> { +unsafe impl WorldQueryData for Entity { + type ReadOnly = Self; +} + +/// SAFETY: access is read only +unsafe impl ReadOnlyWorldQueryData for Entity {} + +/// SAFETY: +/// `fetch` accesses all components in a readonly way. +/// This is sound because `update_component_access` and `update_archetype_component_access` set read access for all components and panic when appropriate. +/// Filters are unchanged. +unsafe impl<'a> WorldQuery for EntityRef<'a> { type Fetch<'w> = UnsafeWorldCell<'w>; type Item<'w> = EntityRef<'w>; - type ReadOnly = Self; type State = (); fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -539,8 +360,6 @@ unsafe impl WorldQuery for EntityRef<'_> { const IS_DENSE: bool = true; - const IS_ARCHETYPAL: bool = true; - unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, _state: &Self::State, @@ -603,14 +422,18 @@ unsafe impl WorldQuery for EntityRef<'_> { } } -/// SAFETY: Access is read-only. -unsafe impl ReadOnlyWorldQuery for EntityRef<'_> {} +/// SAFETY: `Self` is the same as `Self::ReadOnly` +unsafe impl<'a> WorldQueryData for EntityRef<'a> { + type ReadOnly = Self; +} + +/// SAFETY: access is read only +unsafe impl ReadOnlyWorldQueryData for EntityRef<'_> {} /// SAFETY: The accesses of `Self::ReadOnly` are a subset of the accesses of `Self` unsafe impl<'a> WorldQuery for EntityMut<'a> { type Fetch<'w> = UnsafeWorldCell<'w>; type Item<'w> = EntityMut<'w>; - type ReadOnly = EntityRef<'a>; type State = (); fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -619,8 +442,6 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { const IS_DENSE: bool = true; - const IS_ARCHETYPAL: bool = true; - unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, _state: &Self::State, @@ -683,6 +504,11 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { } } +/// SAFETY: access of `EntityRef` is a subset of `EntityMut` +unsafe impl<'a> WorldQueryData for EntityMut<'a> { + type ReadOnly = EntityRef<'a>; +} + #[doc(hidden)] pub struct ReadFetch<'w, T> { // T::Storage = TableStorage @@ -698,11 +524,14 @@ impl Clone for ReadFetch<'_, T> { } impl Copy for ReadFetch<'_, T> {} -/// SAFETY: `Self` is the same as `Self::ReadOnly` +/// SAFETY: +/// `fetch` accesses a single component in a readonly way. +/// This is sound because `update_component_access` and `update_archetype_component_access` add read access for that component and panic when appropriate. +/// `update_component_access` adds a `With` filter for a component. +/// This is sound because `matches_component_set` returns whether the set contains that component. unsafe impl WorldQuery for &T { type Fetch<'w> = ReadFetch<'w, T>; type Item<'w> = &'w T; - type ReadOnly = Self; type State = ComponentId; fn shrink<'wlong: 'wshort, 'wshort>(item: &'wlong T) -> &'wshort T { @@ -716,8 +545,6 @@ unsafe impl WorldQuery for &T { } }; - const IS_ARCHETYPAL: bool = true; - #[inline] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, @@ -823,8 +650,13 @@ unsafe impl WorldQuery for &T { } } +/// SAFETY: `Self` is the same as `Self::ReadOnly` +unsafe impl WorldQueryData for &T { + type ReadOnly = Self; +} + /// SAFETY: access is read only -unsafe impl ReadOnlyWorldQuery for &T {} +unsafe impl ReadOnlyWorldQueryData for &T {} #[doc(hidden)] pub struct RefFetch<'w, T> { @@ -848,11 +680,14 @@ impl Clone for RefFetch<'_, T> { } impl Copy for RefFetch<'_, T> {} -/// SAFETY: `Self` is the same as `Self::ReadOnly` +/// SAFETY: +/// `fetch` accesses a single component in a readonly way. +/// This is sound because `update_component_access` and `update_archetype_component_access` add read access for that component and panic when appropriate. +/// `update_component_access` adds a `With` filter for a component. +/// This is sound because `matches_component_set` returns whether the set contains that component. unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { type Fetch<'w> = RefFetch<'w, T>; type Item<'w> = Ref<'w, T>; - type ReadOnly = Self; type State = ComponentId; fn shrink<'wlong: 'wshort, 'wshort>(item: Ref<'wlong, T>) -> Ref<'wshort, T> { @@ -866,8 +701,6 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { } }; - const IS_ARCHETYPAL: bool = true; - #[inline] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, @@ -984,8 +817,13 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { } } +/// SAFETY: `Self` is the same as `Self::ReadOnly` +unsafe impl<'__w, T: Component> WorldQueryData for Ref<'__w, T> { + type ReadOnly = Self; +} + /// SAFETY: access is read only -unsafe impl<'__w, T: Component> ReadOnlyWorldQuery for Ref<'__w, T> {} +unsafe impl<'__w, T: Component> ReadOnlyWorldQueryData for Ref<'__w, T> {} #[doc(hidden)] pub struct WriteFetch<'w, T> { @@ -1009,11 +847,14 @@ impl Clone for WriteFetch<'_, T> { } impl Copy for WriteFetch<'_, T> {} -/// SAFETY: access of `&T` is a subset of `&mut T` +/// SAFETY: +/// `fetch` accesses a single component mutably. +/// This is sound because `update_component_access` and `update_archetype_component_access` add write access for that component and panic when appropriate. +/// `update_component_access` adds a `With` filter for a component. +/// This is sound because `matches_component_set` returns whether the set contains that component. unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { type Fetch<'w> = WriteFetch<'w, T>; type Item<'w> = Mut<'w, T>; - type ReadOnly = &'__w T; type State = ComponentId; fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> { @@ -1027,8 +868,6 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { } }; - const IS_ARCHETYPAL: bool = true; - #[inline] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, @@ -1145,6 +984,11 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { } } +/// SAFETY: access of `&T` is a subset of `&mut T` +unsafe impl<'__w, T: Component> WorldQueryData for &'__w mut T { + type ReadOnly = &'__w T; +} + #[doc(hidden)] pub struct OptionFetch<'w, T: WorldQuery> { fetch: T::Fetch<'w>, @@ -1160,11 +1004,13 @@ impl Clone for OptionFetch<'_, T> { } } -// SAFETY: defers to soundness of `T: WorldQuery` impl +/// SAFETY: +/// `fetch` might access any components that `T` accesses. +/// This is sound because `update_component_access` and `update_archetype_component_access` add the same accesses as `T`. +/// Filters are unchanged. unsafe impl WorldQuery for Option { type Fetch<'w> = OptionFetch<'w, T>; type Item<'w> = Option>; - type ReadOnly = Option; type State = T::State; fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -1173,8 +1019,6 @@ unsafe impl WorldQuery for Option { const IS_DENSE: bool = T::IS_DENSE; - const IS_ARCHETYPAL: bool = T::IS_ARCHETYPAL; - #[inline] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, @@ -1257,8 +1101,13 @@ unsafe impl WorldQuery for Option { } } +// SAFETY: defers to soundness of `T: WorldQuery` impl +unsafe impl WorldQueryData for Option { + type ReadOnly = Option; +} + /// SAFETY: [`OptionFetch`] is read only because `T` is read only -unsafe impl ReadOnlyWorldQuery for Option {} +unsafe impl ReadOnlyWorldQueryData for Option {} /// Returns a bool that describes if an entity has the component `T`. /// @@ -1315,11 +1164,12 @@ unsafe impl ReadOnlyWorldQuery for Option {} /// ``` pub struct Has(PhantomData); -// SAFETY: `Self::ReadOnly` is the same as `Self` +/// SAFETY: +/// `update_component_access` and `update_archetype_component_access` do nothing. +/// This is sound because `fetch` does not access components. unsafe impl WorldQuery for Has { type Fetch<'w> = bool; type Item<'w> = bool; - type ReadOnly = Self; type State = ComponentId; fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -1333,8 +1183,6 @@ unsafe impl WorldQuery for Has { } }; - const IS_ARCHETYPAL: bool = true; - #[inline] unsafe fn init_fetch<'w>( _world: UnsafeWorldCell<'w>, @@ -1393,105 +1241,14 @@ unsafe impl WorldQuery for Has { } } -/// SAFETY: [`Has`] is read only -unsafe impl ReadOnlyWorldQuery for Has {} - -macro_rules! impl_tuple_fetch { - ($(($name: ident, $state: ident)),*) => { - #[allow(non_snake_case)] - #[allow(clippy::unused_unit)] - // SAFETY: defers to soundness `$name: WorldQuery` impl - unsafe impl<$($name: WorldQuery),*> WorldQuery for ($($name,)*) { - type Fetch<'w> = ($($name::Fetch<'w>,)*); - type Item<'w> = ($($name::Item<'w>,)*); - type ReadOnly = ($($name::ReadOnly,)*); - type State = ($($name::State,)*); - - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { - let ($($name,)*) = item; - ($( - $name::shrink($name), - )*) - } - - #[inline] - #[allow(clippy::unused_unit)] - unsafe fn init_fetch<'w>(_world: UnsafeWorldCell<'w>, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> { - let ($($name,)*) = state; - ($($name::init_fetch(_world, $name, _last_run, _this_run),)*) - } - - const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; - - const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; - - #[inline] - unsafe fn set_archetype<'w>( - _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, - _archetype: &'w Archetype, - _table: &'w Table - ) { - let ($($name,)*) = _fetch; - let ($($state,)*) = _state; - $($name::set_archetype($name, $state, _archetype, _table);)* - } - - #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { - let ($($name,)*) = _fetch; - let ($($state,)*) = _state; - $($name::set_table($name, $state, _table);)* - } - - #[inline(always)] - #[allow(clippy::unused_unit)] - unsafe fn fetch<'w>( - _fetch: &mut Self::Fetch<'w>, - _entity: Entity, - _table_row: TableRow - ) -> Self::Item<'w> { - let ($($name,)*) = _fetch; - ($($name::fetch($name, _entity, _table_row),)*) - } - - #[inline(always)] - unsafe fn filter_fetch( - _fetch: &mut Self::Fetch<'_>, - _entity: Entity, - _table_row: TableRow - ) -> bool { - let ($($name,)*) = _fetch; - true $(&& $name::filter_fetch($name, _entity, _table_row))* - } - - fn update_component_access(state: &Self::State, _access: &mut FilteredAccess) { - let ($($name,)*) = state; - $($name::update_component_access($name, _access);)* - } - - fn update_archetype_component_access(state: &Self::State, _archetype: &Archetype, _access: &mut Access) { - let ($($name,)*) = state; - $($name::update_archetype_component_access($name, _archetype, _access);)* - } - - - fn init_state(_world: &mut World) -> Self::State { - ($($name::init_state(_world),)*) - } - - fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool { - let ($($name,)*) = state; - true $(&& $name::matches_component_set($name, _set_contains_id))* - } - } - - /// SAFETY: each item in the tuple is read only - unsafe impl<$($name: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for ($($name,)*) {} - - }; +/// SAFETY: `Self` is the same as `Self::ReadOnly` +unsafe impl WorldQueryData for Has { + type ReadOnly = Self; } +/// SAFETY: [`Has`] is read only +unsafe impl ReadOnlyWorldQueryData for Has {} + /// The `AnyOf` query parameter fetches entities with any of the component types included in T. /// /// `Query>` is equivalent to `Query<(Option<&A>, Option<&B>, Option<&mut C>), Or<(With, With, With)>>`. @@ -1499,15 +1256,35 @@ macro_rules! impl_tuple_fetch { /// Entities are guaranteed to have at least one of the components in `T`. pub struct AnyOf(PhantomData); -macro_rules! impl_anytuple_fetch { +macro_rules! impl_tuple_world_query_data { ($(($name: ident, $state: ident)),*) => { + #[allow(non_snake_case)] #[allow(clippy::unused_unit)] - // SAFETY: defers to soundness of `$name: WorldQuery` impl + // SAFETY: defers to soundness `$name: WorldQuery` impl + unsafe impl<$($name: WorldQueryData),*> WorldQueryData for ($($name,)*) { + type ReadOnly = ($($name::ReadOnly,)*); + } + + /// SAFETY: each item in the tuple is read only + unsafe impl<$($name: ReadOnlyWorldQueryData),*> ReadOnlyWorldQueryData for ($($name,)*) {} + + }; +} + +macro_rules! impl_anytuple_fetch { + ($(($name: ident, $state: ident)),*) => { + + #[allow(non_snake_case)] + #[allow(clippy::unused_unit)] + /// SAFETY: + /// `fetch` accesses are a subset of the subqueries' accesses + /// This is sound because `update_component_access` and `update_archetype_component_access` adds accesses according to the implementations of all the subqueries. + /// `update_component_access` replaces the filters with a disjunction where every element is a conjunction of the previous filters and the filters of one of the subqueries. + /// This is sound because `matches_component_set` returns a disjunction of the results of the subqueries' implementations. unsafe impl<$($name: WorldQuery),*> WorldQuery for AnyOf<($($name,)*)> { type Fetch<'w> = ($(($name::Fetch<'w>, bool),)*); type Item<'w> = ($(Option<$name::Item<'w>>,)*); - type ReadOnly = AnyOf<($($name::ReadOnly,)*)>; type State = ($($name::State,)*); fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -1526,8 +1303,6 @@ macro_rules! impl_anytuple_fetch { const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; - const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; - #[inline] unsafe fn set_archetype<'w>( _fetch: &mut Self::Fetch<'w>, @@ -1609,32 +1384,38 @@ macro_rules! impl_anytuple_fetch { } } + #[allow(non_snake_case)] + #[allow(clippy::unused_unit)] + // SAFETY: defers to soundness of `$name: WorldQuery` impl + unsafe impl<$($name: WorldQueryData),*> WorldQueryData for AnyOf<($($name,)*)> { + type ReadOnly = AnyOf<($($name::ReadOnly,)*)>; + } + /// SAFETY: each item in the tuple is read only - unsafe impl<$($name: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for AnyOf<($($name,)*)> {} + unsafe impl<$($name: ReadOnlyWorldQueryData),*> ReadOnlyWorldQueryData for AnyOf<($($name,)*)> {} }; } -all_tuples!(impl_tuple_fetch, 0, 15, F, S); +all_tuples!(impl_tuple_world_query_data, 0, 15, F, S); all_tuples!(impl_anytuple_fetch, 0, 15, F, S); /// [`WorldQuery`] used to nullify queries by turning `Query` into `Query>` /// /// This will rarely be useful to consumers of `bevy_ecs`. -pub struct NopWorldQuery(PhantomData); +pub struct NopWorldQuery(PhantomData); -/// SAFETY: `Self::ReadOnly` is `Self` -unsafe impl WorldQuery for NopWorldQuery { +/// SAFETY: +/// `update_component_access` and `update_archetype_component_access` do nothing. +/// This is sound because `fetch` does not access components. +unsafe impl WorldQuery for NopWorldQuery { type Fetch<'w> = (); type Item<'w> = (); - type ReadOnly = Self; type State = Q::State; fn shrink<'wlong: 'wshort, 'wshort>(_: ()) {} const IS_DENSE: bool = Q::IS_DENSE; - const IS_ARCHETYPAL: bool = true; - #[inline(always)] unsafe fn init_fetch( _world: UnsafeWorldCell, @@ -1685,14 +1466,21 @@ unsafe impl WorldQuery for NopWorldQuery { } } -/// SAFETY: `NopFetch` never accesses any data -unsafe impl ReadOnlyWorldQuery for NopWorldQuery {} +/// SAFETY: `Self::ReadOnly` is `Self` +unsafe impl WorldQueryData for NopWorldQuery { + type ReadOnly = Self; +} -/// SAFETY: `PhantomData` never accesses any world data. +/// SAFETY: `NopFetch` never accesses any data +unsafe impl ReadOnlyWorldQueryData for NopWorldQuery {} + +/// SAFETY: +/// `update_component_access` and `update_archetype_component_access` do nothing. +/// This is sound because `fetch` does not access components. unsafe impl WorldQuery for PhantomData { type Item<'a> = (); type Fetch<'a> = (); - type ReadOnly = Self; + type State = (); fn shrink<'wlong: 'wshort, 'wshort>(_item: Self::Item<'wlong>) -> Self::Item<'wshort> {} @@ -1708,8 +1496,6 @@ unsafe impl WorldQuery for PhantomData { // `PhantomData` does not match any components, so all components it matches // are stored in a Table (vacuous truth). const IS_DENSE: bool = true; - // `PhantomData` matches every entity in each archetype. - const IS_ARCHETYPAL: bool = true; unsafe fn set_archetype<'w>( _fetch: &mut Self::Fetch<'w>, @@ -1748,11 +1534,18 @@ unsafe impl WorldQuery for PhantomData { } } +/// SAFETY: `Self::ReadOnly` is `Self` +unsafe impl WorldQueryData for PhantomData { + type ReadOnly = Self; +} + /// SAFETY: `PhantomData` never accesses any world data. -unsafe impl ReadOnlyWorldQuery for PhantomData {} +unsafe impl ReadOnlyWorldQueryData for PhantomData {} #[cfg(test)] mod tests { + use bevy_ecs_macros::WorldQueryData; + use super::*; use crate::{ self as bevy_ecs, @@ -1768,16 +1561,16 @@ mod tests { // Tests that each variant of struct can be used as a `WorldQuery`. #[test] fn world_query_struct_variants() { - #[derive(WorldQuery)] + #[derive(WorldQueryData)] pub struct NamedQuery { id: Entity, a: &'static A, } - #[derive(WorldQuery)] + #[derive(WorldQueryData)] pub struct TupleQuery(&'static A, &'static B); - #[derive(WorldQuery)] + #[derive(WorldQueryData)] pub struct UnitQuery; fn my_system(_: Query<(NamedQuery, TupleQuery, UnitQuery)>) {} @@ -1788,7 +1581,7 @@ mod tests { // Compile test for https://github.com/bevyengine/bevy/pull/8030. #[test] fn world_query_phantom_data() { - #[derive(WorldQuery)] + #[derive(WorldQueryData)] pub struct IgnoredQuery { id: Entity, _marker: PhantomData, @@ -1806,8 +1599,8 @@ mod tests { mod private { use super::*; - #[derive(WorldQuery)] - #[world_query(mutable)] + #[derive(WorldQueryData)] + #[world_query_data(mutable)] pub struct Q { pub a: &'static mut A, } @@ -1831,7 +1624,7 @@ mod tests { fn world_query_metadata_collision() { // The metadata types generated would be named `ClientState` and `ClientFetch`, // but they should rename themselves to avoid conflicts. - #[derive(WorldQuery)] + #[derive(WorldQueryData)] pub struct Client { pub state: &'static S, pub fetch: &'static ClientFetch, diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 7d958fe874..a02f9f703a 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -10,7 +10,92 @@ use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; use bevy_utils::all_tuples; use std::{cell::UnsafeCell, marker::PhantomData}; -use super::ReadOnlyWorldQuery; +/// Types that filter the results of a [`Query`]. +/// +/// There are many types that natively implement this trait: +/// - **Component filters.** +/// [`With`] and [`Without`] filters can be applied to check if the queried entity does or does not contain a particular component. +/// - **Change detection filters.** +/// [`Added`] and [`Changed`] filters can be applied to detect component changes to an entity. +/// - **`WorldQueryFilter` tuples.** +/// If every element of a tuple implements `WorldQueryFilter`, then the tuple itself also implements the same trait. +/// This enables a single `Query` to filter over multiple conditions. +/// Due to the current lack of variadic generics in Rust, the trait has been implemented for tuples from 0 to 15 elements, +/// but nesting of tuples allows infinite `WorldQueryFilter`s. +/// - **Filter disjunction operator.** +/// By default, tuples compose query filters in such a way that all conditions must be satisfied to generate a query item for a given entity. +/// Wrapping a tuple inside an [`Or`] operator will relax the requirement to just one condition. +/// +/// Implementing the trait manually can allow for a fundamentally new type of behavior. +/// +/// Query design can be easily structured by deriving `WorldQueryFilter` for custom types. +/// Despite the added complexity, this approach has several advantages over using `WorldQueryFilter` tuples. +/// The most relevant improvements are: +/// +/// - Reusability across multiple systems. +/// - Filters can be composed together to create a more complex filter. +/// +/// This trait can only be derived for structs if each field also implements `WorldQueryFilter`. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::{query::WorldQueryFilter, component::Component}; +/// # +/// # #[derive(Component)] +/// # struct ComponentA; +/// # #[derive(Component)] +/// # struct ComponentB; +/// # #[derive(Component)] +/// # struct ComponentC; +/// # #[derive(Component)] +/// # struct ComponentD; +/// # #[derive(Component)] +/// # struct ComponentE; +/// # +/// #[derive(WorldQueryFilter)] +/// struct MyFilter { +/// // Field names are not relevant, since they are never manually accessed. +/// with_a: With, +/// or_filter: Or<(With, Added)>, +/// generic_tuple: (With, Without

), +/// } +/// +/// fn my_system(query: Query>) { +/// // ... +/// } +/// # bevy_ecs::system::assert_is_system(my_system); +/// ``` +/// +/// [`fetch`]: Self::fetch +/// [`Changed`]: crate::query::Changed +/// [`matches_component_set`]: Self::matches_component_set +/// [`Or`]: crate::query::Or +/// [`Query`]: crate::system::Query +/// [`State`]: Self::State +/// [`update_archetype_component_access`]: Self::update_archetype_component_access +/// [`update_component_access`]: Self::update_component_access +/// [`With`]: crate::query::With +/// [`Without`]: crate::query::Without + +pub trait WorldQueryFilter: WorldQuery { + /// Returns true if (and only if) this Filter relies strictly on archetypes to limit which + /// components are accessed by the Query. + /// + /// This enables optimizations for [`crate::query::QueryIter`] that rely on knowing exactly how + /// many elements are being iterated (such as `Iterator::collect()`). + const IS_ARCHETYPAL: bool; + + /// # Safety + /// + /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and + /// `table_row` must be in the range of the current table and archetype. + #[allow(unused_variables)] + unsafe fn filter_fetch( + fetch: &mut Self::Fetch<'_>, + entity: Entity, + table_row: TableRow, + ) -> bool; +} /// Filter that selects entities with a component `T`. /// @@ -41,11 +126,14 @@ use super::ReadOnlyWorldQuery; /// ``` pub struct With(PhantomData); -// SAFETY: `Self::ReadOnly` is the same as `Self` +/// SAFETY: +/// `update_component_access` and `update_archetype_component_access` do not add any accesses. +/// This is sound because `fetch` does not access any components. +/// `update_component_access` adds a `With` filter for `T`. +/// This is sound because `matches_component_set` returns whether the set contains the component. unsafe impl WorldQuery for With { type Fetch<'w> = (); type Item<'w> = (); - type ReadOnly = Self; type State = ComponentId; fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {} @@ -66,8 +154,6 @@ unsafe impl WorldQuery for With { } }; - const IS_ARCHETYPAL: bool = true; - #[inline] unsafe fn set_table(_fetch: &mut (), _state: &ComponentId, _table: &Table) {} @@ -113,8 +199,18 @@ unsafe impl WorldQuery for With { } } -// SAFETY: no component access or archetype component access -unsafe impl ReadOnlyWorldQuery for With {} +impl WorldQueryFilter for With { + const IS_ARCHETYPAL: bool = true; + + #[inline(always)] + unsafe fn filter_fetch( + _fetch: &mut Self::Fetch<'_>, + _entity: Entity, + _table_row: TableRow, + ) -> bool { + true + } +} /// Filter that selects entities without a component `T`. /// @@ -142,11 +238,14 @@ unsafe impl ReadOnlyWorldQuery for With {} /// ``` pub struct Without(PhantomData); -// SAFETY: `Self::ReadOnly` is the same as `Self` +/// SAFETY: +/// `update_component_access` and `update_archetype_component_access` do not add any accesses. +/// This is sound because `fetch` does not access any components. +/// `update_component_access` adds a `Without` filter for `T`. +/// This is sound because `matches_component_set` returns whether the set does not contain the component. unsafe impl WorldQuery for Without { type Fetch<'w> = (); type Item<'w> = (); - type ReadOnly = Self; type State = ComponentId; fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {} @@ -167,8 +266,6 @@ unsafe impl WorldQuery for Without { } }; - const IS_ARCHETYPAL: bool = true; - #[inline] unsafe fn set_table(_fetch: &mut (), _state: &Self::State, _table: &Table) {} @@ -214,8 +311,18 @@ unsafe impl WorldQuery for Without { } } -// SAFETY: no component access or archetype component access -unsafe impl ReadOnlyWorldQuery for Without {} +impl WorldQueryFilter for Without { + const IS_ARCHETYPAL: bool = true; + + #[inline(always)] + unsafe fn filter_fetch( + _fetch: &mut Self::Fetch<'_>, + _entity: Entity, + _table_row: TableRow, + ) -> bool { + true + } +} /// A filter that tests if any of the given filters apply. /// @@ -269,11 +376,14 @@ macro_rules! impl_query_filter_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] #[allow(clippy::unused_unit)] - // SAFETY: defers to soundness of `$filter: WorldQuery` impl - unsafe impl<$($filter: WorldQuery),*> WorldQuery for Or<($($filter,)*)> { + /// SAFETY: + /// `fetch` accesses are a subset of the subqueries' accesses + /// This is sound because `update_component_access` and `update_archetype_component_access` adds accesses according to the implementations of all the subqueries. + /// `update_component_access` replace the filters with a disjunction where every element is a conjunction of the previous filters and the filters of one of the subqueries. + /// This is sound because `matches_component_set` returns a disjunction of the results of the subqueries' implementations. + unsafe impl<$($filter: WorldQueryFilter),*> WorldQuery for Or<($($filter,)*)> { type Fetch<'w> = ($(OrFetch<'w, $filter>,)*); type Item<'w> = bool; - type ReadOnly = Or<($($filter::ReadOnly,)*)>; type State = ($($filter::State,)*); fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { @@ -282,8 +392,6 @@ macro_rules! impl_query_filter_tuple { const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*; - const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*; - #[inline] unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { let ($($filter,)*) = state; @@ -332,15 +440,6 @@ macro_rules! impl_query_filter_tuple { false $(|| ($filter.matches && $filter::filter_fetch(&mut $filter.fetch, _entity, _table_row)))* } - #[inline(always)] - unsafe fn filter_fetch( - fetch: &mut Self::Fetch<'_>, - entity: Entity, - table_row: TableRow - ) -> bool { - Self::fetch(fetch, entity, table_row) - } - fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { let ($($filter,)*) = state; @@ -376,129 +475,8 @@ macro_rules! impl_query_filter_tuple { } } - // SAFETY: filters are read only - unsafe impl<$($filter: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for Or<($($filter,)*)> {} - }; -} - -all_tuples!(impl_query_filter_tuple, 0, 15, F, S); - -macro_rules! impl_tick_filter { - ( - $(#[$meta:meta])* - $name: ident, - $(#[$fetch_meta:meta])* - $fetch_name: ident, - $get_slice: expr, - $get_sparse_set: expr - ) => { - $(#[$meta])* - pub struct $name(PhantomData); - - #[doc(hidden)] - #[derive(Clone)] - $(#[$fetch_meta])* - pub struct $fetch_name<'w> { - table_ticks: Option>>, - sparse_set: Option<&'w ComponentSparseSet>, - last_run: Tick, - this_run: Tick, - } - - // SAFETY: `Self::ReadOnly` is the same as `Self` - unsafe impl WorldQuery for $name { - type Fetch<'w> = $fetch_name<'w>; - type Item<'w> = bool; - type ReadOnly = Self; - type State = ComponentId; - - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { - item - } - - #[inline] - unsafe fn init_fetch<'w>( - world: UnsafeWorldCell<'w>, - &id: &ComponentId, - last_run: Tick, - this_run: Tick - ) -> Self::Fetch<'w> { - Self::Fetch::<'w> { - table_ticks: None, - sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet) - .then(|| { - world.storages() - .sparse_sets - .get(id) - .debug_checked_unwrap() - }), - last_run, - this_run, - } - } - - const IS_DENSE: bool = { - match T::Storage::STORAGE_TYPE { - StorageType::Table => true, - StorageType::SparseSet => false, - } - }; - - const IS_ARCHETYPAL: bool = false; - - #[inline] - unsafe fn set_table<'w>( - fetch: &mut Self::Fetch<'w>, - &component_id: &ComponentId, - table: &'w Table - ) { - fetch.table_ticks = Some( - $get_slice( - &table - .get_column(component_id) - .debug_checked_unwrap() - ).into(), - ); - } - - #[inline] - unsafe fn set_archetype<'w>( - fetch: &mut Self::Fetch<'w>, - component_id: &ComponentId, - _archetype: &'w Archetype, - table: &'w Table - ) { - if Self::IS_DENSE { - Self::set_table(fetch, component_id, table); - } - } - - #[inline(always)] - unsafe fn fetch<'w>( - fetch: &mut Self::Fetch<'w>, - entity: Entity, - table_row: TableRow - ) -> Self::Item<'w> { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - fetch - .table_ticks - .debug_checked_unwrap() - .get(table_row.index()) - .deref() - .is_newer_than(fetch.last_run, fetch.this_run) - } - StorageType::SparseSet => { - let sparse_set = &fetch - .sparse_set - .debug_checked_unwrap(); - $get_sparse_set(sparse_set, entity) - .debug_checked_unwrap() - .deref() - .is_newer_than(fetch.last_run, fetch.this_run) - } - } - } + impl<$($filter: WorldQueryFilter),*> WorldQueryFilter for Or<($($filter,)*)> { + const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*; #[inline(always)] unsafe fn filter_fetch( @@ -508,141 +486,399 @@ macro_rules! impl_tick_filter { ) -> bool { Self::fetch(fetch, entity, table_row) } - - #[inline] - fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { - if access.access().has_write(id) { - panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - std::any::type_name::()); - } - access.add_read(id); - } - - #[inline] - fn update_archetype_component_access( - &id: &ComponentId, - archetype: &Archetype, - access: &mut Access, - ) { - if let Some(archetype_component_id) = archetype.get_archetype_component_id(id) { - access.add_read(archetype_component_id); - } - } - - fn init_state(world: &mut World) -> ComponentId { - world.init_component::() - } - - fn matches_component_set(&id: &ComponentId, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool { - set_contains_id(id) - } } - - /// SAFETY: read-only access - unsafe impl ReadOnlyWorldQuery for $name {} }; } -impl_tick_filter!( - /// A filter on a component that only retains results added after the system last ran. - /// - /// A common use for this filter is one-time initialization. - /// - /// To retain all results without filtering but still check whether they were added after the - /// system last ran, use [`Ref`](crate::change_detection::Ref). - /// - /// # Deferred - /// - /// Note, that entity modifications issued with [`Commands`](crate::system::Commands) - /// are visible only after deferred operations are applied, - /// typically at the end of the schedule iteration. - /// - /// # Examples - /// - /// ``` - /// # use bevy_ecs::component::Component; - /// # use bevy_ecs::query::Added; - /// # use bevy_ecs::system::IntoSystem; - /// # use bevy_ecs::system::Query; - /// # - /// # #[derive(Component, Debug)] - /// # struct Name {}; - /// - /// fn print_add_name_component(query: Query<&Name, Added>) { - /// for name in &query { - /// println!("Named entity created: {:?}", name) - /// } - /// } - /// - /// # bevy_ecs::system::assert_is_system(print_add_name_component); - /// ``` - Added, - AddedFetch, - Column::get_added_ticks_slice, - ComponentSparseSet::get_added_tick -); +macro_rules! impl_tuple_world_query_filter { + ($($name: ident),*) => { + #[allow(unused_variables)] + #[allow(non_snake_case)] + #[allow(clippy::unused_unit)] -impl_tick_filter!( - /// A filter on a component that only retains results added or mutably dereferenced after the system last ran. - /// - /// A common use for this filter is avoiding redundant work when values have not changed. - /// - /// **Note** that simply *mutably dereferencing* a component is considered a change ([`DerefMut`](std::ops::DerefMut)). - /// Bevy does not compare components to their previous values. - /// - /// To retain all results without filtering but still check whether they were changed after the - /// system last ran, use [`Ref`](crate::change_detection::Ref). - /// - /// # Deferred - /// - /// Note, that entity modifications issued with [`Commands`](crate::system::Commands) - /// (like entity creation or entity component addition or removal) - /// are visible only after deferred operations are applied, - /// typically at the end of the schedule iteration. - /// - /// # Examples - /// - /// ``` - /// # use bevy_ecs::component::Component; - /// # use bevy_ecs::query::Changed; - /// # use bevy_ecs::system::IntoSystem; - /// # use bevy_ecs::system::Query; - /// # - /// # #[derive(Component, Debug)] - /// # struct Name {}; - /// # #[derive(Component)] - /// # struct Transform {}; - /// - /// fn print_moving_objects_system(query: Query<&Name, Changed>) { - /// for name in &query { - /// println!("Entity Moved: {:?}", name); - /// } - /// } - /// - /// # bevy_ecs::system::assert_is_system(print_moving_objects_system); - /// ``` - Changed, - ChangedFetch, - Column::get_changed_ticks_slice, - ComponentSparseSet::get_changed_tick -); + impl<$($name: WorldQueryFilter),*> WorldQueryFilter for ($($name,)*) { + const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; + + #[inline(always)] + unsafe fn filter_fetch( + fetch: &mut Self::Fetch<'_>, + _entity: Entity, + _table_row: TableRow + ) -> bool { + let ($($name,)*) = fetch; + true $(&& $name::filter_fetch($name, _entity, _table_row))* + } + } + + }; +} + +all_tuples!(impl_tuple_world_query_filter, 0, 15, F); +all_tuples!(impl_query_filter_tuple, 0, 15, F, S); + +/// A filter on a component that only retains results added after the system last ran. +/// +/// A common use for this filter is one-time initialization. +/// +/// To retain all results without filtering but still check whether they were added after the +/// system last ran, use [`Ref`](crate::change_detection::Ref). +/// +/// # Deferred +/// +/// Note, that entity modifications issued with [`Commands`](crate::system::Commands) +/// are visible only after deferred operations are applied, +/// typically at the end of the schedule iteration. +/// +/// # Examples +/// +/// ``` +/// # use bevy_ecs::component::Component; +/// # use bevy_ecs::query::Added; +/// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; +/// # +/// # #[derive(Component, Debug)] +/// # struct Name {}; +/// +/// fn print_add_name_component(query: Query<&Name, Added>) { +/// for name in &query { +/// println!("Named entity created: {:?}", name) +/// } +/// } +/// +/// # bevy_ecs::system::assert_is_system(print_add_name_component); +/// ``` +pub struct Added(PhantomData); + +#[doc(hidden)] +#[derive(Clone)] +pub struct AddedFetch<'w> { + table_ticks: Option>>, + sparse_set: Option<&'w ComponentSparseSet>, + last_run: Tick, + this_run: Tick, +} + +/// SAFETY: +/// `fetch` accesses a single component in a readonly way. +/// This is sound because `update_component_access` and `update_archetype_component_access` add read access for that component and panic when appropriate. +/// `update_component_access` adds a `With` filter for a component. +/// This is sound because `matches_component_set` returns whether the set contains that component. +unsafe impl WorldQuery for Added { + type Fetch<'w> = AddedFetch<'w>; + type Item<'w> = bool; + type State = ComponentId; + + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: UnsafeWorldCell<'w>, + &id: &ComponentId, + last_run: Tick, + this_run: Tick, + ) -> Self::Fetch<'w> { + Self::Fetch::<'w> { + table_ticks: None, + sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet) + .then(|| world.storages().sparse_sets.get(id).debug_checked_unwrap()), + last_run, + this_run, + } + } + + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { + StorageType::Table => true, + StorageType::SparseSet => false, + } + }; + + #[inline] + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + &component_id: &ComponentId, + table: &'w Table, + ) { + fetch.table_ticks = Some( + Column::get_added_ticks_slice(table.get_column(component_id).debug_checked_unwrap()) + .into(), + ); + } + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut Self::Fetch<'w>, + component_id: &ComponentId, + _archetype: &'w Archetype, + table: &'w Table, + ) { + if Self::IS_DENSE { + Self::set_table(fetch, component_id, table); + } + } + + #[inline(always)] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + ) -> Self::Item<'w> { + match T::Storage::STORAGE_TYPE { + StorageType::Table => fetch + .table_ticks + .debug_checked_unwrap() + .get(table_row.index()) + .deref() + .is_newer_than(fetch.last_run, fetch.this_run), + StorageType::SparseSet => { + let sparse_set = &fetch.sparse_set.debug_checked_unwrap(); + ComponentSparseSet::get_added_tick(sparse_set, entity) + .debug_checked_unwrap() + .deref() + .is_newer_than(fetch.last_run, fetch.this_run) + } + } + } + + #[inline] + fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { + if access.access().has_write(id) { + panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); + } + access.add_read(id); + } + + #[inline] + fn update_archetype_component_access( + &id: &ComponentId, + archetype: &Archetype, + access: &mut Access, + ) { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(id) { + access.add_read(archetype_component_id); + } + } + + fn init_state(world: &mut World) -> ComponentId { + world.init_component::() + } + + fn matches_component_set( + &id: &ComponentId, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + set_contains_id(id) + } +} + +impl WorldQueryFilter for Added { + const IS_ARCHETYPAL: bool = false; + #[inline(always)] + unsafe fn filter_fetch( + fetch: &mut Self::Fetch<'_>, + entity: Entity, + table_row: TableRow, + ) -> bool { + Self::fetch(fetch, entity, table_row) + } +} + +/// A filter on a component that only retains results added or mutably dereferenced after the system last ran. +/// +/// A common use for this filter is avoiding redundant work when values have not changed. +/// +/// **Note** that simply *mutably dereferencing* a component is considered a change ([`DerefMut`](std::ops::DerefMut)). +/// Bevy does not compare components to their previous values. +/// +/// To retain all results without filtering but still check whether they were changed after the +/// system last ran, use [`Ref`](crate::change_detection::Ref). +/// +/// # Deferred +/// +/// Note, that entity modifications issued with [`Commands`](crate::system::Commands) +/// (like entity creation or entity component addition or removal) +/// are visible only after deferred operations are applied, +/// typically at the end of the schedule iteration. +/// +/// # Examples +/// +/// ``` +/// # use bevy_ecs::component::Component; +/// # use bevy_ecs::query::Changed; +/// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; +/// # +/// # #[derive(Component, Debug)] +/// # struct Name {}; +/// # #[derive(Component)] +/// # struct Transform {}; +/// +/// fn print_moving_objects_system(query: Query<&Name, Changed>) { +/// for name in &query { +/// println!("Entity Moved: {:?}", name); +/// } +/// } +/// +/// # bevy_ecs::system::assert_is_system(print_moving_objects_system); +/// ``` +pub struct Changed(PhantomData); + +#[doc(hidden)] +#[derive(Clone)] +pub struct ChangedFetch<'w> { + table_ticks: Option>>, + sparse_set: Option<&'w ComponentSparseSet>, + last_run: Tick, + this_run: Tick, +} + +/// SAFETY: +/// `fetch` accesses a single component in a readonly way. +/// This is sound because `update_component_access` and `update_archetype_component_access` add read access for that component and panic when appropriate. +/// `update_component_access` adds a `With` filter for a component. +/// This is sound because `matches_component_set` returns whether the set contains that component. +unsafe impl WorldQuery for Changed { + type Fetch<'w> = ChangedFetch<'w>; + type Item<'w> = bool; + type State = ComponentId; + + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: UnsafeWorldCell<'w>, + &id: &ComponentId, + last_run: Tick, + this_run: Tick, + ) -> Self::Fetch<'w> { + Self::Fetch::<'w> { + table_ticks: None, + sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet) + .then(|| world.storages().sparse_sets.get(id).debug_checked_unwrap()), + last_run, + this_run, + } + } + + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { + StorageType::Table => true, + StorageType::SparseSet => false, + } + }; + + #[inline] + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + &component_id: &ComponentId, + table: &'w Table, + ) { + fetch.table_ticks = Some( + Column::get_changed_ticks_slice(table.get_column(component_id).debug_checked_unwrap()) + .into(), + ); + } + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut Self::Fetch<'w>, + component_id: &ComponentId, + _archetype: &'w Archetype, + table: &'w Table, + ) { + if Self::IS_DENSE { + Self::set_table(fetch, component_id, table); + } + } + + #[inline(always)] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + ) -> Self::Item<'w> { + match T::Storage::STORAGE_TYPE { + StorageType::Table => fetch + .table_ticks + .debug_checked_unwrap() + .get(table_row.index()) + .deref() + .is_newer_than(fetch.last_run, fetch.this_run), + StorageType::SparseSet => { + let sparse_set = &fetch.sparse_set.debug_checked_unwrap(); + ComponentSparseSet::get_changed_tick(sparse_set, entity) + .debug_checked_unwrap() + .deref() + .is_newer_than(fetch.last_run, fetch.this_run) + } + } + } + + #[inline] + fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { + if access.access().has_write(id) { + panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); + } + access.add_read(id); + } + + #[inline] + fn update_archetype_component_access( + &id: &ComponentId, + archetype: &Archetype, + access: &mut Access, + ) { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(id) { + access.add_read(archetype_component_id); + } + } + + fn init_state(world: &mut World) -> ComponentId { + world.init_component::() + } + + fn matches_component_set( + &id: &ComponentId, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + set_contains_id(id) + } +} + +impl WorldQueryFilter for Changed { + const IS_ARCHETYPAL: bool = false; + + #[inline(always)] + unsafe fn filter_fetch( + fetch: &mut Self::Fetch<'_>, + entity: Entity, + table_row: TableRow, + ) -> bool { + Self::fetch(fetch, entity, table_row) + } +} /// A marker trait to indicate that the filter works at an archetype level. /// /// This is needed to implement [`ExactSizeIterator`] for /// [`QueryIter`](crate::query::QueryIter) that contains archetype-level filters. /// -/// The trait must only be implement for filters where its corresponding [`WorldQuery::IS_ARCHETYPAL`] +/// The trait must only be implemented for filters where its corresponding [`WorldQueryFilter::IS_ARCHETYPAL`] /// is [`prim@true`]. As such, only the [`With`] and [`Without`] filters can implement the trait. /// [Tuples](prim@tuple) and [`Or`] filters are automatically implemented with the trait only if its containing types /// also implement the same trait. /// /// [`Added`] and [`Changed`] works with entities, and therefore are not archetypal. As such /// they do not implement [`ArchetypeFilter`]. -pub trait ArchetypeFilter {} +pub trait ArchetypeFilter: WorldQueryFilter {} -impl ArchetypeFilter for With {} -impl ArchetypeFilter for Without {} +impl ArchetypeFilter for With {} +impl ArchetypeFilter for Without {} macro_rules! impl_archetype_filter_tuple { ($($filter: ident),*) => { diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 142877efc5..2ed3433593 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -2,26 +2,26 @@ use crate::{ archetype::{ArchetypeEntity, ArchetypeId, Archetypes}, component::Tick, entity::{Entities, Entity}, - query::{ArchetypeFilter, DebugCheckedUnwrap, QueryState, WorldQuery}, + query::{ArchetypeFilter, DebugCheckedUnwrap, QueryState}, storage::{TableId, TableRow, Tables}, world::unsafe_world_cell::UnsafeWorldCell, }; use std::{borrow::Borrow, iter::FusedIterator, mem::MaybeUninit}; -use super::ReadOnlyWorldQuery; +use super::{ReadOnlyWorldQueryData, WorldQueryData, WorldQueryFilter}; /// An [`Iterator`] over query results of a [`Query`](crate::system::Query). /// /// This struct is created by the [`Query::iter`](crate::system::Query::iter) and /// [`Query::iter_mut`](crate::system::Query::iter_mut) methods. -pub struct QueryIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { +pub struct QueryIter<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> { tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, cursor: QueryIterationCursor<'w, 's, Q, F>, } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIter<'w, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryIter<'w, 's, Q, F> { /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. /// - `world` must be the same one used to initialize `query_state`. @@ -41,7 +41,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIter<'w, 's, Q, F> { } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Iterator for QueryIter<'w, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> Iterator for QueryIter<'w, 's, Q, F> { type Item = Q::Item<'w>; #[inline(always)] @@ -57,14 +57,14 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Iterator for QueryIter<'w, 's fn size_hint(&self) -> (usize, Option) { let max_size = self.cursor.max_remaining(self.tables, self.archetypes); - let archetype_query = Q::IS_ARCHETYPAL && F::IS_ARCHETYPAL; + let archetype_query = F::IS_ARCHETYPAL; let min_size = if archetype_query { max_size } else { 0 }; (min_size, Some(max_size)) } } // This is correct as [`QueryIter`] always returns `None` once exhausted. -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> FusedIterator for QueryIter<'w, 's, Q, F> {} +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> FusedIterator for QueryIter<'w, 's, Q, F> {} /// An [`Iterator`] over the query items generated from an iterator of [`Entity`]s. /// @@ -72,7 +72,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> FusedIterator for QueryIter<' /// Entities that don't match the query are skipped. /// /// This struct is created by the [`Query::iter_many`](crate::system::Query::iter_many) and [`Query::iter_many_mut`](crate::system::Query::iter_many_mut) methods. -pub struct QueryManyIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, I: Iterator> +pub struct QueryManyIter<'w, 's, Q: WorldQueryData, F: WorldQueryFilter, I: Iterator> where I::Item: Borrow, { @@ -85,7 +85,7 @@ where query_state: &'s QueryState, } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, I: Iterator> QueryManyIter<'w, 's, Q, F, I> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter, I: Iterator> QueryManyIter<'w, 's, Q, F, I> where I::Item: Borrow, { @@ -182,7 +182,7 @@ where } } -impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, I: Iterator> Iterator +impl<'w, 's, Q: ReadOnlyWorldQueryData, F: WorldQueryFilter, I: Iterator> Iterator for QueryManyIter<'w, 's, Q, F, I> where I::Item: Borrow, @@ -202,7 +202,7 @@ where } // This is correct as [`QueryManyIter`] always returns `None` once exhausted. -impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, I: Iterator> FusedIterator +impl<'w, 's, Q: ReadOnlyWorldQueryData, F: WorldQueryFilter, I: Iterator> FusedIterator for QueryManyIter<'w, 's, Q, F, I> where I::Item: Borrow, @@ -272,14 +272,14 @@ where /// [`Query`]: crate::system::Query /// [`Query::iter_combinations`]: crate::system::Query::iter_combinations /// [`Query::iter_combinations_mut`]: crate::system::Query::iter_combinations_mut -pub struct QueryCombinationIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize> { +pub struct QueryCombinationIter<'w, 's, Q: WorldQueryData, F: WorldQueryFilter, const K: usize> { tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, cursors: [QueryIterationCursor<'w, 's, Q, F>; K], } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter, const K: usize> QueryCombinationIter<'w, 's, Q, F, K> { /// # Safety @@ -386,7 +386,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize> // Iterator type is intentionally implemented only for read-only access. // Doing so for mutable references would be unsound, because calling `next` // multiple times would allow multiple owned references to the same data to exist. -impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, const K: usize> Iterator +impl<'w, 's, Q: ReadOnlyWorldQueryData, F: WorldQueryFilter, const K: usize> Iterator for QueryCombinationIter<'w, 's, Q, F, K> { type Item = [Q::Item<'w>; K]; @@ -422,14 +422,14 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, const K: usize> Itera Some(acc + choose(n, K - i)?) }); - let archetype_query = F::IS_ARCHETYPAL && Q::IS_ARCHETYPAL; + let archetype_query = F::IS_ARCHETYPAL; let known_max = max_combinations.unwrap_or(usize::MAX); let min_combinations = if archetype_query { known_max } else { 0 }; (min_combinations, max_combinations) } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, F> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> ExactSizeIterator for QueryIter<'w, 's, Q, F> where F: ArchetypeFilter, { @@ -439,12 +439,12 @@ where } // This is correct as [`QueryCombinationIter`] always returns `None` once exhausted. -impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, const K: usize> FusedIterator +impl<'w, 's, Q: ReadOnlyWorldQueryData, F: WorldQueryFilter, const K: usize> FusedIterator for QueryCombinationIter<'w, 's, Q, F, K> { } -struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { +struct QueryIterationCursor<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, table_entities: &'w [Entity], @@ -457,7 +457,7 @@ struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { current_row: usize, } -impl Clone for QueryIterationCursor<'_, '_, Q, F> { +impl Clone for QueryIterationCursor<'_, '_, Q, F> { fn clone(&self) -> Self { Self { table_id_iter: self.table_id_iter.clone(), @@ -472,7 +472,7 @@ impl Clone for QueryIterationCursor<'_, '_ } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryIterationCursor<'w, 's, Q, F> { const IS_DENSE: bool = Q::IS_DENSE && F::IS_DENSE; unsafe fn init_empty( diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index c30292cb76..2e6f4df187 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -7,14 +7,17 @@ mod filter; mod iter; mod par_iter; mod state; +mod world_query; pub use access::*; +pub use bevy_ecs_macros::{WorldQueryData, WorldQueryFilter}; pub use error::*; pub use fetch::*; pub use filter::*; pub use iter::*; pub use par_iter::*; pub use state::*; +pub use world_query::*; /// A debug checked version of [`Option::unwrap_unchecked`]. Will panic in /// debug modes if unwrapping a `None` or `Err` value in debug mode, but is @@ -64,9 +67,10 @@ impl DebugCheckedUnwrap for Option { #[cfg(test)] mod tests { - use super::{ReadOnlyWorldQuery, WorldQuery}; + use bevy_ecs_macros::{WorldQueryData, WorldQueryFilter}; + use crate::prelude::{AnyOf, Changed, Entity, Or, QueryState, With, Without}; - use crate::query::{ArchetypeFilter, Has, QueryCombinationIter}; + use crate::query::{ArchetypeFilter, Has, QueryCombinationIter, ReadOnlyWorldQueryData}; use crate::schedule::{IntoSystemConfigs, Schedule}; use crate::system::{IntoSystem, Query, System, SystemState}; use crate::{self as bevy_ecs, component::Component, world::World}; @@ -113,9 +117,8 @@ mod tests { } fn assert_combination(world: &mut World, expected_size: usize) where - Q: ReadOnlyWorldQuery, - F: ReadOnlyWorldQuery, - F::ReadOnly: ArchetypeFilter, + Q: ReadOnlyWorldQueryData, + F: ArchetypeFilter, { let mut query = world.query_filtered::(); let query_type = type_name::>(); @@ -128,9 +131,8 @@ mod tests { } fn assert_all_sizes_equal(world: &mut World, expected_size: usize) where - Q: ReadOnlyWorldQuery, - F: ReadOnlyWorldQuery, - F::ReadOnly: ArchetypeFilter, + Q: ReadOnlyWorldQueryData, + F: ArchetypeFilter, { let mut query = world.query_filtered::(); let query_type = type_name::>(); @@ -499,8 +501,8 @@ mod tests { #[test] #[should_panic = "&mut bevy_ecs::query::tests::A conflicts with a previous access in this query."] fn self_conflicting_worldquery() { - #[derive(WorldQuery)] - #[world_query(mutable)] + #[derive(WorldQueryData)] + #[world_query_data(mutable)] struct SelfConflicting { a: &'static mut A, b: &'static mut A, @@ -536,7 +538,7 @@ mod tests { world.spawn_empty(); { - #[derive(WorldQuery)] + #[derive(WorldQueryData)] struct CustomAB { a: &'static A, b: &'static B, @@ -556,7 +558,7 @@ mod tests { } { - #[derive(WorldQuery)] + #[derive(WorldQueryData)] struct FancyParam { e: Entity, b: &'static B, @@ -577,11 +579,11 @@ mod tests { } { - #[derive(WorldQuery)] + #[derive(WorldQueryData)] struct MaybeBSparse { blah: Option<(&'static B, &'static Sparse)>, } - #[derive(WorldQuery)] + #[derive(WorldQueryData)] struct MatchEverything { abcs: AnyOf<(&'static A, &'static B, &'static C)>, opt_bsparse: MaybeBSparse, @@ -616,11 +618,11 @@ mod tests { } { - #[derive(WorldQuery)] + #[derive(WorldQueryFilter)] struct AOrBFilter { a: Or<(With, With)>, } - #[derive(WorldQuery)] + #[derive(WorldQueryFilter)] struct NoSparseThatsSlow { no: Without, } @@ -637,7 +639,7 @@ mod tests { } { - #[derive(WorldQuery)] + #[derive(WorldQueryFilter)] struct CSparseFilter { tuple_structs_pls: With, ugh: With, @@ -655,7 +657,7 @@ mod tests { } { - #[derive(WorldQuery)] + #[derive(WorldQueryFilter)] struct WithoutComps { _1: Without, _2: Without, @@ -674,7 +676,7 @@ mod tests { } { - #[derive(WorldQuery)] + #[derive(WorldQueryData)] struct IterCombAB { a: &'static A, b: &'static B, diff --git a/crates/bevy_ecs/src/query/par_iter.rs b/crates/bevy_ecs/src/query/par_iter.rs index 1454b6ef51..c21931c27e 100644 --- a/crates/bevy_ecs/src/query/par_iter.rs +++ b/crates/bevy_ecs/src/query/par_iter.rs @@ -1,7 +1,7 @@ use crate::{component::Tick, world::unsafe_world_cell::UnsafeWorldCell}; use std::ops::Range; -use super::{QueryItem, QueryState, ReadOnlyWorldQuery, WorldQuery}; +use super::{QueryItem, QueryState, WorldQueryData, WorldQueryFilter}; /// Dictates how a parallel query chunks up large tables/archetypes /// during iteration. @@ -82,7 +82,7 @@ impl BatchingStrategy { /// /// This struct is created by the [`Query::par_iter`](crate::system::Query::par_iter) and /// [`Query::par_iter_mut`](crate::system::Query::par_iter_mut) methods. -pub struct QueryParIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { +pub struct QueryParIter<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> { pub(crate) world: UnsafeWorldCell<'w>, pub(crate) state: &'s QueryState, pub(crate) last_run: Tick, @@ -90,7 +90,7 @@ pub struct QueryParIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { pub(crate) batching_strategy: BatchingStrategy, } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryParIter<'w, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryParIter<'w, 's, Q, F> { /// Changes the batching strategy used when iterating. /// /// For more information on how this affects the resultant iteration, see diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index ee962498f1..dbd69e6497 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -6,7 +6,7 @@ use crate::{ prelude::{Component, FromWorld}, query::{ Access, BatchingStrategy, DebugCheckedUnwrap, FilteredAccess, QueryCombinationIter, - QueryIter, QueryParIter, WorldQuery, + QueryIter, QueryParIter, }, storage::{TableId, TableRow}, world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId}, @@ -18,15 +18,15 @@ use std::{any::TypeId, borrow::Borrow, fmt, mem::MaybeUninit}; use super::{ NopWorldQuery, QueryComponentError, QueryEntityError, QueryManyIter, QuerySingleError, - ROQueryItem, ReadOnlyWorldQuery, + ROQueryItem, WorldQueryData, WorldQueryFilter, }; -/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter. +/// Provides scoped access to a [`World`] state according to a given [`WorldQueryData`] and [`WorldQueryFilter`]. #[repr(C)] // SAFETY NOTE: // Do not add any new fields that use the `Q` or `F` generic parameters as this may // make `QueryState::as_transmuted_state` unsound if not done with care. -pub struct QueryState { +pub struct QueryState { world_id: WorldId, pub(crate) archetype_generation: ArchetypeGeneration, pub(crate) matched_tables: FixedBitSet, @@ -43,7 +43,7 @@ pub struct QueryState { par_iter_span: Span, } -impl std::fmt::Debug for QueryState { +impl std::fmt::Debug for QueryState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("QueryState") .field("world_id", &self.world_id) @@ -53,18 +53,18 @@ impl std::fmt::Debug for QueryState } } -impl FromWorld for QueryState { +impl FromWorld for QueryState { fn from_world(world: &mut World) -> Self { world.query_filtered() } } -impl QueryState { +impl QueryState { /// Converts this `QueryState` reference to a `QueryState` that does not access anything mutably. - pub fn as_readonly(&self) -> &QueryState { + pub fn as_readonly(&self) -> &QueryState { // SAFETY: invariant on `WorldQuery` trait upholds that `Q::ReadOnly` and `F::ReadOnly` // have a subset of the access, and match the exact same archetypes/tables as `Q`/`F` respectively. - unsafe { self.as_transmuted_state::() } + unsafe { self.as_transmuted_state::() } } /// Converts this `QueryState` reference to a `QueryState` that does not return any data @@ -88,8 +88,8 @@ impl QueryState { /// `NewQ` must have a subset of the access that `Q` does and match the exact same archetypes/tables /// `NewF` must have a subset of the access that `F` does and match the exact same archetypes/tables pub(crate) unsafe fn as_transmuted_state< - NewQ: WorldQuery, - NewF: ReadOnlyWorldQuery, + NewQ: WorldQueryData, + NewF: WorldQueryFilter, >( &self, ) -> &QueryState { @@ -97,7 +97,7 @@ impl QueryState { } } -impl QueryState { +impl QueryState { /// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`. pub fn new(world: &mut World) -> Self { let fetch_state = Q::init_state(world); @@ -162,7 +162,7 @@ impl QueryState { /// /// # Safety /// - /// - `world` must have permission to read any components required by this instance's `F` [`WorldQuery`]. + /// - `world` must have permission to read any components required by this instance's `F` [`WorldQueryFilter`]. /// - `world` must match the one used to create this [`QueryState`]. #[inline] pub(crate) unsafe fn is_empty_unsafe_world_cell( @@ -701,10 +701,7 @@ impl QueryState { /// /// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries. #[inline] - pub fn iter<'w, 's>( - &'s mut self, - world: &'w World, - ) -> QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly> { + pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q::ReadOnly, F> { self.update_archetypes(world); // SAFETY: query is read only unsafe { @@ -733,10 +730,7 @@ impl QueryState { /// /// This can only be called for read-only queries. #[inline] - pub fn iter_manual<'w, 's>( - &'s self, - world: &'w World, - ) -> QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly> { + pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q::ReadOnly, F> { self.validate_world(world.id()); // SAFETY: query is read only and world is validated unsafe { @@ -773,7 +767,7 @@ impl QueryState { pub fn iter_combinations<'w, 's, const K: usize>( &'s mut self, world: &'w World, - ) -> QueryCombinationIter<'w, 's, Q::ReadOnly, F::ReadOnly, K> { + ) -> QueryCombinationIter<'w, 's, Q::ReadOnly, F, K> { self.update_archetypes(world); // SAFETY: query is read only unsafe { @@ -833,7 +827,7 @@ impl QueryState { &'s mut self, world: &'w World, entities: EntityList, - ) -> QueryManyIter<'w, 's, Q::ReadOnly, F::ReadOnly, EntityList::IntoIter> + ) -> QueryManyIter<'w, 's, Q::ReadOnly, F, EntityList::IntoIter> where EntityList::Item: Borrow, { @@ -868,7 +862,7 @@ impl QueryState { &'s self, world: &'w World, entities: EntityList, - ) -> QueryManyIter<'w, 's, Q::ReadOnly, F::ReadOnly, EntityList::IntoIter> + ) -> QueryManyIter<'w, 's, Q::ReadOnly, F, EntityList::IntoIter> where EntityList::Item: Borrow, { @@ -1072,7 +1066,7 @@ impl QueryState { pub fn par_iter<'w, 's>( &'s mut self, world: &'w World, - ) -> QueryParIter<'w, 's, Q::ReadOnly, F::ReadOnly> { + ) -> QueryParIter<'w, 's, Q::ReadOnly, F> { self.update_archetypes(world); QueryParIter { world: world.as_unsafe_world_cell_readonly(), diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs new file mode 100644 index 0000000000..8e867750a6 --- /dev/null +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -0,0 +1,230 @@ +use crate::{ + archetype::{Archetype, ArchetypeComponentId}, + component::{ComponentId, Tick}, + entity::Entity, + query::{Access, FilteredAccess}, + storage::{Table, TableRow}, + world::{unsafe_world_cell::UnsafeWorldCell, World}, +}; +use bevy_utils::all_tuples; + +/// Types that can be used as parameters in a [`Query`]. +/// Types that implement this should also implement either [`WorldQueryData`] or [`WorldQueryFilter`] +/// +/// # Safety +/// +/// Implementor must ensure that +/// [`update_component_access`], [`update_archetype_component_access`], [`matches_component_set`], and [`fetch`] +/// obey the following: +/// +/// - For each component mutably accessed by [`fetch`], [`update_component_access`] should add write access unless read or write access has already been added, in which case it should panic. +/// - For each component readonly accessed by [`fetch`], [`update_component_access`] should add read access unless write access has already been added, in which case it should panic. +/// - For each component mutably accessed by [`fetch`], [`update_archetype_component_access`] should add write access if that component belongs to the archetype. +/// - For each component readonly accessed by [`fetch`], [`update_archetype_component_access`] should add read access if that component belongs to the archetype. +/// - If `fetch` mutably accesses the same component twice, [`update_component_access`] should panic. +/// - [`update_component_access`] may not add a `Without` filter for a component unless [`matches_component_set`] always returns `false` when the component set contains that component. +/// - [`update_component_access`] may not add a `With` filter for a component unless [`matches_component_set`] always returns `false` when the component set doesn't contain that component. +/// - In cases where the query represents a disjunction (such as an `Or` filter) where each element is a valid [`WorldQuery`], the following rules must be obeyed: +/// - [`matches_component_set`] must be a disjunction of the element's implementations +/// - [`update_component_access`] must replace the filters with a disjunction of filters +/// - Each filter in that disjunction must be a conjunction of the corresponding element's filter with the previous `access` +/// +/// When implementing [`update_component_access`], note that `add_read` and `add_write` both also add a `With` filter, whereas `extend_access` does not change the filters. +/// +/// [`fetch`]: Self::fetch +/// [`matches_component_set`]: Self::matches_component_set +/// [`Query`]: crate::system::Query +/// [`update_archetype_component_access`]: Self::update_archetype_component_access +/// [`update_component_access`]: Self::update_component_access +/// [`WorldQueryData`]: crate::query::WorldQueryData +/// [`WorldQueryFilter`]: crate::query::WorldQueryFilter +pub unsafe trait WorldQuery { + /// The item returned by this [`WorldQuery`] + /// For `WorldQueryData` this will be the item returned by the query. + /// For `WorldQueryFilter` this will be either `()`, or a `bool` indicating whether the entity should be included + /// or a tuple of such things. + type Item<'a>; + + /// Per archetype/table state used by this [`WorldQuery`] to fetch [`Self::Item`](crate::query::WorldQuery::Item) + type Fetch<'a>: Clone; + + /// State used to construct a [`Self::Fetch`](crate::query::WorldQuery::Fetch). This will be cached inside [`QueryState`](crate::query::QueryState), + /// so it is best to move as much data / computation here as possible to reduce the cost of + /// constructing [`Self::Fetch`](crate::query::WorldQuery::Fetch). + type State: Send + Sync + Sized; + + /// This function manually implements subtyping for the query items. + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort>; + + /// Creates a new instance of this fetch. + /// + /// # Safety + /// + /// - `world` must have permission to access any of the components specified in `Self::update_archetype_component_access`. + /// - `state` must have been initialized (via [`WorldQuery::init_state`]) using the same `world` passed + /// in to this function. + unsafe fn init_fetch<'w>( + world: UnsafeWorldCell<'w>, + state: &Self::State, + last_run: Tick, + this_run: Tick, + ) -> Self::Fetch<'w>; + + /// Returns true if (and only if) every table of every archetype matched by this fetch contains + /// all of the matched components. This is used to select a more efficient "table iterator" + /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before + /// [`WorldQuery::fetch`] can be called for iterators. If this returns false, + /// [`WorldQuery::set_archetype`] must be used before [`WorldQuery::fetch`] can be called for + /// iterators. + const IS_DENSE: bool; + + /// Adjusts internal state to account for the next [`Archetype`]. This will always be called on + /// archetypes that match this [`WorldQuery`]. + /// + /// # Safety + /// + /// - `archetype` and `tables` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. + /// - [`Self::update_archetype_component_access`] must have been previously called with `archetype`. + /// - `table` must correspond to `archetype`. + /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. + unsafe fn set_archetype<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + archetype: &'w Archetype, + table: &'w Table, + ); + + /// Adjusts internal state to account for the next [`Table`]. This will always be called on tables + /// that match this [`WorldQuery`]. + /// + /// # Safety + /// + /// - `table` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. + /// - `table` must belong to an archetype that was previously registered with + /// [`Self::update_archetype_component_access`]. + /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. + unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table); + + /// Fetch [`Self::Item`](`WorldQuery::Item`) for either the given `entity` in the current [`Table`], + /// or for the given `entity` in the current [`Archetype`]. This must always be called after + /// [`WorldQuery::set_table`] with a `table_row` in the range of the current [`Table`] or after + /// [`WorldQuery::set_archetype`] with a `entity` in the current archetype. + /// + /// # Safety + /// + /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and + /// `table_row` must be in the range of the current table and archetype. + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + ) -> Self::Item<'w>; + + /// Adds any component accesses used by this [`WorldQuery`] to `access`. + // This does not have a default body of `{}` because 99% of cases need to add accesses + // and forgetting to do so would be unsound. + fn update_component_access(state: &Self::State, access: &mut FilteredAccess); + + /// For the given `archetype`, adds any component accessed used by this [`WorldQuery`] to `access`. + // This does not have a default body of `{}` because 99% of cases need to add accesses + // and forgetting to do so would be unsound. + fn update_archetype_component_access( + state: &Self::State, + archetype: &Archetype, + access: &mut Access, + ); + + /// Creates and initializes a [`State`](WorldQuery::State) for this [`WorldQuery`] type. + fn init_state(world: &mut World) -> Self::State; + + /// Returns `true` if this query matches a set of components. Otherwise, returns `false`. + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool; +} + +macro_rules! impl_tuple_world_query { + ($(($name: ident, $state: ident)),*) => { + + #[allow(non_snake_case)] + #[allow(clippy::unused_unit)] + /// SAFETY: + /// `fetch` accesses are the conjunction of the subqueries' accesses + /// This is sound because `update_component_access` and `update_archetype_component_access` adds accesses according to the implementations of all the subqueries. + /// `update_component_access` adds all `With` and `Without` filters from the subqueries. + /// This is sound because `matches_component_set` always returns `false` if any the subqueries' implementations return `false`. + unsafe impl<$($name: WorldQuery),*> WorldQuery for ($($name,)*) { + type Fetch<'w> = ($($name::Fetch<'w>,)*); + type Item<'w> = ($($name::Item<'w>,)*); + type State = ($($name::State,)*); + + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + let ($($name,)*) = item; + ($( + $name::shrink($name), + )*) + } + + #[inline] + #[allow(clippy::unused_unit)] + unsafe fn init_fetch<'w>(_world: UnsafeWorldCell<'w>, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> { + let ($($name,)*) = state; + ($($name::init_fetch(_world, $name, _last_run, _this_run),)*) + } + + const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; + + #[inline] + unsafe fn set_archetype<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _archetype: &'w Archetype, + _table: &'w Table + ) { + let ($($name,)*) = _fetch; + let ($($state,)*) = _state; + $($name::set_archetype($name, $state, _archetype, _table);)* + } + + #[inline] + unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + let ($($name,)*) = _fetch; + let ($($state,)*) = _state; + $($name::set_table($name, $state, _table);)* + } + + #[inline(always)] + #[allow(clippy::unused_unit)] + unsafe fn fetch<'w>( + _fetch: &mut Self::Fetch<'w>, + _entity: Entity, + _table_row: TableRow + ) -> Self::Item<'w> { + let ($($name,)*) = _fetch; + ($($name::fetch($name, _entity, _table_row),)*) + } + + fn update_component_access(state: &Self::State, _access: &mut FilteredAccess) { + let ($($name,)*) = state; + $($name::update_component_access($name, _access);)* + } + + fn update_archetype_component_access(state: &Self::State, _archetype: &Archetype, _access: &mut Access) { + let ($($name,)*) = state; + $($name::update_archetype_component_access($name, _archetype, _access);)* + } + + fn init_state(_world: &mut World) -> Self::State { + ($($name::init_state(_world),)*) + } + + fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool { + let ($($name,)*) = state; + true $(&& $name::matches_component_set($name, _set_contains_id))* + } + } + }; +} + +all_tuples!(impl_tuple_world_query, 0, 15, F, S); diff --git a/crates/bevy_ecs/src/system/exclusive_system_param.rs b/crates/bevy_ecs/src/system/exclusive_system_param.rs index 5bc49b68fa..64789f357a 100644 --- a/crates/bevy_ecs/src/system/exclusive_system_param.rs +++ b/crates/bevy_ecs/src/system/exclusive_system_param.rs @@ -1,6 +1,6 @@ use crate::{ prelude::{FromWorld, QueryState}, - query::{ReadOnlyWorldQuery, WorldQuery}, + query::{WorldQueryData, WorldQueryFilter}, system::{Local, SystemMeta, SystemParam, SystemState}, world::World, }; @@ -29,7 +29,7 @@ pub trait ExclusiveSystemParam: Sized { /// for a given [`ExclusiveSystemParam`]. pub type ExclusiveSystemParamItem<'s, P> =

::Item<'s>; -impl<'a, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> ExclusiveSystemParam +impl<'a, Q: WorldQueryData + 'static, F: WorldQueryFilter + 'static> ExclusiveSystemParam for &'a mut QueryState { type State = QueryState; diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 36f822232a..f4f6e651df 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -3,8 +3,8 @@ use crate::{ entity::Entity, query::{ BatchingStrategy, QueryCombinationIter, QueryComponentError, QueryEntityError, QueryIter, - QueryManyIter, QueryParIter, QuerySingleError, QueryState, ROQueryItem, ReadOnlyWorldQuery, - WorldQuery, + QueryManyIter, QueryParIter, QuerySingleError, QueryState, ROQueryItem, + ReadOnlyWorldQueryData, WorldQueryData, WorldQueryFilter, }, world::{unsafe_world_cell::UnsafeWorldCell, Mut}, }; @@ -16,13 +16,15 @@ use std::{any::TypeId, borrow::Borrow}; /// Its iterators and getter methods return *query items*. /// Each query item is a type containing data relative to an entity. /// -/// `Query` is a generic data structure that accepts two type parameters, both of which must implement the [`WorldQuery`] trait: +/// `Query` is a generic data structure that accepts two type parameters: /// /// - **`Q` (query fetch).** /// The type of data contained in the query item. /// Only entities that match the requested data will generate an item. +/// Must implement the [`WorldQueryData`] trait. /// - **`F` (query filter).** /// A set of conditions that determines whether query items should be kept or discarded. +/// Must implement the [`WorldQueryFilter`] trait. /// This type parameter is optional. /// /// [`World`]: crate::world::World @@ -30,7 +32,7 @@ use std::{any::TypeId, borrow::Borrow}; /// # System parameter declaration /// /// A query should always be declared as a system parameter. -/// This section shows the most common idioms involving the declaration of `Query`, emerging by combining [`WorldQuery`] implementors. +/// This section shows the most common idioms involving the declaration of `Query`. /// /// ## Component access /// @@ -71,7 +73,7 @@ use std::{any::TypeId, borrow::Borrow}; /// # bevy_ecs::system::assert_is_system(system); /// ``` /// -/// ## `WorldQuery` tuples +/// ## `WorldQueryData` or `WorldQueryFilter` tuples /// /// Using tuples, each `Query` type parameter can contain multiple elements. /// @@ -323,7 +325,7 @@ use std::{any::TypeId, borrow::Borrow}; /// [`Table`]: crate::storage::Table /// [`With`]: crate::query::With /// [`Without`]: crate::query::Without -pub struct Query<'world, 'state, Q: WorldQuery, F: ReadOnlyWorldQuery = ()> { +pub struct Query<'world, 'state, Q: WorldQueryData, F: WorldQueryFilter = ()> { // SAFETY: Must have access to the components registered in `state`. world: UnsafeWorldCell<'world>, state: &'state QueryState, @@ -337,7 +339,7 @@ pub struct Query<'world, 'state, Q: WorldQuery, F: ReadOnlyWorldQuery = ()> { force_read_only_component_access: bool, } -impl std::fmt::Debug for Query<'_, '_, Q, F> { +impl std::fmt::Debug for Query<'_, '_, Q, F> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("Query") .field("matched_entities", &self.iter().count()) @@ -349,7 +351,7 @@ impl std::fmt::Debug for Query<'_, '_, Q, } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> Query<'w, 's, Q, F> { /// Creates a new query. /// /// # Panics @@ -384,7 +386,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// For example, `Query<(&mut A, &B, &mut C), With>` will become `Query<(&A, &B, &C), With>`. /// This can be useful when working around the borrow checker, /// or reusing functionality between systems via functions that accept query types. - pub fn to_readonly(&self) -> Query<'_, 's, Q::ReadOnly, F::ReadOnly> { + pub fn to_readonly(&self) -> Query<'_, 's, Q::ReadOnly, F> { let new_state = self.state.as_readonly(); // SAFETY: This is memory safe because it turns the query immutable. unsafe { @@ -425,7 +427,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// - [`iter_mut`](Self::iter_mut) for mutable query items. /// - [`for_each`](Self::for_each) for the closure based alternative. #[inline] - pub fn iter(&self) -> QueryIter<'_, 's, Q::ReadOnly, F::ReadOnly> { + pub fn iter(&self) -> QueryIter<'_, 's, Q::ReadOnly, F> { // SAFETY: // - `self.world` has permission to access the required components. // - The query is read-only, so it can be aliased even if it was originally mutable. @@ -491,7 +493,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { #[inline] pub fn iter_combinations( &self, - ) -> QueryCombinationIter<'_, 's, Q::ReadOnly, F::ReadOnly, K> { + ) -> QueryCombinationIter<'_, 's, Q::ReadOnly, F, K> { // SAFETY: // - `self.world` has permission to access the required components. // - The query is read-only, so it can be aliased even if it was originally mutable. @@ -574,7 +576,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { pub fn iter_many( &self, entities: EntityList, - ) -> QueryManyIter<'_, 's, Q::ReadOnly, F::ReadOnly, EntityList::IntoIter> + ) -> QueryManyIter<'_, 's, Q::ReadOnly, F, EntityList::IntoIter> where EntityList::Item: Borrow, { @@ -787,7 +789,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// [`par_iter_mut`]: Self::par_iter_mut /// [`World`]: crate::world::World #[inline] - pub fn par_iter(&self) -> QueryParIter<'_, '_, Q::ReadOnly, F::ReadOnly> { + pub fn par_iter(&self) -> QueryParIter<'_, '_, Q::ReadOnly, F> { QueryParIter { world: self.world, state: self.state.as_readonly(), @@ -1420,16 +1422,16 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> IntoIterator for &'w Query<'_, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> IntoIterator for &'w Query<'_, 's, Q, F> { type Item = ROQueryItem<'w, Q>; - type IntoIter = QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly>; + type IntoIter = QueryIter<'w, 's, Q::ReadOnly, F>; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> IntoIterator for &'w mut Query<'_, 's, Q, F> { +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> IntoIterator for &'w mut Query<'_, 's, Q, F> { type Item = Q::Item<'w>; type IntoIter = QueryIter<'w, 's, Q, F>; @@ -1438,7 +1440,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> IntoIterator for &'w mut Quer } } -impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { +impl<'w, 's, Q: ReadOnlyWorldQueryData, F: WorldQueryFilter> Query<'w, 's, Q, F> { /// Returns the query item for the given [`Entity`], with the actual "inner" world lifetime. /// /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is @@ -1509,7 +1511,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// # bevy_ecs::system::assert_is_system(report_names_system); /// ``` #[inline] - pub fn iter_inner(&self) -> QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly> { + pub fn iter_inner(&self) -> QueryIter<'w, 's, Q::ReadOnly, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 47d42c2906..84a37c9a75 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -6,7 +6,8 @@ use crate::{ component::{ComponentId, ComponentTicks, Components, Tick}, entity::Entities, query::{ - Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery, + Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQueryData, + WorldQueryData, WorldQueryFilter, }, system::{Query, SystemMeta}, world::{unsafe_world_cell::UnsafeWorldCell, FromWorld, World}, @@ -152,14 +153,14 @@ pub unsafe trait ReadOnlySystemParam: SystemParam {} pub type SystemParamItem<'w, 's, P> =

::Item<'w, 's>; // SAFETY: QueryState is constrained to read-only fetches, so it only reads World. -unsafe impl<'w, 's, Q: ReadOnlyWorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> +unsafe impl<'w, 's, Q: ReadOnlyWorldQueryData + 'static, F: WorldQueryFilter + 'static> ReadOnlySystemParam for Query<'w, 's, Q, F> { } // SAFETY: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. -unsafe impl SystemParam +unsafe impl SystemParam for Query<'_, '_, Q, F> { type State = QueryState; @@ -1557,7 +1558,6 @@ mod tests { use super::*; use crate::{ self as bevy_ecs, // Necessary for the `SystemParam` Derive when used inside `bevy_ecs`. - query::{ReadOnlyWorldQuery, WorldQuery}, system::{assert_is_system, Query}, }; use std::{cell::RefCell, marker::PhantomData}; @@ -1569,8 +1569,8 @@ mod tests { pub struct SpecialQuery< 'w, 's, - Q: WorldQuery + Send + Sync + 'static, - F: ReadOnlyWorldQuery + Send + Sync + 'static = (), + Q: WorldQueryData + Send + Sync + 'static, + F: WorldQueryFilter + Send + Sync + 'static = (), > { _query: Query<'w, 's, Q, F>, } @@ -1691,7 +1691,7 @@ mod tests { #[derive(SystemParam)] pub struct WhereParam<'w, 's, Q> where - Q: 'static + WorldQuery, + Q: 'static + WorldQueryData, { _q: Query<'w, 's, Q, ()>, } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 0de995257f..1b0eb9d07d 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -18,7 +18,7 @@ use crate::{ component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, Tick}, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, event::{Event, EventId, Events, SendBatchIds}, - query::{DebugCheckedUnwrap, QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery}, + query::{DebugCheckedUnwrap, QueryEntityError, QueryState, WorldQueryData, WorldQueryFilter}, removal_detection::RemovedComponentEvents, schedule::{Schedule, ScheduleLabel, Schedules}, storage::{ResourceData, Storages}, @@ -922,7 +922,7 @@ impl World { self.last_change_tick = self.increment_change_tick(); } - /// Returns [`QueryState`] for the given [`WorldQuery`], which is used to efficiently + /// Returns [`QueryState`] for the given [`WorldQueryData`], which is used to efficiently /// run queries on the [`World`] by storing and reusing the [`QueryState`]. /// ``` /// use bevy_ecs::{component::Component, entity::Entity, world::World}; @@ -985,11 +985,11 @@ impl World { /// ]); /// ``` #[inline] - pub fn query(&mut self) -> QueryState { + pub fn query(&mut self) -> QueryState { self.query_filtered::() } - /// Returns [`QueryState`] for the given filtered [`WorldQuery`], which is used to efficiently + /// Returns [`QueryState`] for the given filtered [`WorldQueryData`], which is used to efficiently /// run queries on the [`World`] by storing and reusing the [`QueryState`]. /// ``` /// use bevy_ecs::{component::Component, entity::Entity, world::World, query::With}; @@ -1009,7 +1009,7 @@ impl World { /// assert_eq!(matching_entities, vec![e2]); /// ``` #[inline] - pub fn query_filtered(&mut self) -> QueryState { + pub fn query_filtered(&mut self) -> QueryState { QueryState::new(self) } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_combinations_mut_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_combinations_mut_iterator_safety.stderr index 570cab0a54..7129822002 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_combinations_mut_iterator_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_combinations_mut_iterator_safety.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `&mut A: ReadOnlyWorldQuery` is not satisfied +error[E0277]: the trait bound `&mut A: ReadOnlyWorldQueryData` is not satisfied --> tests/ui/query_iter_combinations_mut_iterator_safety.rs:10:17 | 10 | is_iterator(iter) - | ----------- ^^^^ the trait `ReadOnlyWorldQuery` is not implemented for `&mut A` + | ----------- ^^^^ the trait `ReadOnlyWorldQueryData` is not implemented for `&mut A` | | | required by a bound introduced by this call | - = help: the following other types implement trait `ReadOnlyWorldQuery`: + = help: the following other types implement trait `ReadOnlyWorldQueryData`: bevy_ecs::change_detection::Ref<'__w, T> Has AnyOf<()> @@ -16,7 +16,7 @@ error[E0277]: the trait bound `&mut A: ReadOnlyWorldQuery` is not satisfied AnyOf<(F0, F1, F2, F3)> AnyOf<(F0, F1, F2, F3, F4)> and $N others - = note: `ReadOnlyWorldQuery` is implemented for `&A`, but not for `&mut A` + = note: `ReadOnlyWorldQueryData` is implemented for `&A`, but not for `&mut A` = note: required for `QueryCombinationIter<'_, '_, &mut A, (), _>` to implement `Iterator` note: required by a bound in `is_iterator` --> tests/ui/query_iter_combinations_mut_iterator_safety.rs:13:19 diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.stderr index 6fa4ef005c..9083289fe4 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `&mut A: ReadOnlyWorldQuery` is not satisfied +error[E0277]: the trait bound `&mut A: ReadOnlyWorldQueryData` is not satisfied --> tests/ui/query_iter_many_mut_iterator_safety.rs:10:17 | 10 | is_iterator(iter) - | ----------- ^^^^ the trait `ReadOnlyWorldQuery` is not implemented for `&mut A` + | ----------- ^^^^ the trait `ReadOnlyWorldQueryData` is not implemented for `&mut A` | | | required by a bound introduced by this call | - = help: the following other types implement trait `ReadOnlyWorldQuery`: + = help: the following other types implement trait `ReadOnlyWorldQueryData`: bevy_ecs::change_detection::Ref<'__w, T> Has AnyOf<()> @@ -16,7 +16,7 @@ error[E0277]: the trait bound `&mut A: ReadOnlyWorldQuery` is not satisfied AnyOf<(F0, F1, F2, F3)> AnyOf<(F0, F1, F2, F3, F4)> and $N others - = note: `ReadOnlyWorldQuery` is implemented for `&A`, but not for `&mut A` + = note: `ReadOnlyWorldQueryData` is implemented for `&A`, but not for `&mut A` = note: required for `QueryManyIter<'_, '_, &mut A, (), std::array::IntoIter>` to implement `Iterator` note: required by a bound in `is_iterator` --> tests/ui/query_iter_many_mut_iterator_safety.rs:13:19 diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_param_derive_readonly.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_param_derive_readonly.stderr index 7d952a9324..49e2eae85b 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_param_derive_readonly.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_param_derive_readonly.stderr @@ -6,13 +6,13 @@ warning: unused import: `SystemState` | = note: `#[warn(unused_imports)]` on by default -error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not satisfied +error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQueryData` is not satisfied --> tests/ui/system_param_derive_readonly.rs:18:23 | 18 | assert_readonly::(); - | ^^^^^^^ the trait `ReadOnlyWorldQuery` is not implemented for `&'static mut Foo` + | ^^^^^^^ the trait `ReadOnlyWorldQueryData` is not implemented for `&'static mut Foo` | - = help: the following other types implement trait `ReadOnlyWorldQuery`: + = help: the following other types implement trait `ReadOnlyWorldQueryData`: bevy_ecs::change_detection::Ref<'__w, T> Has AnyOf<()> @@ -22,7 +22,7 @@ error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not sati AnyOf<(F0, F1, F2, F3)> AnyOf<(F0, F1, F2, F3, F4)> and $N others - = note: `ReadOnlyWorldQuery` is implemented for `&'static Foo`, but not for `&'static mut Foo` + = note: `ReadOnlyWorldQueryData` is implemented for `&'static Foo`, but not for `&'static mut Foo` = note: required for `bevy_ecs::system::Query<'_, '_, &'static mut Foo>` to implement `ReadOnlySystemParam` = note: 1 redundant requirement hidden = note: required for `Mutable<'_, '_>` to implement `ReadOnlySystemParam` diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.rs index 1619384096..64ccd386da 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.rs @@ -1,21 +1,21 @@ use bevy_ecs::prelude::*; -use bevy_ecs::query::WorldQuery; +use bevy_ecs::query::WorldQueryData; #[derive(Component)] struct Foo; -#[derive(WorldQuery)] +#[derive(WorldQueryData)] struct MutableUnmarked { a: &'static mut Foo, } -#[derive(WorldQuery)] -#[world_query(mutable)] +#[derive(WorldQueryData)] +#[world_query_data(mutable)] struct MutableMarked { a: &'static mut Foo, } -#[derive(WorldQuery)] +#[derive(WorldQueryData)] struct NestedMutableUnmarked { a: MutableMarked, } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.stderr index 830a130e50..8942271b19 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/world_query_derive.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not satisfied +error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQueryData` is not satisfied --> tests/ui/world_query_derive.rs:9:8 | 9 | a: &'static mut Foo, - | ^^^^^^^^^^^^^^^^ the trait `ReadOnlyWorldQuery` is not implemented for `&'static mut Foo` + | ^^^^^^^^^^^^^^^^ the trait `ReadOnlyWorldQueryData` is not implemented for `&'static mut Foo` | - = help: the following other types implement trait `ReadOnlyWorldQuery`: + = help: the following other types implement trait `ReadOnlyWorldQueryData`: MutableUnmarked MutableMarkedReadOnly NestedMutableUnmarked @@ -17,17 +17,17 @@ error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not sati note: required by a bound in `_::assert_readonly` --> tests/ui/world_query_derive.rs:7:10 | -7 | #[derive(WorldQuery)] - | ^^^^^^^^^^ required by this bound in `assert_readonly` - = note: this error originates in the derive macro `WorldQuery` (in Nightly builds, run with -Z macro-backtrace for more info) +7 | #[derive(WorldQueryData)] + | ^^^^^^^^^^^^^^ required by this bound in `assert_readonly` + = note: this error originates in the derive macro `WorldQueryData` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `MutableMarked: ReadOnlyWorldQuery` is not satisfied +error[E0277]: the trait bound `MutableMarked: ReadOnlyWorldQueryData` is not satisfied --> tests/ui/world_query_derive.rs:20:8 | 20 | a: MutableMarked, - | ^^^^^^^^^^^^^ the trait `ReadOnlyWorldQuery` is not implemented for `MutableMarked` + | ^^^^^^^^^^^^^ the trait `ReadOnlyWorldQueryData` is not implemented for `MutableMarked` | - = help: the following other types implement trait `ReadOnlyWorldQuery`: + = help: the following other types implement trait `ReadOnlyWorldQueryData`: MutableUnmarked MutableMarkedReadOnly NestedMutableUnmarked @@ -40,6 +40,6 @@ error[E0277]: the trait bound `MutableMarked: ReadOnlyWorldQuery` is not satisfi note: required by a bound in `_::assert_readonly` --> tests/ui/world_query_derive.rs:18:10 | -18 | #[derive(WorldQuery)] - | ^^^^^^^^^^ required by this bound in `assert_readonly` - = note: this error originates in the derive macro `WorldQuery` (in Nightly builds, run with -Z macro-backtrace for more info) +18 | #[derive(WorldQueryData)] + | ^^^^^^^^^^^^^^ required by this bound in `assert_readonly` + = note: this error originates in the derive macro `WorldQueryData` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/bevy_hierarchy/src/query_extension.rs b/crates/bevy_hierarchy/src/query_extension.rs index 6ee6dc26fc..edb70bf425 100644 --- a/crates/bevy_hierarchy/src/query_extension.rs +++ b/crates/bevy_hierarchy/src/query_extension.rs @@ -2,14 +2,14 @@ use std::collections::VecDeque; use bevy_ecs::{ entity::Entity, - query::{ReadOnlyWorldQuery, WorldQuery}, + query::{WorldQuery, WorldQueryData, WorldQueryFilter}, system::Query, }; use crate::{Children, Parent}; /// An extension trait for [`Query`] that adds hierarchy related methods. -pub trait HierarchyQueryExt<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { +pub trait HierarchyQueryExt<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> { /// Returns an [`Iterator`] of [`Entity`]s over all of `entity`s descendants. /// /// Can only be called on a [`Query`] of [`Children`] (i.e. `Query<&Children>`). @@ -57,7 +57,7 @@ pub trait HierarchyQueryExt<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { Q::ReadOnly: WorldQuery = &'w Parent>; } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> HierarchyQueryExt<'w, 's, Q, F> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> HierarchyQueryExt<'w, 's, Q, F> for Query<'w, 's, Q, F> { fn iter_descendants(&'w self, entity: Entity) -> DescendantIter<'w, 's, Q, F> @@ -78,7 +78,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> HierarchyQueryExt<'w, 's, Q, /// An [`Iterator`] of [`Entity`]s over the descendants of an [`Entity`]. /// /// Traverses the hierarchy breadth-first. -pub struct DescendantIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> +pub struct DescendantIter<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> where Q::ReadOnly: WorldQuery = &'w Children>, { @@ -86,7 +86,7 @@ where vecdeque: VecDeque, } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> DescendantIter<'w, 's, Q, F> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> DescendantIter<'w, 's, Q, F> where Q::ReadOnly: WorldQuery = &'w Children>, { @@ -104,7 +104,7 @@ where } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Iterator for DescendantIter<'w, 's, Q, F> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> Iterator for DescendantIter<'w, 's, Q, F> where Q::ReadOnly: WorldQuery = &'w Children>, { @@ -122,7 +122,7 @@ where } /// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`]. -pub struct AncestorIter<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> +pub struct AncestorIter<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> where Q::ReadOnly: WorldQuery = &'w Parent>, { @@ -130,7 +130,7 @@ where next: Option, } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> AncestorIter<'w, 's, Q, F> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> AncestorIter<'w, 's, Q, F> where Q::ReadOnly: WorldQuery = &'w Parent>, { @@ -143,7 +143,7 @@ where } } -impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Iterator for AncestorIter<'w, 's, Q, F> +impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> Iterator for AncestorIter<'w, 's, Q, F> where Q::ReadOnly: WorldQuery = &'w Parent>, { diff --git a/crates/bevy_render/src/batching/mod.rs b/crates/bevy_render/src/batching/mod.rs index bb4fe79670..8d0fb9a601 100644 --- a/crates/bevy_render/src/batching/mod.rs +++ b/crates/bevy_render/src/batching/mod.rs @@ -1,7 +1,7 @@ use bevy_ecs::{ component::Component, prelude::Res, - query::{QueryItem, ReadOnlyWorldQuery}, + query::{QueryItem, ReadOnlyWorldQueryData, WorldQueryFilter}, system::{Query, ResMut, StaticSystemParam, SystemParam, SystemParamItem}, }; use bevy_utils::nonmax::NonMaxU32; @@ -57,8 +57,8 @@ impl BatchMeta { /// items. pub trait GetBatchData { type Param: SystemParam + 'static; - type Query: ReadOnlyWorldQuery; - type QueryFilter: ReadOnlyWorldQuery; + type Query: ReadOnlyWorldQueryData; + type QueryFilter: WorldQueryFilter; /// Data used for comparison between phase items. If the pipeline id, draw /// function id, per-instance data buffer dynamic offset and this data /// matches, the draws can be batched. diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 7577517f12..ee5c8c45ae 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -9,7 +9,7 @@ use bevy_asset::{Asset, Handle}; use bevy_ecs::{ component::Component, prelude::*, - query::{QueryItem, ReadOnlyWorldQuery, WorldQuery}, + query::{QueryItem, ReadOnlyWorldQueryData, WorldQueryFilter}, system::lifetimeless::Read, }; use std::{marker::PhantomData, ops::Deref}; @@ -35,10 +35,10 @@ impl DynamicUniformIndex { /// Therefore the component is transferred from the "app world" into the "render world" /// in the [`ExtractSchedule`] step. pub trait ExtractComponent: Component { - /// ECS [`WorldQuery`] to fetch the components to extract. - type Query: WorldQuery + ReadOnlyWorldQuery; + /// ECS [`ReadOnlyWorldQueryData`] to fetch the components to extract. + type Query: ReadOnlyWorldQueryData; /// Filters the entities with additional constraints. - type Filter: WorldQuery + ReadOnlyWorldQuery; + type Filter: WorldQueryFilter; /// The output from extraction. /// diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_render/src/extract_instances.rs index 82c04dba10..8fc925b8e9 100644 --- a/crates/bevy_render/src/extract_instances.rs +++ b/crates/bevy_render/src/extract_instances.rs @@ -11,7 +11,7 @@ use bevy_asset::{Asset, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ prelude::Entity, - query::{QueryItem, ReadOnlyWorldQuery, WorldQuery}, + query::{QueryItem, ReadOnlyWorldQueryData, WorldQueryFilter}, system::{lifetimeless::Read, Query, ResMut, Resource}, }; use bevy_utils::EntityHashMap; @@ -28,10 +28,10 @@ use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; /// [`ExtractComponent`](crate::extract_component::ExtractComponent), but /// higher-performance because it avoids the ECS overhead. pub trait ExtractInstance: Send + Sync + Sized + 'static { - /// ECS [`WorldQuery`] to fetch the components to extract. - type Query: WorldQuery + ReadOnlyWorldQuery; + /// ECS [`ReadOnlyWorldQueryData`] to fetch the components to extract. + type Query: ReadOnlyWorldQueryData; /// Filters the entities with additional constraints. - type Filter: WorldQuery + ReadOnlyWorldQuery; + type Filter: WorldQueryFilter; /// Defines how the component is transferred into the "render world". fn extract(item: QueryItem<'_, Self::Query>) -> Option; diff --git a/crates/bevy_render/src/render_graph/node.rs b/crates/bevy_render/src/render_graph/node.rs index 0e4630e3c7..aa4a786089 100644 --- a/crates/bevy_render/src/render_graph/node.rs +++ b/crates/bevy_render/src/render_graph/node.rs @@ -7,7 +7,7 @@ use crate::{ renderer::RenderContext, }; use bevy_ecs::{ - query::{QueryItem, QueryState, ReadOnlyWorldQuery}, + query::{QueryItem, QueryState, ReadOnlyWorldQueryData}, world::{FromWorld, World}, }; use downcast_rs::{impl_downcast, Downcast}; @@ -342,7 +342,7 @@ impl Node for RunGraphOnViewNode { pub trait ViewNode { /// The query that will be used on the view entity. /// It is guaranteed to run on the view entity, so there's no need for a filter - type ViewQuery: ReadOnlyWorldQuery; + type ViewQuery: ReadOnlyWorldQueryData; /// Updates internal node state using the current render [`World`] prior to the run method. fn update(&mut self, _world: &mut World) {} diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 1d439e501d..3ebf9b1550 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -2,7 +2,7 @@ use crate::render_phase::{PhaseItem, TrackedRenderPass}; use bevy_app::App; use bevy_ecs::{ entity::Entity, - query::{QueryState, ROQueryItem, ReadOnlyWorldQuery}, + query::{QueryState, ROQueryItem, ReadOnlyWorldQueryData}, system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState}, world::World, }; @@ -179,12 +179,12 @@ pub trait RenderCommand { /// The view entity refers to the camera, or shadow-casting light, etc. from which the phase /// item will be rendered from. /// All components have to be accessed read only. - type ViewWorldQuery: ReadOnlyWorldQuery; + type ViewWorldQuery: ReadOnlyWorldQueryData; /// Specifies the ECS data of the item entity required by [`RenderCommand::render`]. /// /// The item is the entity that will be rendered for the corresponding view. /// All components have to be accessed read only. - type ItemWorldQuery: ReadOnlyWorldQuery; + type ItemWorldQuery: ReadOnlyWorldQueryData; /// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups, /// issuing draw calls, etc.) via the [`TrackedRenderPass`]. diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 49dc7be8c3..7302fdfc3e 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -3,7 +3,7 @@ use bevy_ecs::{ change_detection::DetectChangesMut, entity::Entity, prelude::{Component, With}, - query::WorldQuery, + query::WorldQueryData, reflect::ReflectComponent, system::{Local, Query, Res}, }; @@ -105,8 +105,8 @@ pub struct State { } /// Main query for [`ui_focus_system`] -#[derive(WorldQuery)] -#[world_query(mutable)] +#[derive(WorldQueryData)] +#[world_query_data(mutable)] pub struct NodeQuery { entity: Entity, node: &'static Node, diff --git a/examples/ecs/custom_query_param.rs b/examples/ecs/custom_query_param.rs index 8cc5f17207..b53db4c399 100644 --- a/examples/ecs/custom_query_param.rs +++ b/examples/ecs/custom_query_param.rs @@ -12,7 +12,10 @@ //! //! For more details on the `WorldQuery` derive macro, see the trait documentation. -use bevy::{ecs::query::WorldQuery, prelude::*}; +use bevy::{ + ecs::query::{WorldQueryData, WorldQueryFilter}, + prelude::*, +}; use std::fmt::Debug; fn main() { @@ -42,8 +45,8 @@ struct ComponentD; #[derive(Component, Debug)] struct ComponentZ; -#[derive(WorldQuery)] -#[world_query(derive(Debug))] +#[derive(WorldQueryData)] +#[world_query_data(derive(Debug))] struct ReadOnlyCustomQuery { entity: Entity, a: &'static ComponentA, @@ -76,8 +79,8 @@ fn print_components_read_only( // suffix. // Note: if you want to use derive macros with read-only query variants, you need to pass them with // using the `derive` attribute. -#[derive(WorldQuery)] -#[world_query(mutable, derive(Debug))] +#[derive(WorldQueryData)] +#[world_query_data(mutable, derive(Debug))] struct CustomQuery { entity: Entity, a: &'static mut ComponentA, @@ -90,26 +93,26 @@ struct CustomQuery { } // This is a valid query as well, which would iterate over every entity. -#[derive(WorldQuery)] -#[world_query(derive(Debug))] +#[derive(WorldQueryData)] +#[world_query_data(derive(Debug))] struct EmptyQuery { empty: (), } -#[derive(WorldQuery)] -#[world_query(derive(Debug))] +#[derive(WorldQueryData)] +#[world_query_data(derive(Debug))] struct NestedQuery { c: &'static ComponentC, d: Option<&'static ComponentD>, } -#[derive(WorldQuery)] -#[world_query(derive(Debug))] +#[derive(WorldQueryData)] +#[world_query_data(derive(Debug))] struct GenericQuery { generic: (&'static T, &'static P), } -#[derive(WorldQuery)] +#[derive(WorldQueryFilter)] struct QueryFilter { _c: With, _d: With,