From e9fc9d0d40b54c5a2da8081f4ceadc11f203e2e1 Mon Sep 17 00:00:00 2001 From: James Liu Date: Wed, 14 Dec 2022 21:52:39 -0500 Subject: [PATCH 01/11] Implement WorldQuery for EntityRef --- crates/bevy_ecs/src/query/fetch.rs | 79 ++++++++++++++++++++++++- crates/bevy_ecs/src/world/entity_ref.rs | 5 -- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 4a3f49f302d95..fcc65d5b8129a 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -5,7 +5,7 @@ use crate::{ entity::Entity, query::{Access, DebugCheckedUnwrap, FilteredAccess}, storage::{ComponentSparseSet, Table, TableRow}, - world::{Mut, World}, + world::{EntityRef, Mut, World}, }; use bevy_ecs_macros::all_tuples; pub use bevy_ecs_macros::WorldQuery; @@ -515,6 +515,83 @@ unsafe impl WorldQuery for Entity { /// SAFETY: access is read only unsafe impl ReadOnlyWorldQuery for Entity {} +/// SAFETY: no component or archetype access +unsafe impl<'a> WorldQuery for EntityRef<'a> { + type Fetch<'w> = &'w World; + type Item<'w> = EntityRef<'w>; + type ReadOnly = Self; + type State = (); + + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + item + } + + const IS_DENSE: bool = true; + + const IS_ARCHETYPAL: bool = true; + + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + _last_change_tick: u32, + _change_tick: u32, + ) -> Self::Fetch<'w> { + world + } + + unsafe fn clone_fetch<'w>(world: &Self::Fetch<'w>) -> Self::Fetch<'w> { + world + } + + #[inline] + unsafe fn set_archetype<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _archetype: &'w Archetype, + _table: &Table, + ) { + } + + #[inline] + unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + } + + #[inline(always)] + unsafe fn fetch<'w>( + world: &mut Self::Fetch<'w>, + entity: Entity, + _table_row: TableRow, + ) -> Self::Item<'w> { + world.get_entity(entity).debug_checked_unwrap() + } + + fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { + access.read_all(); + } + + fn update_archetype_component_access( + _state: &Self::State, + archetype: &Archetype, + access: &mut Access, + ) { + for component_id in archetype.components() { + access.add_read(archetype.get_archetype_component_id(component_id).unwrap()); + } + } + + fn init_state(_world: &mut World) {} + + fn matches_component_set( + _state: &Self::State, + _set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + true + } +} + +/// SAFETY: access is read only +unsafe impl<'a> ReadOnlyWorldQuery for EntityRef<'a> {} + #[doc(hidden)] pub struct ReadFetch<'w, T> { // T::Storage = TableStorage diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 458564beb4cfe..abdba2fb988f4 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -48,11 +48,6 @@ impl<'w> EntityRef<'w> { &self.world.archetypes[self.location.archetype_id] } - #[inline] - pub fn world(&self) -> &'w World { - self.world - } - #[inline] pub fn contains(&self) -> bool { self.contains_type_id(TypeId::of::()) From 13387e35d103d93ee1b81d13988f4c1d2a2e405a Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 15 Dec 2022 00:07:24 -0500 Subject: [PATCH 02/11] Update query docs --- crates/bevy_ecs/src/system/query.rs | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 8f7e4f70bc303..96c41b7aa577d 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -181,6 +181,41 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// /// An alternative to this idiom is to wrap the conflicting queries into a [`ParamSet`](super::ParamSet). /// +/// ## Whole Entity Access +/// +/// [`EntityRef`]s can be fetched from a query. This will give read-only access to any component on the entity, +/// and can be use to dynamically fetch any component without baking it into the query type. Due to this global +/// acess to the entity, this will block any other system from parallelizing with it. As such these queries +/// should be sparingly used. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::world::EntityRef; +/// # #[derive(Component)] +/// # struct ComponentA; +/// # fn system( +/// query: Query<(EntityRef, &ComponentA)> +/// # ) {} +/// # bevy_ecs::system::assert_is_system(system); +/// ``` +/// +/// As `EntityRef` can read any component on an entity, a query using it will conflict with *any* mutable +/// access. It is strongly advised to couple `EntityRef` queries with the use of either `With`/`Without` +/// filters or `ParamSets`. This also limits the scope of the query, which will improve iteration performance +/// and also allows it to parallelize with other non-conflicting systems. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::world::EntityRef; +/// # #[derive(Component)] +/// # struct ComponentA; +/// # fn system( +/// // This will panic! +/// query: Query<(EntityRef, &mut ComponentA)> +/// # ) {} +/// # bevy_ecs::system::assert_is_system(system); +/// ``` +/// /// # Accessing query items /// /// The following table summarizes the behavior of the safe methods that can be used to get query items. @@ -246,6 +281,7 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// [`Changed`]: crate::query::Changed /// [components]: crate::component::Component /// [entity identifiers]: crate::entity::Entity +/// [`EntityRef`]: crate::world::EntityRef /// [`for_each`]: Self::for_each /// [`for_each_mut`]: Self::for_each_mut /// [`get`]: Self::get From c74843c2b2fcb2549a74f3cdc871825ff99c61ab Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 15 Dec 2022 00:15:01 -0500 Subject: [PATCH 03/11] Ensure invalid use of EntityRef as a WorldQuery panics --- crates/bevy_ecs/src/lib.rs | 16 +++++++++++++++- crates/bevy_ecs/src/query/access.rs | 5 +++++ crates/bevy_ecs/src/query/fetch.rs | 4 ++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index d4f79bec0f9ad..fdb0c1f05d15e 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -61,7 +61,7 @@ mod tests { Added, ChangeTrackers, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without, }, system::Resource, - world::{Mut, World}, + world::{Mut, World, EntityRef}, }; use bevy_tasks::{ComputeTaskPool, TaskPool}; use std::{ @@ -1329,6 +1329,13 @@ mod tests { world.query::<(&A, &mut A)>(); } + #[test] + #[should_panic] + fn entity_ref_and_mut_query_panic() { + let mut world = World::new(); + world.query::<(EntityRef, &mut A)>(); + } + #[test] #[should_panic] fn mut_and_ref_query_panic() { @@ -1336,6 +1343,13 @@ mod tests { world.query::<(&mut A, &A)>(); } + #[test] + #[should_panic] + fn mut_and_entity_ref_query_panic() { + let mut world = World::new(); + world.query::<(&mut A, EntityRef)>(); + } + #[test] #[should_panic] fn mut_and_mut_query_panic() { diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index a28e6df23ef83..0177027de753a 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -113,6 +113,11 @@ impl Access { self.writes.contains(index.sparse_set_index()) } + /// Returns `true` if this accesses anything mutably. + pub fn has_any_write(&self) -> bool { + !self.writes.is_clear() + } + /// Sets this as having access to all indexed elements (i.e. `&World`). pub fn read_all(&mut self) { self.reads_all = true; diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index fcc65d5b8129a..db9561f39dcfb 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -566,6 +566,10 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { } fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { + assert!( + !access.access().has_any_write(), + "EntityRef conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + ); access.read_all(); } From d00da4ecaea4c281ef808e0e53803e0499c52ec4 Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 15 Dec 2022 00:16:45 -0500 Subject: [PATCH 04/11] Add EntityRef to the ECS prelude --- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/system/query.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index fdb0c1f05d15e..763fe77da9af5 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -43,7 +43,7 @@ pub mod prelude { Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction, }, - world::{FromWorld, Mut, World}, + world::{FromWorld, Mut, World, EntityRef}, }; } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 96c41b7aa577d..591cff482bf36 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -190,7 +190,6 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::world::EntityRef; /// # #[derive(Component)] /// # struct ComponentA; /// # fn system( @@ -206,7 +205,6 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::world::EntityRef; /// # #[derive(Component)] /// # struct ComponentA; /// # fn system( From 9cd5baa8c0aa5c87c0b76ed9a20568b0fa3c493b Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 15 Dec 2022 00:33:08 -0500 Subject: [PATCH 05/11] Fix safety comment --- crates/bevy_ecs/src/query/fetch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index db9561f39dcfb..789f56cae25fd 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -515,7 +515,7 @@ unsafe impl WorldQuery for Entity { /// SAFETY: access is read only unsafe impl ReadOnlyWorldQuery for Entity {} -/// SAFETY: no component or archetype access +/// SAFETY: `Self` is the same as `Self::ReadOnly` unsafe impl<'a> WorldQuery for EntityRef<'a> { type Fetch<'w> = &'w World; type Item<'w> = EntityRef<'w>; From ddd845f61a37c5aca9b59447a31d8e81c951ebbb Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 15 Dec 2022 00:33:25 -0500 Subject: [PATCH 06/11] Formatting --- crates/bevy_ecs/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 763fe77da9af5..2ef23e40a273c 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -43,7 +43,7 @@ pub mod prelude { Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction, }, - world::{FromWorld, Mut, World, EntityRef}, + world::{EntityRef, FromWorld, Mut, World}, }; } @@ -61,7 +61,7 @@ mod tests { Added, ChangeTrackers, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without, }, system::Resource, - world::{Mut, World, EntityRef}, + world::{EntityRef, Mut, World}, }; use bevy_tasks::{ComputeTaskPool, TaskPool}; use std::{ From a94a3481974c994bc0f4028f9e1ecf9e38b3de65 Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 15 Dec 2022 18:18:28 -0500 Subject: [PATCH 07/11] Fix typo. Co-authored-by: Carter Weinberg --- crates/bevy_ecs/src/system/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 591cff482bf36..656a063ef6054 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -185,7 +185,7 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// /// [`EntityRef`]s can be fetched from a query. This will give read-only access to any component on the entity, /// and can be use to dynamically fetch any component without baking it into the query type. Due to this global -/// acess to the entity, this will block any other system from parallelizing with it. As such these queries +/// access to the entity, this will block any other system from parallelizing with it. As such these queries /// should be sparingly used. /// /// ``` From fc4a1c6d4507de1cccb5929c479189f5794e6403 Mon Sep 17 00:00:00 2001 From: James Liu Date: Tue, 17 Jan 2023 12:06:36 -0800 Subject: [PATCH 08/11] Provide a safety comment to fetch Co-authored-by: Jakob Hellermann --- crates/bevy_ecs/src/query/fetch.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 789f56cae25fd..565ceabd78336 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -562,7 +562,8 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { entity: Entity, _table_row: TableRow, ) -> Self::Item<'w> { - world.get_entity(entity).debug_checked_unwrap() + // SAFETY: `fetch` must be called with an entity that exists in the world + unsafe { world.get_entity(entity).debug_checked_unwrap() } } fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { From aea7f0bd5f8ee2df126474c6ec425fcf398d2eab Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 22:09:44 -0800 Subject: [PATCH 09/11] Ensure it conflicts --- crates/bevy_ecs/src/query/fetch.rs | 2 +- crates/bevy_ecs/src/system/mod.rs | 27 ++++++++++++++++++++++++++- crates/bevy_ecs/src/system/query.rs | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 5cdde5d49fe2c..523a06c70de54 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -5,7 +5,7 @@ use crate::{ entity::Entity, query::{Access, DebugCheckedUnwrap, FilteredAccess}, storage::{ComponentSparseSet, Table, TableRow}, - world::{EntityRef, Mut, World}, + world::{EntityRef, Mut, Ref, World}, }; use bevy_ecs_macros::all_tuples; pub use bevy_ecs_macros::WorldQuery; diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 5fd37e11534b4..786c626287c63 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -113,6 +113,9 @@ pub use system::*; pub use system_param::*; pub use system_piping::*; +use crate::world::World; +use core::panic::UnwindSafe; + /// Ensure that a given function is a system /// /// This should be used when writing doc examples, @@ -121,11 +124,33 @@ pub use system_piping::*; pub fn assert_is_system>(sys: S) { if false { // Check it can be converted into a system - // TODO: This should ensure that the system has no conflicting system params IntoSystem::into_system(sys); } } +/// Ensures that the provided system conflicts with itself. +/// +/// This function will panic if the provided system *doesn't* conflict with itself. +/// +/// Note: this will run the system on an empty world. +pub fn assert_system_conflicts + UnwindSafe>(sys: S) { + std::panic::catch_unwind(move || { + let mut world = World::new(); + IntoSystem::into_system(sys).run((), &mut world); + }) + .unwrap_err(); +} + +/// Ensures that the provided system doesn't with itself. +/// +/// This function will panic if the provided system conflict with itself. +/// +/// Note: this will run the system on an empty world. +pub fn assert_system_does_not_conflict>(sys: S) { + let mut world = World::new(); + IntoSystem::into_system(sys).run((), &mut world); +} + #[cfg(test)] mod tests { use std::any::TypeId; diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index c27e37464589f..7b1452676914f 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -211,7 +211,7 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// // This will panic! /// query: Query<(EntityRef, &mut ComponentA)> /// # ) {} -/// # bevy_ecs::system::assert_is_system(system); +/// # bevy_ecs::system::assert_system_conflicts(system); /// ``` /// /// # Accessing query items From a0cbc4be76b29e508044ba6e7c1143feb09ba792 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 22:29:39 -0800 Subject: [PATCH 10/11] Make sure doc test panics --- crates/bevy_ecs/src/system/mod.rs | 21 +++++++-------------- crates/bevy_ecs/src/system/query.rs | 4 ++-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 786c626287c63..7da42523375b3 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -114,7 +114,6 @@ pub use system_param::*; pub use system_piping::*; use crate::world::World; -use core::panic::UnwindSafe; /// Ensure that a given function is a system /// @@ -128,19 +127,6 @@ pub fn assert_is_system>(sys: S) } } -/// Ensures that the provided system conflicts with itself. -/// -/// This function will panic if the provided system *doesn't* conflict with itself. -/// -/// Note: this will run the system on an empty world. -pub fn assert_system_conflicts + UnwindSafe>(sys: S) { - std::panic::catch_unwind(move || { - let mut world = World::new(); - IntoSystem::into_system(sys).run((), &mut world); - }) - .unwrap_err(); -} - /// Ensures that the provided system doesn't with itself. /// /// This function will panic if the provided system conflict with itself. @@ -1237,4 +1223,11 @@ mod tests { let query = unsafe { Query::new(&world2, &qstate, 0, 0, false) }; query.iter(); } + + #[test] + #[should_panic] + fn assert_system_does_not_conflict() { + fn system(_query: Query<(&mut W, &mut W)>) {} + super::assert_system_does_not_conflict(system); + } } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 7b1452676914f..b430a50fc4741 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -203,7 +203,7 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// filters or `ParamSets`. This also limits the scope of the query, which will improve iteration performance /// and also allows it to parallelize with other non-conflicting systems. /// -/// ``` +/// ```should_panic /// # use bevy_ecs::prelude::*; /// # #[derive(Component)] /// # struct ComponentA; @@ -211,7 +211,7 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// // This will panic! /// query: Query<(EntityRef, &mut ComponentA)> /// # ) {} -/// # bevy_ecs::system::assert_system_conflicts(system); +/// # bevy_ecs::system::assert_system_does_not_conflict(system); /// ``` /// /// # Accessing query items From 39cf33707fdc2eab075df6210bf7cca73701af1e Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 17 Jan 2023 22:48:28 -0800 Subject: [PATCH 11/11] Add non-panicking doc test that shows how to use With/Without with EntityRef --- crates/bevy_ecs/src/system/mod.rs | 4 +++- crates/bevy_ecs/src/system/query.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 7da42523375b3..5cbc52e2ae850 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -134,7 +134,9 @@ pub fn assert_is_system>(sys: S) /// Note: this will run the system on an empty world. pub fn assert_system_does_not_conflict>(sys: S) { let mut world = World::new(); - IntoSystem::into_system(sys).run((), &mut world); + let mut system = IntoSystem::into_system(sys); + system.initialize(&mut world); + system.run((), &mut world); } #[cfg(test)] diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index b430a50fc4741..d34d035d5dee2 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -213,6 +213,19 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug}; /// # ) {} /// # bevy_ecs::system::assert_system_does_not_conflict(system); /// ``` +/// ``` +/// # use bevy_ecs::prelude::*; +/// # #[derive(Component)] +/// # struct ComponentA; +/// # #[derive(Component)] +/// # struct ComponentB; +/// # fn system( +/// // This will not panic. +/// query_a: Query>, +/// query_b: Query<&mut ComponentB, Without>, +/// # ) {} +/// # bevy_ecs::system::assert_system_does_not_conflict(system); +/// ``` /// /// # Accessing query items ///