diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 56c8adb53aa5b..4dec9a41ae517 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -318,7 +318,8 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// ``` /// # Safety /// -/// component access of `ROQueryFetch` should be a subset of `QueryFetch` +/// component access of `ROQueryFetch` must be a subset of `QueryFetch` +/// and `ROQueryFetch` must match exactly the same archetypes/tables as `QueryFetch` pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w, _State = Self::State> { type ReadOnly: ReadOnlyWorldQuery; type State: FetchState; @@ -1608,6 +1609,25 @@ macro_rules! impl_anytuple_fetch { all_tuples!(impl_tuple_fetch, 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); + +/// SAFETY: `Self::ReadOnly` is `Self` +unsafe impl WorldQuery for NopWorldQuery { + type ReadOnly = Self; + type State = Q::State; + + fn shrink<'wlong: 'wshort, 'wshort>(_: ()) {} +} +impl<'a, Q: WorldQuery> WorldQueryGats<'a> for NopWorldQuery { + type Fetch = NopFetch>; + type _State = >::_State; +} +/// SAFETY: `NopFetch` never accesses any data +unsafe impl ReadOnlyWorldQuery for NopWorldQuery {} + /// [`Fetch`] that does not actually fetch anything /// /// Mostly useful when something is generic over the Fetch and you don't want to fetch as you will discard the result @@ -1616,18 +1636,18 @@ pub struct NopFetch { } // SAFETY: NopFetch doesnt access anything -unsafe impl<'w, State: FetchState> Fetch<'w> for NopFetch { +unsafe impl<'w, F: Fetch<'w>> Fetch<'w> for NopFetch { type Item = (); - type State = State; + type State = F::State; - const IS_DENSE: bool = true; + const IS_DENSE: bool = F::IS_DENSE; const IS_ARCHETYPAL: bool = true; #[inline(always)] unsafe fn init( _world: &'w World, - _state: &State, + _state: &F::State, _last_change_tick: u32, _change_tick: u32, ) -> Self { diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 498e04fe4245d..e5bdc4770a9e8 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -13,17 +13,14 @@ use super::{QueryFetch, QueryItem, ReadOnlyWorldQuery}; /// /// 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, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { +pub struct QueryIter<'w, 's, Q: WorldQuery, F: WorldQuery> { tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, - cursor: QueryIterationCursor<'w, 's, Q, QF, F>, + cursor: QueryIterationCursor<'w, 's, Q, F>, } -impl<'w, 's, Q: WorldQuery, QF, F: WorldQuery> QueryIter<'w, 's, Q, QF, F> -where - QF: Fetch<'w, State = Q::State>, -{ +impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIter<'w, 's, Q, F> { /// # Safety /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. @@ -44,11 +41,8 @@ where } } -impl<'w, 's, Q: WorldQuery, QF, F: WorldQuery> Iterator for QueryIter<'w, 's, Q, QF, F> -where - QF: Fetch<'w, State = Q::State>, -{ - type Item = QF::Item; +impl<'w, 's, Q: WorldQuery, F: WorldQuery> Iterator for QueryIter<'w, 's, Q, F> { + type Item = QueryItem<'w, Q>; #[inline(always)] fn next(&mut self) -> Option { @@ -69,42 +63,32 @@ where .map(|id| self.archetypes[*id].len()) .sum(); - let archetype_query = F::Fetch::IS_ARCHETYPAL && QF::IS_ARCHETYPAL; + let archetype_query = Q::Fetch::IS_ARCHETYPAL && F::Fetch::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, QF, F: WorldQuery> FusedIterator for QueryIter<'w, 's, Q, QF, F> where - QF: Fetch<'w, State = Q::State> -{ -} +impl<'w, 's, Q: WorldQuery, F: WorldQuery> FusedIterator for QueryIter<'w, 's, Q, F> {} /// An [`Iterator`] over query results of a [`Query`](crate::system::Query). /// /// This struct is created by the [`Query::iter_many`](crate::system::Query::iter_many) method. -pub struct QueryManyIter< - 'w, - 's, - Q: WorldQuery, - QF: Fetch<'w, State = Q::State>, - F: WorldQuery, - I: Iterator, -> where +pub struct QueryManyIter<'w, 's, Q: WorldQuery, F: WorldQuery, I: Iterator> +where I::Item: Borrow, { entity_iter: I, entities: &'w Entities, tables: &'w Tables, archetypes: &'w Archetypes, - fetch: QF, + fetch: QueryFetch<'w, Q>, filter: QueryFetch<'w, F>, query_state: &'s QueryState, } -impl<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery, I: Iterator> - QueryManyIter<'w, 's, Q, QF, F, I> +impl<'w, 's, Q: WorldQuery, F: WorldQuery, I: Iterator> QueryManyIter<'w, 's, Q, F, I> where I::Item: Borrow, { @@ -119,14 +103,14 @@ where entity_list: EntityList, last_change_tick: u32, change_tick: u32, - ) -> QueryManyIter<'w, 's, Q, QF, F, I> { - let fetch = QF::init( + ) -> QueryManyIter<'w, 's, Q, F, I> { + let fetch = Q::Fetch::init( world, &query_state.fetch_state, last_change_tick, change_tick, ); - let filter = QueryFetch::::init( + let filter = F::Fetch::init( world, &query_state.filter_state, last_change_tick, @@ -144,12 +128,11 @@ where } } -impl<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery, I: Iterator> Iterator - for QueryManyIter<'w, 's, Q, QF, F, I> +impl<'w, 's, Q: WorldQuery, F: WorldQuery, I: Iterator> Iterator for QueryManyIter<'w, 's, Q, F, I> where I::Item: Borrow, { - type Item = QF::Item; + type Item = QueryItem<'w, Q>; #[inline(always)] fn next(&mut self) -> Option { @@ -201,7 +184,7 @@ pub struct QueryCombinationIter<'w, 's, Q: WorldQuery, F: WorldQuery, const K: u tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, - cursors: [QueryIterationCursor<'w, 's, Q, QueryFetch<'w, Q>, F>; K], + cursors: [QueryIterationCursor<'w, 's, Q, F>; K], } impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<'w, 's, Q, F, K> { @@ -219,11 +202,10 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter< // Initialize array with cursors. // There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit - let mut array: MaybeUninit<[QueryIterationCursor<'w, 's, Q, QueryFetch<'w, Q>, F>; K]> = - MaybeUninit::uninit(); + let mut array: MaybeUninit<[QueryIterationCursor<'w, 's, Q, F>; K]> = MaybeUninit::uninit(); let ptr = array .as_mut_ptr() - .cast::, F>>(); + .cast::>(); if K != 0 { ptr.write(QueryIterationCursor::init( world, @@ -367,10 +349,9 @@ where } } -impl<'w, 's, Q: WorldQuery, QF, F> ExactSizeIterator for QueryIter<'w, 's, Q, QF, F> +impl<'w, 's, Q: WorldQuery, F: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, F> where - QF: Fetch<'w, State = Q::State>, - F: WorldQuery + ArchetypeFilter, + F: ArchetypeFilter, { fn len(&self) -> usize { self.query_state @@ -405,21 +386,21 @@ where { } -struct QueryIterationCursor<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { +struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, - fetch: QF, + fetch: QueryFetch<'w, Q>, filter: QueryFetch<'w, F>, // length of the table table or length of the archetype, depending on whether both `Q`'s and `F`'s fetches are dense current_len: usize, // either table row or archetype index, depending on whether both `Q`'s and `F`'s fetches are dense current_index: usize, - phantom: PhantomData<(&'w (), Q)>, + phantom: PhantomData, } -impl<'w, 's, Q: WorldQuery, QF, F: WorldQuery> Clone for QueryIterationCursor<'w, 's, Q, QF, F> +impl<'w, 's, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'w, 's, Q, F> where - QF: Fetch<'w, State = Q::State> + Clone, + QueryFetch<'w, Q>: Clone, QueryFetch<'w, F>: Clone, { fn clone(&self) -> Self { @@ -435,11 +416,8 @@ where } } -impl<'w, 's, Q: WorldQuery, QF, F: WorldQuery> QueryIterationCursor<'w, 's, Q, QF, F> -where - QF: Fetch<'w, State = Q::State>, -{ - const IS_DENSE: bool = QF::IS_DENSE && >::IS_DENSE; +impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> { + const IS_DENSE: bool = Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE; unsafe fn init_empty( world: &'w World, @@ -460,13 +438,13 @@ where last_change_tick: u32, change_tick: u32, ) -> Self { - let fetch = QF::init( + let fetch = Q::Fetch::init( world, &query_state.fetch_state, last_change_tick, change_tick, ); - let filter = QueryFetch::::init( + let filter = F::Fetch::init( world, &query_state.filter_state, last_change_tick, @@ -485,7 +463,7 @@ where /// retrieve item returned from most recent `next` call again. #[inline] - unsafe fn peek_last(&mut self) -> Option { + unsafe fn peek_last(&mut self) -> Option> { if self.current_index > 0 { if Self::IS_DENSE { Some(self.fetch.table_fetch(self.current_index - 1)) @@ -509,7 +487,7 @@ where tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, - ) -> Option { + ) -> Option> { if Self::IS_DENSE { loop { // we are on the beginning of the query, or finished processing a table, so skip to the next diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 2a6da06c314f1..16dc86db4062e 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -21,8 +21,8 @@ pub(crate) unsafe fn debug_checked_unreachable() -> ! { mod tests { use super::WorldQuery; use crate::prelude::{AnyOf, Entity, Or, QueryState, With, Without}; - use crate::query::{ArchetypeFilter, QueryCombinationIter, QueryFetch, ReadOnlyWorldQuery}; - use crate::system::{IntoSystem, Query, System}; + use crate::query::{ArchetypeFilter, QueryCombinationIter, QueryFetch}; + use crate::system::{IntoSystem, Query, System, SystemState}; use crate::{self as bevy_ecs, component::Component, world::World}; use std::any::type_name; use std::collections::HashSet; @@ -67,10 +67,11 @@ mod tests { } fn assert_combination(world: &mut World, expected_size: usize) where - Q: ReadOnlyWorldQuery, - F: ReadOnlyWorldQuery + ArchetypeFilter, - for<'w> QueryFetch<'w, Q>: Clone, - for<'w> QueryFetch<'w, F>: Clone, + Q: WorldQuery, + F: WorldQuery, + F::ReadOnly: ArchetypeFilter, + for<'w> QueryFetch<'w, Q::ReadOnly>: Clone, + for<'w> QueryFetch<'w, F::ReadOnly>: Clone, { let mut query = world.query_filtered::(); let iter = query.iter_combinations::(world); @@ -79,10 +80,11 @@ mod tests { } fn assert_all_sizes_equal(world: &mut World, expected_size: usize) where - Q: ReadOnlyWorldQuery, - F: ReadOnlyWorldQuery + ArchetypeFilter, - for<'w> QueryFetch<'w, Q>: Clone, - for<'w> QueryFetch<'w, F>: Clone, + Q: WorldQuery, + F: WorldQuery, + F::ReadOnly: ArchetypeFilter, + for<'w> QueryFetch<'w, Q::ReadOnly>: Clone, + for<'w> QueryFetch<'w, F::ReadOnly>: Clone, { let mut query = world.query_filtered::(); let iter = query.iter(world); @@ -653,4 +655,42 @@ count(): {count}"# system.run((), &mut world); } } + + #[test] + fn mut_to_immut_query_methods_have_immut_item() { + #[derive(Component)] + struct Foo; + + let mut world = World::new(); + let e = world.spawn().insert(Foo).id(); + + // state + let mut q = world.query::<&mut Foo>(); + let _: Option<&Foo> = q.iter(&world).next(); + let _: Option<[&Foo; 2]> = q.iter_combinations::<2>(&world).next(); + let _: Option<&Foo> = q.iter_manual(&world).next(); + let _: Option<&Foo> = q.iter_many(&world, [e]).next(); + q.for_each(&world, |_: &Foo| ()); + + let _: Option<&Foo> = q.get(&world, e).ok(); + let _: Option<&Foo> = q.get_manual(&world, e).ok(); + let _: Option<[&Foo; 1]> = q.get_many(&world, [e]).ok(); + let _: Option<&Foo> = q.get_single(&world).ok(); + let _: &Foo = q.single(&world); + + // system param + let mut q = SystemState::>::new(&mut world); + let q = q.get_mut(&mut world); + let _: Option<&Foo> = q.iter().next(); + let _: Option<[&Foo; 2]> = q.iter_combinations::<2>().next(); + let _: Option<&Foo> = q.iter_many([e]).next(); + q.for_each(|_: &Foo| ()); + + let _: Option<&Foo> = q.get(e).ok(); + let _: Option<&Foo> = q.get_component(e).ok(); + let _: Option<[&Foo; 1]> = q.get_many([e]).ok(); + let _: Option<&Foo> = q.get_single().ok(); + let _: [&Foo; 1] = q.many([e]); + let _: &Foo = q.single(); + } } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 471b84e9581f6..95b058b76f70f 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -4,8 +4,7 @@ use crate::{ entity::Entity, prelude::FromWorld, query::{ - Access, Fetch, FetchState, FilteredAccess, NopFetch, QueryCombinationIter, QueryIter, - WorldQuery, + Access, Fetch, FetchState, FilteredAccess, QueryCombinationIter, QueryIter, WorldQuery, }, storage::TableId, world::{World, WorldId}, @@ -16,9 +15,13 @@ use bevy_utils::tracing::Instrument; use fixedbitset::FixedBitSet; use std::{borrow::Borrow, fmt}; -use super::{QueryFetch, QueryItem, QueryManyIter, ROQueryFetch, ROQueryItem}; +use super::{NopWorldQuery, QueryFetch, QueryItem, QueryManyIter, ROQueryItem}; /// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter. +#[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 { world_id: WorldId, pub(crate) archetype_generation: ArchetypeGeneration, @@ -40,6 +43,44 @@ impl FromWorld for QueryState { } } +impl QueryState { + /// Converts this `QueryState` reference to a `QueryState` that does not access anything mutably. + 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::() } + } + + /// Converts this `QueryState` reference to a `QueryState` that does not return any data + /// which can be faster. + /// + /// This doesn't use `NopWorldQuery` as it loses filter functionality, for example + /// `NopWorldQuery>` is functionally equivelent to `With`. + pub fn as_nop(&self) -> &QueryState, F> { + // SAFETY: `NopWorldQuery` doesn't have any accesses and defers to + // `Q` for table/archetype matching + unsafe { self.as_transmuted_state::, F>() } + } + + /// Converts this `QueryState` reference to any other `QueryState` with + /// the same `WorldQuery::State` associated types. + /// + /// Consider using `as_readonly` or `as_nop` instead which are safe functions. + /// + /// # SAFETY + /// + /// `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: WorldQuery, + >( + &self, + ) -> &QueryState { + &*(self as *const QueryState as *const 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 { @@ -83,7 +124,8 @@ impl QueryState { pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool { // SAFETY: NopFetch does not access any members while &self ensures no one has exclusive access unsafe { - self.iter_unchecked_manual::>(world, last_change_tick, change_tick) + self.as_nop() + .iter_unchecked_manual(world, last_change_tick, change_tick) .next() .is_none() } @@ -162,7 +204,7 @@ impl QueryState { self.update_archetypes(world); // SAFETY: query is read only unsafe { - self.get_unchecked_manual::>( + self.as_readonly().get_unchecked_manual( world, entity, world.last_change_tick(), @@ -232,7 +274,7 @@ impl QueryState { self.update_archetypes(world); // SAFETY: query has unique world access unsafe { - self.get_unchecked_manual::>( + self.get_unchecked_manual( world, entity, world.last_change_tick(), @@ -308,7 +350,7 @@ impl QueryState { self.validate_world(world); // SAFETY: query is read only and world is validated unsafe { - self.get_unchecked_manual::>( + self.as_readonly().get_unchecked_manual( world, entity, world.last_change_tick(), @@ -330,7 +372,7 @@ impl QueryState { entity: Entity, ) -> Result, QueryEntityError> { self.update_archetypes(world); - self.get_unchecked_manual::>( + self.get_unchecked_manual( world, entity, world.last_change_tick(), @@ -348,13 +390,13 @@ impl QueryState { /// /// This must be called on the same `World` that the `Query` was generated from: /// use `QueryState::validate_world` to verify this. - pub(crate) unsafe fn get_unchecked_manual<'w, QF: Fetch<'w, State = Q::State>>( + pub(crate) unsafe fn get_unchecked_manual<'w>( &self, world: &'w World, entity: Entity, last_change_tick: u32, change_tick: u32, - ) -> Result { + ) -> Result, QueryEntityError> { let location = world .entities .get(entity) @@ -366,7 +408,8 @@ impl QueryState { return Err(QueryEntityError::QueryDoesNotMatch(entity)); } let archetype = &world.archetypes[location.archetype_id]; - let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick); + let mut fetch = + as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick); let mut filter = as Fetch>::init( world, &self.filter_state, @@ -400,12 +443,8 @@ impl QueryState { // SAFETY: fetch is read-only // and world must be validated let array_of_results = entities.map(|entity| { - self.get_unchecked_manual::>( - world, - entity, - last_change_tick, - change_tick, - ) + self.as_readonly() + .get_unchecked_manual(world, entity, last_change_tick, change_tick) }); // TODO: Replace with TryMap once https://github.com/rust-lang/rust/issues/79711 is stabilized @@ -447,14 +486,8 @@ impl QueryState { } } - let array_of_results = entities.map(|entity| { - self.get_unchecked_manual::>( - world, - entity, - last_change_tick, - change_tick, - ) - }); + let array_of_results = entities + .map(|entity| self.get_unchecked_manual(world, entity, last_change_tick, change_tick)); // If any of the get calls failed, bubble up the error for result in &array_of_results { @@ -475,20 +508,21 @@ impl QueryState { pub fn iter<'w, 's>( &'s mut self, world: &'w World, - ) -> QueryIter<'w, 's, Q, ROQueryFetch<'w, Q>, F> { + ) -> QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly> { // SAFETY: query is read only unsafe { self.update_archetypes(world); - self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) + self.as_readonly().iter_unchecked_manual( + world, + world.last_change_tick(), + world.read_change_tick(), + ) } } /// Returns an [`Iterator`] over the query results for the given [`World`]. #[inline] - pub fn iter_mut<'w, 's>( - &'s mut self, - world: &'w mut World, - ) -> QueryIter<'w, 's, Q, QueryFetch<'w, Q>, F> { + pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> { // SAFETY: query has unique world access unsafe { self.update_archetypes(world); @@ -504,11 +538,15 @@ impl QueryState { pub fn iter_manual<'w, 's>( &'s self, world: &'w World, - ) -> QueryIter<'w, 's, Q, ROQueryFetch<'w, Q>, F> { + ) -> QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly> { self.validate_world(world); // SAFETY: query is read only and world is validated unsafe { - self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) + self.as_readonly().iter_unchecked_manual( + world, + world.last_change_tick(), + world.read_change_tick(), + ) } } @@ -526,11 +564,11 @@ impl QueryState { pub fn iter_combinations<'w, 's, const K: usize>( &'s mut self, world: &'w World, - ) -> QueryCombinationIter<'w, 's, Q, F, K> { + ) -> QueryCombinationIter<'w, 's, Q::ReadOnly, F::ReadOnly, K> { // SAFETY: query is read only unsafe { self.update_archetypes(world); - self.iter_combinations_unchecked_manual( + self.as_readonly().iter_combinations_unchecked_manual( world, world.last_change_tick(), world.read_change_tick(), @@ -571,14 +609,14 @@ impl QueryState { &'s mut self, world: &'w World, entities: EntityList, - ) -> QueryManyIter<'w, 's, Q, ROQueryFetch<'w, Q>, F, EntityList::IntoIter> + ) -> QueryManyIter<'w, 's, Q::ReadOnly, F::ReadOnly, EntityList::IntoIter> where EntityList::Item: Borrow, { // SAFETY: query is read only unsafe { self.update_archetypes(world); - self.iter_many_unchecked_manual( + self.as_readonly().iter_many_unchecked_manual( entities, world, world.last_change_tick(), @@ -597,7 +635,7 @@ impl QueryState { pub unsafe fn iter_unchecked<'w, 's>( &'s mut self, world: &'w World, - ) -> QueryIter<'w, 's, Q, QueryFetch<'w, Q>, F> { + ) -> QueryIter<'w, 's, Q, F> { self.update_archetypes(world); self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) } @@ -633,12 +671,12 @@ impl QueryState { /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` /// with a mismatched [`WorldId`] is unsound. #[inline] - pub(crate) unsafe fn iter_unchecked_manual<'w, 's, QF: Fetch<'w, State = Q::State>>( + pub(crate) unsafe fn iter_unchecked_manual<'w, 's>( &'s self, world: &'w World, last_change_tick: u32, change_tick: u32, - ) -> QueryIter<'w, 's, Q, QF, F> { + ) -> QueryIter<'w, 's, Q, F> { QueryIter::new(world, self, last_change_tick, change_tick) } @@ -649,22 +687,17 @@ impl QueryState { /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. - /// this does not check for entity uniqueness + /// This does not check for entity uniqueness /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` /// with a mismatched [`WorldId`] is unsound. #[inline] - pub(crate) unsafe fn iter_many_unchecked_manual< - 'w, - 's, - QF: Fetch<'w, State = Q::State>, - EntityList: IntoIterator, - >( + pub(crate) unsafe fn iter_many_unchecked_manual<'w, 's, EntityList: IntoIterator>( &'s self, entities: EntityList, world: &'w World, last_change_tick: u32, change_tick: u32, - ) -> QueryManyIter<'w, 's, Q, QF, F, EntityList::IntoIter> + ) -> QueryManyIter<'w, 's, Q, F, EntityList::IntoIter> where EntityList::Item: Borrow, { @@ -700,7 +733,7 @@ impl QueryState { // SAFETY: query is read only unsafe { self.update_archetypes(world); - self.for_each_unchecked_manual::, FN>( + self.as_readonly().for_each_unchecked_manual( world, func, world.last_change_tick(), @@ -720,7 +753,7 @@ impl QueryState { // SAFETY: query has unique world access unsafe { self.update_archetypes(world); - self.for_each_unchecked_manual::, FN>( + self.for_each_unchecked_manual( world, func, world.last_change_tick(), @@ -745,7 +778,7 @@ impl QueryState { func: FN, ) { self.update_archetypes(world); - self.for_each_unchecked_manual::, FN>( + self.for_each_unchecked_manual( world, func, world.last_change_tick(), @@ -771,7 +804,7 @@ impl QueryState { // SAFETY: query is read only unsafe { self.update_archetypes(world); - self.par_for_each_unchecked_manual::, FN>( + self.as_readonly().par_for_each_unchecked_manual( world, batch_size, func, @@ -796,7 +829,7 @@ impl QueryState { // SAFETY: query has unique world access unsafe { self.update_archetypes(world); - self.par_for_each_unchecked_manual::, FN>( + self.par_for_each_unchecked_manual( world, batch_size, func, @@ -826,7 +859,7 @@ impl QueryState { func: FN, ) { self.update_archetypes(world); - self.par_for_each_unchecked_manual::, FN>( + self.par_for_each_unchecked_manual( world, batch_size, func, @@ -868,11 +901,7 @@ impl QueryState { /// have unique access to the components they query. /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` /// with a mismatched [`WorldId`] is unsound. - pub(crate) unsafe fn for_each_unchecked_manual< - 'w, - QF: Fetch<'w, State = Q::State>, - FN: FnMut(QF::Item), - >( + pub(crate) unsafe fn for_each_unchecked_manual<'w, FN: FnMut(QueryItem<'w, Q>)>( &self, world: &'w World, mut func: FN, @@ -881,7 +910,8 @@ impl QueryState { ) { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual - let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick); + let mut fetch = + as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick); let mut filter = as Fetch>::init( world, &self.filter_state, @@ -938,8 +968,7 @@ impl QueryState { /// with a mismatched [`WorldId`] is unsound. pub(crate) unsafe fn par_for_each_unchecked_manual< 'w, - QF: Fetch<'w, State = Q::State>, - FN: Fn(QF::Item) + Send + Sync + Clone, + FN: Fn(QueryItem<'w, Q>) + Send + Sync + Clone, >( &self, world: &'w World, @@ -951,7 +980,7 @@ impl QueryState { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual ComputeTaskPool::get().scope(|scope| { - if QF::IS_DENSE && >::IS_DENSE { + if >::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; @@ -960,8 +989,12 @@ impl QueryState { let func = func.clone(); let len = batch_size.min(table.len() - offset); let task = async move { - let mut fetch = - QF::init(world, &self.fetch_state, last_change_tick, change_tick); + let mut fetch = as Fetch>::init( + world, + &self.fetch_state, + last_change_tick, + change_tick, + ); let mut filter = as Fetch>::init( world, &self.filter_state, @@ -1002,8 +1035,12 @@ impl QueryState { let func = func.clone(); let len = batch_size.min(archetype.len() - offset); let task = async move { - let mut fetch = - QF::init(world, &self.fetch_state, last_change_tick, change_tick); + let mut fetch = as Fetch>::init( + world, + &self.fetch_state, + last_change_tick, + change_tick, + ); let mut filter = as Fetch>::init( world, &self.filter_state, @@ -1130,7 +1167,7 @@ impl QueryState { // SAFETY: query is read only unsafe { - self.get_single_unchecked_manual::>( + self.as_readonly().get_single_unchecked_manual( world, world.last_change_tick(), world.read_change_tick(), @@ -1166,7 +1203,7 @@ impl QueryState { // SAFETY: query has unique world access unsafe { - self.get_single_unchecked_manual::>( + self.get_single_unchecked_manual( world, world.last_change_tick(), world.read_change_tick(), @@ -1189,12 +1226,7 @@ impl QueryState { world: &'w World, ) -> Result, QuerySingleError> { self.update_archetypes(world); - - self.get_single_unchecked_manual::>( - world, - world.last_change_tick(), - world.read_change_tick(), - ) + self.get_single_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) } /// Returns a query result when there is exactly one entity matching the query, @@ -1208,13 +1240,13 @@ impl QueryState { /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn get_single_unchecked_manual<'w, QF: Fetch<'w, State = Q::State>>( + pub unsafe fn get_single_unchecked_manual<'w>( &self, world: &'w World, last_change_tick: u32, change_tick: u32, - ) -> Result { - let mut query = self.iter_unchecked_manual::(world, last_change_tick, change_tick); + ) -> Result, QuerySingleError> { + let mut query = self.iter_unchecked_manual(world, last_change_tick, change_tick); let first = query.next(); let extra = query.next().is_some(); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 1c0352dade788..12a7f9b8b8fa0 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -2,9 +2,8 @@ use crate::{ component::Component, entity::Entity, query::{ - NopFetch, QueryCombinationIter, QueryEntityError, QueryFetch, QueryItem, QueryIter, - QueryManyIter, QuerySingleError, QueryState, ROQueryFetch, ROQueryItem, ReadOnlyWorldQuery, - WorldQuery, + QueryCombinationIter, QueryEntityError, QueryItem, QueryIter, QueryManyIter, + QuerySingleError, QueryState, ROQueryItem, ReadOnlyWorldQuery, WorldQuery, }, world::{Mut, World}, }; @@ -292,12 +291,15 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// # bevy_ecs::system::assert_is_system(report_names_system); /// ``` #[inline] - pub fn iter(&self) -> QueryIter<'_, 's, Q, ROQueryFetch<'_, Q>, F> { + pub fn iter(&self) -> QueryIter<'_, 's, Q::ReadOnly, F::ReadOnly> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state - .iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick) + self.state.as_readonly().iter_unchecked_manual( + self.world, + self.last_change_tick, + self.change_tick, + ) } } @@ -322,7 +324,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// # bevy_ecs::system::assert_is_system(gravity_system); /// ``` #[inline] - pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, QueryFetch<'_, Q>, F> { + pub fn iter_mut(&mut self) -> QueryIter<'_, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { @@ -339,11 +341,13 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// - if `K < N`: all possible `K`-sized combinations of query results, without repetition /// - if `K > N`: empty set (no `K`-sized combinations exist) #[inline] - pub fn iter_combinations(&self) -> QueryCombinationIter<'_, '_, Q, F, K> { + pub fn iter_combinations( + &self, + ) -> QueryCombinationIter<'_, '_, Q::ReadOnly, F::ReadOnly, K> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.iter_combinations_unchecked_manual( + self.state.as_readonly().iter_combinations_unchecked_manual( self.world, self.last_change_tick, self.change_tick, @@ -422,14 +426,14 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { pub fn iter_many( &self, entities: EntityList, - ) -> QueryManyIter<'_, '_, Q, ROQueryFetch<'_, Q>, F, EntityList::IntoIter> + ) -> QueryManyIter<'_, '_, Q::ReadOnly, F::ReadOnly, EntityList::IntoIter> where EntityList::Item: Borrow, { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.iter_many_unchecked_manual( + self.state.as_readonly().iter_many_unchecked_manual( entities, self.world, self.last_change_tick, @@ -445,7 +449,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// This function makes it possible to violate Rust's aliasing guarantees. You must make sure /// this call does not result in multiple mutable references to the same component #[inline] - pub unsafe fn iter_unsafe(&'s self) -> QueryIter<'w, 's, Q, QueryFetch<'w, Q>, F> { + pub unsafe fn iter_unsafe(&'s self) -> QueryIter<'w, 's, Q, F> { // SEMI-SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict self.state @@ -482,7 +486,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { pub unsafe fn iter_many_unsafe( &self, entities: EntityList, - ) -> QueryManyIter<'_, '_, Q, QueryFetch<'_, Q>, F, EntityList::IntoIter> + ) -> QueryManyIter<'_, '_, Q, F, EntityList::IntoIter> where EntityList::Item: Borrow, { @@ -522,7 +526,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.for_each_unchecked_manual::, _>( + self.state.as_readonly().for_each_unchecked_manual( self.world, f, self.last_change_tick, @@ -557,7 +561,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict unsafe { - self.state.for_each_unchecked_manual::, FN>( + self.state.for_each_unchecked_manual( self.world, f, self.last_change_tick, @@ -600,14 +604,13 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict unsafe { - self.state - .par_for_each_unchecked_manual::, _>( - self.world, - batch_size, - f, - self.last_change_tick, - self.change_tick, - ); + self.state.as_readonly().par_for_each_unchecked_manual( + self.world, + batch_size, + f, + self.last_change_tick, + self.change_tick, + ); }; } @@ -628,14 +631,13 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict unsafe { - self.state - .par_for_each_unchecked_manual::, FN>( - self.world, - batch_size, - f, - self.last_change_tick, - self.change_tick, - ); + self.state.par_for_each_unchecked_manual( + self.world, + batch_size, + f, + self.last_change_tick, + self.change_tick, + ); }; } @@ -724,7 +726,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.get_unchecked_manual::>( + self.state.as_readonly().get_unchecked_manual( self.world, entity, self.last_change_tick, @@ -826,7 +828,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.get_unchecked_manual::>( + self.state.get_unchecked_manual( self.world, entity, self.last_change_tick, @@ -919,12 +921,8 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { ) -> Result, QueryEntityError> { // SEMI-SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict - self.state.get_unchecked_manual::>( - self.world, - entity, - self.last_change_tick, - self.change_tick, - ) + self.state + .get_unchecked_manual(self.world, entity, self.last_change_tick, self.change_tick) } /// Returns a reference to the [`Entity`]'s [`Component`] of the given type. @@ -1121,7 +1119,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // the query ensures that the components it accesses are not mutably accessible somewhere else // and the query is read only. unsafe { - self.state.get_single_unchecked_manual::>( + self.state.as_readonly().get_single_unchecked_manual( self.world, self.last_change_tick, self.change_tick, @@ -1186,7 +1184,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // the query ensures mutable access to the components it accesses, and the query // is uniquely borrowed unsafe { - self.state.get_single_unchecked_manual::>( + self.state.get_single_unchecked_manual( self.world, self.last_change_tick, self.change_tick, @@ -1247,12 +1245,8 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: NopFetch does not access any members while &self ensures no one has exclusive access unsafe { self.state - .get_unchecked_manual::>( - self.world, - entity, - self.last_change_tick, - self.change_tick, - ) + .as_nop() + .get_unchecked_manual(self.world, entity, self.last_change_tick, self.change_tick) .is_ok() } } @@ -1260,16 +1254,16 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { impl<'w, 's, Q: WorldQuery, F: WorldQuery> IntoIterator for &'w Query<'_, 's, Q, F> { type Item = ROQueryItem<'w, Q>; - type IntoIter = QueryIter<'w, 's, Q, ROQueryFetch<'w, Q>, F>; + type IntoIter = QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly>; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl<'w, Q: WorldQuery, F: WorldQuery> IntoIterator for &'w mut Query<'_, '_, Q, F> { +impl<'w, 's, Q: WorldQuery, F: WorldQuery> IntoIterator for &'w mut Query<'_, 's, Q, F> { type Item = QueryItem<'w, Q>; - type IntoIter = QueryIter<'w, 'w, Q, QueryFetch<'w, Q>, F>; + type IntoIter = QueryIter<'w, 's, Q, F>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() @@ -1349,7 +1343,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.get_unchecked_manual::>( + self.state.as_readonly().get_unchecked_manual( self.world, entity, self.last_change_tick, @@ -1382,12 +1376,15 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// # bevy_ecs::system::assert_is_system(report_names_system); /// ``` #[inline] - pub fn iter_inner(&'s self) -> QueryIter<'w, 's, Q, ROQueryFetch<'w, Q>, F> { + pub fn iter_inner(&'s self) -> QueryIter<'w, 's, Q::ReadOnly, F::ReadOnly> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state - .iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick) + self.state.as_readonly().iter_unchecked_manual( + self.world, + self.last_change_tick, + self.change_tick, + ) } } } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr index 7eab4aa382ee5..fca7f7a52b731 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr @@ -16,7 +16,7 @@ error[E0277]: the trait bound `bevy_ecs::query::Changed: ArchetypeFilter` i (F0, F1, F2, F3, F4, F5, F6) (F0, F1, F2, F3, F4, F5, F6, F7) and 26 others - = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>` + = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, bevy_ecs::query::Changed>` note: required by a bound in `is_exact_size_iterator` --> tests/ui/query_exact_sized_iterator_safety.rs:16:30 | @@ -41,7 +41,7 @@ error[E0277]: the trait bound `bevy_ecs::query::Added: ArchetypeFilter` is (F0, F1, F2, F3, F4, F5, F6) (F0, F1, F2, F3, F4, F5, F6, F7) and 26 others - = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>` + = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, bevy_ecs::query::Added>` note: required by a bound in `is_exact_size_iterator` --> tests/ui/query_exact_sized_iterator_safety.rs:16:30 | 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 3d5907eeac8e1..6245a562f3dc7 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 @@ -21,7 +21,7 @@ error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not sati (F0, F1, F2, F3, F4) (F0, F1, F2, F3, F4, F5) (F0, F1, F2, F3, F4, F5, F6) - and 48 others + and 49 others = note: `ReadOnlyWorldQuery` is implemented for `&'static Foo`, but not for `&'static mut Foo` = note: required because of the requirements on the impl of `ReadOnlySystemParamFetch` for `QueryState<&'static mut Foo>` = note: 2 redundant requirements hidden 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 ac7361b87717d..c3515b78fd515 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 @@ -13,7 +13,7 @@ error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not sati (F0, F1, F2, F3, F4) (F0, F1, F2, F3, F4, F5) (F0, F1, F2, F3, F4, F5, F6) - and 51 others + and 52 others note: required by a bound in `_::assert_readonly` --> tests/ui/world_query_derive.rs:7:10 | @@ -36,7 +36,7 @@ error[E0277]: the trait bound `MutableMarked: ReadOnlyWorldQuery` is not satisfi (F0, F1, F2, F3, F4) (F0, F1, F2, F3, F4, F5) (F0, F1, F2, F3, F4, F5, F6) - and 51 others + and 52 others note: required by a bound in `_::assert_readonly` --> tests/ui/world_query_derive.rs:18:10 |