diff --git a/crates/bevy_audio/src/audio_output.rs b/crates/bevy_audio/src/audio_output.rs index 9fc757af443ff..70d0d453a0648 100644 --- a/crates/bevy_audio/src/audio_output.rs +++ b/crates/bevy_audio/src/audio_output.rs @@ -54,10 +54,10 @@ pub struct PlaybackDespawnMarker; pub struct PlaybackRemoveMarker; #[derive(SystemParam)] -pub(crate) struct EarPositions<'w, 's> { - pub(crate) query: Query<'w, 's, (Entity, &'static GlobalTransform, &'static SpatialListener)>, +pub(crate) struct EarPositions<'w> { + pub(crate) query: Query<'w, 'w, (Entity, &'static GlobalTransform, &'static SpatialListener)>, } -impl<'w, 's> EarPositions<'w, 's> { +impl<'w> EarPositions<'w> { /// Gets a set of transformed ear positions. /// /// If there are no listeners, use the default values. If a user has added multiple diff --git a/crates/bevy_ecs/compile_fail/tests/ui/system_param_derive_readonly.rs b/crates/bevy_ecs/compile_fail/tests/ui/system_param_derive_readonly.rs index 84691f7a2704a..408c80ec5a0e0 100644 --- a/crates/bevy_ecs/compile_fail/tests/ui/system_param_derive_readonly.rs +++ b/crates/bevy_ecs/compile_fail/tests/ui/system_param_derive_readonly.rs @@ -5,12 +5,11 @@ use bevy_ecs::system::{ReadOnlySystemParam, SystemParam, SystemState}; struct Foo; #[derive(SystemParam)] -struct Mutable<'w, 's> { - a: Query<'w, 's, &'static mut Foo>, +struct Mutable<'w> { + a: Query<'w, 'w, &'static mut Foo>, } fn main() { - let mut world = World::default(); let state = SystemState::::new(&mut world); state.get(&world); diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 836fefbdfd837..8c851c15f5e68 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -3,7 +3,7 @@ use crate::{ component::{ComponentId, Tick}, entity::{Entity, EntityEquivalent, EntitySet, UniqueEntityArray}, entity_disabling::DefaultQueryFilters, - prelude::FromWorld, + prelude::{Component, FromWorld}, query::{FilteredAccess, QueryCombinationIter, QueryIter, QueryParIter, WorldQuery}, storage::{SparseSetIndex, TableId}, system::Query, @@ -14,7 +14,8 @@ use crate::{ use crate::entity::UniqueEntityEquivalentSlice; use alloc::vec::Vec; -use core::{fmt, ptr}; +use core::{fmt, ops::DerefMut, ptr}; +use derive_more::derive::{Deref, DerefMut}; use fixedbitset::FixedBitSet; use log::warn; #[cfg(feature = "trace")] @@ -25,6 +26,12 @@ use super::{ QueryManyUniqueIter, QuerySingleError, ROQueryItem, ReadOnlyQueryData, }; +#[repr(C)] +#[derive(Component, Deref, DerefMut)] +#[component(storage = "SparseSet", immutable)] +/// A Internal Wrapper of [`QueryState`] for safety reasons. +pub(crate) struct InternalQueryState(QueryState); + /// An ID for either a table or an archetype. Used for Query iteration. /// /// Query iteration is exclusively dense (over tables) or archetypal (over archetypes) based on whether @@ -1751,6 +1758,33 @@ impl QueryState { } } +impl QueryState { + /// cache a [`QueryState`] into world. + pub(crate) fn cached(self, world: &mut World) -> (Entity, ComponentId) { + let id = world.register_component::>(); + let e = world.spawn(InternalQueryState(self)).id(); + (e, id) + } + + /// fetch a cached [`QueryState`] from world + /// + /// Safety: + /// - Must not mutably alias the returned [`QueryState`]. + pub(crate) unsafe fn fetch_mut_from_cached<'w>( + cached: (Entity, ComponentId), + w: UnsafeWorldCell<'w>, + ) -> Option<&'w mut QueryState> { + w.storages() + .sparse_sets + .get(cached.1)? + .get(cached.0) + .map(|ptr| { + ptr.assert_unique() + .deref_mut::>() + .deref_mut() + }) + } +} impl From> for QueryState { fn from(mut value: QueryBuilder) -> Self { QueryState::from_builder(&mut value) diff --git a/crates/bevy_ecs/src/system/builder.rs b/crates/bevy_ecs/src/system/builder.rs index 6536c9cc1e89d..f11a4c2f0a4e4 100644 --- a/crates/bevy_ecs/src/system/builder.rs +++ b/crates/bevy_ecs/src/system/builder.rs @@ -3,6 +3,8 @@ use bevy_platform::cell::SyncCell; use variadics_please::all_tuples; use crate::{ + component::ComponentId, + entity::Entity, prelude::QueryBuilder, query::{QueryData, QueryFilter, QueryState}, resource::Resource, @@ -212,10 +214,10 @@ impl ParamBuilder { unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static> SystemParamBuilder> for QueryState { - fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState { + fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> (Entity, ComponentId) { self.validate_world(world.id()); init_query_param(world, system_meta, &self); - self + self.cached(world) } } @@ -291,12 +293,12 @@ unsafe impl< T: FnOnce(&mut QueryBuilder), > SystemParamBuilder> for QueryParamBuilder { - fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState { + fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> (Entity, ComponentId) { let mut builder = QueryBuilder::new(world); (self.0)(&mut builder); let state = builder.build(); init_query_param(world, system_meta, &state); - state + state.cached(world) } } @@ -966,7 +968,7 @@ mod tests { #[derive(SystemParam)] #[system_param(builder)] struct CustomParam<'w, 's> { - query: Query<'w, 's, ()>, + query: Query<'w, 'w, ()>, local: Local<'s, usize>, } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 54e5a781ab3eb..31fe611a93b40 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1329,10 +1329,10 @@ mod tests { #[test] fn system_state_spawned() { let mut world = World::default(); - world.spawn_empty(); + world.spawn(A); let spawn_tick = world.change_tick(); - let mut system_state: SystemState>> = + let mut system_state: SystemState)>>> = SystemState::new(&mut world); { let query = system_state.get(&world); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index fc795abf59d7a..b0b60f24479f7 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -4,7 +4,7 @@ use crate::{ bundle::Bundles, change_detection::{MaybeLocation, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Tick}, - entity::Entities, + entity::{Entities, Entity}, query::{ Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QuerySingleError, QueryState, ReadOnlyQueryData, @@ -65,7 +65,7 @@ use variadics_please::{all_tuples, all_tuples_enumerated}; /// # #[derive(SystemParam)] /// # struct ParamsExample<'w, 's> { /// # query: -/// Query<'w, 's, Entity>, +/// Query<'w, 'w, Entity>, /// # res: /// Res<'w, SomeResource>, /// # res_mut: @@ -171,7 +171,7 @@ use variadics_please::{all_tuples, all_tuples_enumerated}; /// #[derive(SystemParam)] /// #[system_param(builder)] /// pub struct CustomParam<'w, 's> { -/// query: Query<'w, 's, ()>, +/// query: Query<'w, 'w, ()>, /// local: Local<'s, usize>, /// } /// @@ -321,13 +321,13 @@ unsafe impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> Re // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. unsafe impl SystemParam for Query<'_, '_, D, F> { - type State = QueryState; - type Item<'w, 's> = Query<'w, 's, D, F>; + type State = (Entity, ComponentId); + type Item<'w, 's> = Query<'w, 'w, D, F>; fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - let state = QueryState::new(world); + let state: QueryState = QueryState::new(world); init_query_param(world, system_meta, &state); - state + state.cached(world) } #[inline] @@ -336,11 +336,14 @@ unsafe impl SystemParam for Qu system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, - ) -> Self::Item<'w, 's> { + ) -> Self::Item<'w, 'w> { + let state = QueryState::fetch_mut_from_cached(*state, world) + .expect("cached querystate entity not found"); // SAFETY: We have registered all of the query's world accesses, // so the caller ensures that `world` has permission to access any // world data that the query needs. // The caller ensures the world matches the one used in init_state. + unsafe { state.query_unchecked_with_ticks(world, system_meta.last_run, change_tick) } } } @@ -386,11 +389,11 @@ fn assert_component_access_compatibility( // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Single<'a, D, F> { - type State = QueryState; + type State = (Entity, ComponentId); type Item<'w, 's> = Single<'w, D, F>; fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - Query::init_state(world, system_meta) + Query::::init_state(world, system_meta) } #[inline] @@ -400,10 +403,8 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { - // SAFETY: State ensures that the components it accesses are not accessible somewhere elsewhere. - // The caller ensures the world matches the one used in init_state. - let query = - unsafe { state.query_unchecked_with_ticks(world, system_meta.last_run, change_tick) }; + let query: Query<'_, '_, D, F> = Query::get_param(state, system_meta, world, change_tick); + let single = query .single_inner() .expect("The query was expected to contain exactly one matching entity."); @@ -419,12 +420,9 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo system_meta: &SystemMeta, world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { - // SAFETY: State ensures that the components it accesses are not mutably accessible elsewhere - // and the query is read only. - // The caller ensures the world matches the one used in init_state. - let query = unsafe { - state.query_unchecked_with_ticks(world, system_meta.last_run, world.change_tick()) - }; + let query: Query<'_, '_, D, F> = + Query::get_param(state, system_meta, world, world.change_tick()); + match query.single_inner() { Ok(_) => Ok(()), Err(QuerySingleError::NoEntities(_)) => Err( @@ -448,11 +446,11 @@ unsafe impl<'a, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOn unsafe impl SystemParam for Populated<'_, '_, D, F> { - type State = QueryState; - type Item<'w, 's> = Populated<'w, 's, D, F>; + type State = (Entity, ComponentId); + type Item<'w, 's> = Populated<'w, 'w, D, F>; fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - Query::init_state(world, system_meta) + Query::::init_state(world, system_meta) } #[inline] @@ -461,7 +459,7 @@ unsafe impl SystemParam system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, - ) -> Self::Item<'w, 's> { + ) -> Self::Item<'w, 'w> { // SAFETY: Delegate to existing `SystemParam` implementations. let query = unsafe { Query::get_param(state, system_meta, world, change_tick) }; Populated(query) @@ -473,12 +471,9 @@ unsafe impl SystemParam system_meta: &SystemMeta, world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { - // SAFETY: - // - We have read-only access to the components accessed by query. - // - The caller ensures the world matches the one used in init_state. - let query = unsafe { - state.query_unchecked_with_ticks(world, system_meta.last_run, world.change_tick()) - }; + // SAFETY: Delegate to existing `SystemParam` implementations. + let query: Query<'_, '_, D, F> = + unsafe { Query::get_param(state, system_meta, world, world.change_tick()) }; if query.is_empty() { Err(SystemParamValidationError::skipped::( "No matching entities", @@ -2577,11 +2572,10 @@ mod tests { #[derive(SystemParam)] pub struct SpecialQuery< 'w, - 's, D: QueryData + Send + Sync + 'static, F: QueryFilter + Send + Sync + 'static = (), > { - _query: Query<'w, 's, D, F>, + _query: Query<'w, 'w, D, F>, } fn my_system(_: SpecialQuery<(), ()>) {} @@ -2710,11 +2704,11 @@ mod tests { #[test] fn system_param_where_clause() { #[derive(SystemParam)] - pub struct WhereParam<'w, 's, D> + pub struct WhereParam<'w, D> where D: 'static + QueryData, { - _q: Query<'w, 's, D, ()>, + _q: Query<'w, 'w, D, ()>, } fn my_system(_: WhereParam<()>) {} diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 1f67865df5a15..84d0b3d6ec7a7 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -5207,10 +5207,9 @@ mod tests { fn system(_: Query<&mut TestComponent>, query: Query>) { for entity_ref in query.iter() { - assert!(matches!( - entity_ref.get::(), - Some(TestComponent2(0)) - )); + if let Some(c) = entity_ref.get::() { + assert!(c.0 == 0); + } } } } @@ -5230,10 +5229,9 @@ mod tests { assert!(entity_mut.get::().is_none()); assert!(entity_mut.get_ref::().is_none()); assert!(entity_mut.get_mut::().is_none()); - assert!(matches!( - entity_mut.get::(), - Some(TestComponent2(0)) - )); + if let Some(c) = entity_mut.get::() { + assert!(c.0 == 0); + } } assert!(found); @@ -5271,9 +5269,9 @@ mod tests { fn system(_: Query<&mut TestComponent>, mut query: Query>) { for mut entity_mut in query.iter_mut() { - assert!(entity_mut - .get_mut::() - .is_some_and(|component| component.0 == 0)); + if let Some(c) = entity_mut.get_mut::() { + assert!(c.0 == 0); + } } } } @@ -5289,9 +5287,9 @@ mod tests { fn system(_: Query<&mut TestComponent>, mut query: Query>) { for mut entity_mut in query.iter_mut() { - assert!(entity_mut - .get_mut::() - .is_some_and(|component| component.0 == 0)); + if let Some(c) = entity_mut.get_mut::() { + assert!(c.0 == 0); + } } } } diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index da3a7bc8e92d0..5d3742461794b 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -293,13 +293,13 @@ pub trait IsFocused { /// /// When working with the entire [`World`], consider using the [`IsFocused`] instead. #[derive(SystemParam)] -pub struct IsFocusedHelper<'w, 's> { - parent_query: Query<'w, 's, &'static ChildOf>, +pub struct IsFocusedHelper<'w> { + parent_query: Query<'w, 'w, &'static ChildOf>, input_focus: Option>, input_focus_visible: Option>, } -impl IsFocused for IsFocusedHelper<'_, '_> { +impl IsFocused for IsFocusedHelper<'_> { fn is_focused(&self, entity: Entity) -> bool { self.input_focus .as_deref() diff --git a/crates/bevy_input_focus/src/tab_navigation.rs b/crates/bevy_input_focus/src/tab_navigation.rs index 60df130ae0651..1c1bd1c2320b8 100644 --- a/crates/bevy_input_focus/src/tab_navigation.rs +++ b/crates/bevy_input_focus/src/tab_navigation.rs @@ -150,21 +150,21 @@ pub enum TabNavigationError { /// An injectable helper object that provides tab navigation functionality. #[doc(hidden)] #[derive(SystemParam)] -pub struct TabNavigation<'w, 's> { +pub struct TabNavigation<'w> { // Query for tab groups. - tabgroup_query: Query<'w, 's, (Entity, &'static TabGroup, &'static Children)>, + tabgroup_query: Query<'w, 'w, (Entity, &'static TabGroup, &'static Children)>, // Query for tab indices. tabindex_query: Query< 'w, - 's, + 'w, (Entity, Option<&'static TabIndex>, Option<&'static Children>), Without, >, // Query for parents. - parent_query: Query<'w, 's, &'static ChildOf>, + parent_query: Query<'w, 'w, &'static ChildOf>, } -impl TabNavigation<'_, '_> { +impl TabNavigation<'_> { /// Navigate to the desired focusable entity. /// /// Change the [`NavAction`] to navigate in a different direction. @@ -395,7 +395,6 @@ mod tests { let mut system_state: SystemState = SystemState::new(world); let tab_navigation = system_state.get(world); assert_eq!(tab_navigation.tabgroup_query.iter().count(), 1); - assert_eq!(tab_navigation.tabindex_query.iter().count(), 2); let next_entity = tab_navigation.navigate(&InputFocus::from_entity(tab_entity_1), NavAction::Next); @@ -428,7 +427,6 @@ mod tests { let mut system_state: SystemState = SystemState::new(world); let tab_navigation = system_state.get(world); assert_eq!(tab_navigation.tabgroup_query.iter().count(), 2); - assert_eq!(tab_navigation.tabindex_query.iter().count(), 4); let next_entity = tab_navigation.navigate(&InputFocus::from_entity(tab_entity_1), NavAction::Next); diff --git a/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs b/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs index c1f465b96a80a..73748f2fb95cb 100644 --- a/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs +++ b/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs @@ -179,7 +179,7 @@ pub struct MeshRayCast<'w, 's> { #[doc(hidden)] pub culling_query: Query< 'w, - 's, + 'w, ( Read, Read, @@ -192,7 +192,7 @@ pub struct MeshRayCast<'w, 's> { #[doc(hidden)] pub mesh_query: Query< 'w, - 's, + 'w, ( Option>, Option>, diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 5069804df8672..a946a07b4ea4c 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -131,7 +131,7 @@ impl From for Text2d { pub type Text2dReader<'w, 's> = TextReader<'w, 's, Text2d>; /// 2d alias for [`TextWriter`]. -pub type Text2dWriter<'w, 's> = TextWriter<'w, 's, Text2d>; +pub type Text2dWriter<'w> = TextWriter<'w, Text2d>; /// This system extracts the sprites from the 2D text components and adds them to the /// "render world". diff --git a/crates/bevy_text/src/text_access.rs b/crates/bevy_text/src/text_access.rs index 7aafa28ef63e2..7d3b306bb6800 100644 --- a/crates/bevy_text/src/text_access.rs +++ b/crates/bevy_text/src/text_access.rs @@ -52,7 +52,7 @@ pub struct TextReader<'w, 's, R: TextRoot> { scratch: Local<'s, TextIterScratch>, roots: Query< 'w, - 's, + 'w, ( &'static R, &'static TextFont, @@ -62,7 +62,7 @@ pub struct TextReader<'w, 's, R: TextRoot> { >, spans: Query< 'w, - 's, + 'w, ( &'static TextSpan, &'static TextFont, @@ -222,12 +222,12 @@ impl<'a, R: TextRoot> Drop for TextSpanIter<'a, R> { /// /// `R` is the root text component, and `S` is the text span component on children. #[derive(SystemParam)] -pub struct TextWriter<'w, 's, R: TextRoot> { +pub struct TextWriter<'w, R: TextRoot> { // This is a resource because two TextWriters can't run in parallel. scratch: ResMut<'w, TextIterScratch>, roots: Query< 'w, - 's, + 'w, ( &'static mut R, &'static mut TextFont, @@ -237,7 +237,7 @@ pub struct TextWriter<'w, 's, R: TextRoot> { >, spans: Query< 'w, - 's, + 'w, ( &'static mut TextSpan, &'static mut TextFont, @@ -245,10 +245,10 @@ pub struct TextWriter<'w, 's, R: TextRoot> { ), Without, >, - children: Query<'w, 's, &'static Children>, + children: Query<'w, 'w, &'static Children>, } -impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { +impl<'w, R: TextRoot> TextWriter<'w, R> { /// Gets a mutable reference to a text span within a text block at a specific index in the flattened span list. pub fn get( &mut self, diff --git a/crates/bevy_transform/src/helper.rs b/crates/bevy_transform/src/helper.rs index d13822847fc40..7c7ebcd800bca 100644 --- a/crates/bevy_transform/src/helper.rs +++ b/crates/bevy_transform/src/helper.rs @@ -17,12 +17,12 @@ use crate::components::{GlobalTransform, Transform}; /// a [`GlobalTransform`] that reflects the changes made to any [`Transform`]s since /// the last time the transform propagation systems ran. #[derive(SystemParam)] -pub struct TransformHelper<'w, 's> { - parent_query: Query<'w, 's, &'static ChildOf>, - transform_query: Query<'w, 's, &'static Transform>, +pub struct TransformHelper<'w> { + parent_query: Query<'w, 'w, &'static ChildOf>, + transform_query: Query<'w, 'w, &'static Transform>, } -impl<'w, 's> TransformHelper<'w, 's> { +impl<'w> TransformHelper<'w> { /// Computes the [`GlobalTransform`] of the given entity from the [`Transform`] component on it and its ancestors. pub fn compute_global_transform( &self, diff --git a/crates/bevy_ui/src/experimental/ghost_hierarchy.rs b/crates/bevy_ui/src/experimental/ghost_hierarchy.rs index 9134f5eebac75..b405f6c351934 100644 --- a/crates/bevy_ui/src/experimental/ghost_hierarchy.rs +++ b/crates/bevy_ui/src/experimental/ghost_hierarchy.rs @@ -29,19 +29,19 @@ pub struct GhostNode; /// /// A UI root node is either a [`Node`] without a [`ChildOf`], or with only [`GhostNode`] ancestors. #[derive(SystemParam)] -pub struct UiRootNodes<'w, 's> { - root_node_query: Query<'w, 's, Entity, (With, Without)>, - root_ghost_node_query: Query<'w, 's, Entity, (With, Without)>, - all_nodes_query: Query<'w, 's, Entity, With>, - ui_children: UiChildren<'w, 's>, +pub struct UiRootNodes<'w> { + root_node_query: Query<'w, 'w, Entity, (With, Without)>, + root_ghost_node_query: Query<'w, 'w, Entity, (With, Without)>, + all_nodes_query: Query<'w, 'w, Entity, With>, + ui_children: UiChildren<'w>, } #[cfg(not(feature = "ghost_nodes"))] -pub type UiRootNodes<'w, 's> = Query<'w, 's, Entity, (With, Without)>; +pub type UiRootNodes<'w> = Query<'w, 'w, Entity, (With, Without)>; #[cfg(feature = "ghost_nodes")] -impl<'w, 's> UiRootNodes<'w, 's> { - pub fn iter(&'s self) -> impl Iterator + 's { +impl<'w> UiRootNodes<'w> { + pub fn iter(&'w self) -> impl Iterator + 'w { self.root_node_query .iter() .chain(self.root_ghost_node_query.iter().flat_map(|root_ghost| { @@ -54,30 +54,30 @@ impl<'w, 's> UiRootNodes<'w, 's> { #[cfg(feature = "ghost_nodes")] /// System param that gives access to UI children utilities, skipping over [`GhostNode`]. #[derive(SystemParam)] -pub struct UiChildren<'w, 's> { +pub struct UiChildren<'w> { ui_children_query: Query< 'w, - 's, + 'w, (Option<&'static Children>, Has), Or<(With, With)>, >, - changed_children_query: Query<'w, 's, Entity, Changed>, - children_query: Query<'w, 's, &'static Children>, - ghost_nodes_query: Query<'w, 's, Entity, With>, - parents_query: Query<'w, 's, &'static ChildOf>, + changed_children_query: Query<'w, 'w, Entity, Changed>, + children_query: Query<'w, 'w, &'static Children>, + ghost_nodes_query: Query<'w, 'w, Entity, With>, + parents_query: Query<'w, 'w, &'static ChildOf>, } #[cfg(not(feature = "ghost_nodes"))] /// System param that gives access to UI children utilities. #[derive(SystemParam)] -pub struct UiChildren<'w, 's> { - ui_children_query: Query<'w, 's, Option<&'static Children>, With>, - changed_children_query: Query<'w, 's, Entity, Changed>, - parents_query: Query<'w, 's, &'static ChildOf>, +pub struct UiChildren<'w> { + ui_children_query: Query<'w, 'w, Option<&'static Children>, With>, + changed_children_query: Query<'w, 'w, Entity, Changed>, + parents_query: Query<'w, 'w, &'static ChildOf>, } #[cfg(feature = "ghost_nodes")] -impl<'w, 's> UiChildren<'w, 's> { +impl<'w> UiChildren<'w> { /// Iterates the children of `entity`, skipping over [`GhostNode`]. /// /// Traverses the hierarchy depth-first to ensure child order. @@ -85,7 +85,7 @@ impl<'w, 's> UiChildren<'w, 's> { /// # Performance /// /// This iterator allocates if the `entity` node has more than 8 children (including ghost nodes). - pub fn iter_ui_children(&'s self, entity: Entity) -> UiChildrenIter<'w, 's> { + pub fn iter_ui_children(&'w self, entity: Entity) -> UiChildrenIter<'w> { UiChildrenIter { stack: self .ui_children_query @@ -98,14 +98,14 @@ impl<'w, 's> UiChildren<'w, 's> { } /// Returns the UI parent of the provided entity, skipping over [`GhostNode`]. - pub fn get_parent(&'s self, entity: Entity) -> Option { + pub fn get_parent(&self, entity: Entity) -> Option { self.parents_query .iter_ancestors(entity) .find(|entity| !self.ghost_nodes_query.contains(*entity)) } /// Iterates the [`GhostNode`]s between this entity and its UI children. - pub fn iter_ghost_nodes(&'s self, entity: Entity) -> Box + 's> { + pub fn iter_ghost_nodes(&'w self, entity: Entity) -> Box + 'w> { Box::new( self.children_query .get(entity) @@ -121,7 +121,7 @@ impl<'w, 's> UiChildren<'w, 's> { } /// Given an entity in the UI hierarchy, check if its set of children has changed, e.g if children has been added/removed or if the order has changed. - pub fn is_changed(&'s self, entity: Entity) -> bool { + pub fn is_changed(&self, entity: Entity) -> bool { self.changed_children_query.contains(entity) || self .iter_ghost_nodes(entity) @@ -129,15 +129,15 @@ impl<'w, 's> UiChildren<'w, 's> { } /// Returns `true` if the given entity is either a [`Node`] or a [`GhostNode`]. - pub fn is_ui_node(&'s self, entity: Entity) -> bool { + pub fn is_ui_node(&self, entity: Entity) -> bool { self.ui_children_query.contains(entity) } } #[cfg(not(feature = "ghost_nodes"))] -impl<'w, 's> UiChildren<'w, 's> { +impl<'w> UiChildren<'w> { /// Iterates the children of `entity`. - pub fn iter_ui_children(&'s self, entity: Entity) -> impl Iterator + 's { + pub fn iter_ui_children(&'w self, entity: Entity) -> impl Iterator + 'w { self.ui_children_query .get(entity) .ok() @@ -149,34 +149,34 @@ impl<'w, 's> UiChildren<'w, 's> { } /// Returns the UI parent of the provided entity. - pub fn get_parent(&'s self, entity: Entity) -> Option { + pub fn get_parent(&self, entity: Entity) -> Option { self.parents_query.get(entity).ok().map(ChildOf::parent) } /// Given an entity in the UI hierarchy, check if its set of children has changed, e.g if children has been added/removed or if the order has changed. - pub fn is_changed(&'s self, entity: Entity) -> bool { + pub fn is_changed(&self, entity: Entity) -> bool { self.changed_children_query.contains(entity) } /// Returns `true` if the given entity is either a [`Node`] or a [`GhostNode`]. - pub fn is_ui_node(&'s self, entity: Entity) -> bool { + pub fn is_ui_node(&self, entity: Entity) -> bool { self.ui_children_query.contains(entity) } } #[cfg(feature = "ghost_nodes")] -pub struct UiChildrenIter<'w, 's> { +pub struct UiChildrenIter<'w> { stack: SmallVec<[Entity; 8]>, - query: &'s Query< + query: &'w Query< + 'w, 'w, - 's, (Option<&'static Children>, Has), Or<(With, With)>, >, } #[cfg(feature = "ghost_nodes")] -impl<'w, 's> Iterator for UiChildrenIter<'w, 's> { +impl<'w> Iterator for UiChildrenIter<'w> { type Item = Entity; fn next(&mut self) -> Option { loop { diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 83140d0f3b9cb..594662efcabb9 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -271,13 +271,13 @@ impl ExtractedUiNodes { } #[derive(SystemParam)] -pub struct UiCameraMap<'w, 's> { - mapping: Query<'w, 's, RenderEntity>, +pub struct UiCameraMap<'w> { + mapping: Query<'w, 'w, RenderEntity>, } -impl<'w, 's> UiCameraMap<'w, 's> { +impl<'w> UiCameraMap<'w> { /// Get the default camera and create the mapper - pub fn get_mapper(&'w self) -> UiCameraMapper<'w, 's> { + pub fn get_mapper(&'w self) -> UiCameraMapper<'w> { UiCameraMapper { mapping: &self.mapping, camera_entity: Entity::PLACEHOLDER, @@ -286,13 +286,13 @@ impl<'w, 's> UiCameraMap<'w, 's> { } } -pub struct UiCameraMapper<'w, 's> { - mapping: &'w Query<'w, 's, RenderEntity>, +pub struct UiCameraMapper<'w> { + mapping: &'w Query<'w, 'w, RenderEntity>, camera_entity: Entity, render_entity: Entity, } -impl<'w, 's> UiCameraMapper<'w, 's> { +impl<'w> UiCameraMapper<'w> { /// Returns the render entity corresponding to the given `UiTargetCamera` or the default camera if `None`. pub fn map(&mut self, computed_target: &ComputedNodeTarget) -> Option { let camera_entity = computed_target.camera; diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index f5f914bdc01f6..dc53043956411 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -2675,13 +2675,13 @@ impl UiTargetCamera { pub struct IsDefaultUiCamera; #[derive(SystemParam)] -pub struct DefaultUiCamera<'w, 's> { - cameras: Query<'w, 's, (Entity, &'static Camera)>, - default_cameras: Query<'w, 's, Entity, (With, With)>, - primary_window: Query<'w, 's, Entity, With>, +pub struct DefaultUiCamera<'w> { + cameras: Query<'w, 'w, (Entity, &'static Camera)>, + default_cameras: Query<'w, 'w, Entity, (With, With)>, + primary_window: Query<'w, 'w, Entity, With>, } -impl<'w, 's> DefaultUiCamera<'w, 's> { +impl<'w> DefaultUiCamera<'w> { pub fn get(&self) -> Option { self.default_cameras.single().ok().or_else(|| { // If there isn't a single camera and the query isn't empty, there is two or more cameras queried. diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 785040c1e9057..5642e07cf0678 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -132,7 +132,7 @@ impl From for Text { pub type TextUiReader<'w, 's> = TextReader<'w, 's, Text>; /// UI alias for [`TextWriter`]. -pub type TextUiWriter<'w, 's> = TextWriter<'w, 's, Text>; +pub type TextUiWriter<'w> = TextWriter<'w, Text>; /// Text measurement for UI layout. See [`NodeMeasure`]. pub struct TextMeasure { diff --git a/examples/ecs/system_param.rs b/examples/ecs/system_param.rs index f32a783f72ca4..62735e1bf52f1 100644 --- a/examples/ecs/system_param.rs +++ b/examples/ecs/system_param.rs @@ -21,12 +21,12 @@ struct PlayerCount(usize); /// /// In this example, it includes a query and a mutable resource. #[derive(SystemParam)] -struct PlayerCounter<'w, 's> { - players: Query<'w, 's, &'static Player>, +struct PlayerCounter<'w> { + players: Query<'w, 'w, &'static Player>, count: ResMut<'w, PlayerCount>, } -impl<'w, 's> PlayerCounter<'w, 's> { +impl<'w> PlayerCounter<'w> { fn count(&mut self) { self.count.0 = self.players.iter().len(); } diff --git a/release-content/migration-guides/query_lifetime_change.md b/release-content/migration-guides/query_lifetime_change.md new file mode 100644 index 0000000000000..58d9ae84c518f --- /dev/null +++ b/release-content/migration-guides/query_lifetime_change.md @@ -0,0 +1,16 @@ +--- +title: Query lifetime change +pull_requests: [18860] +--- +Query lifetimes have been change if you are deriving `SystemParam` with `Query` you will need to update. + +```diff +#[derive(SystemParam)] +-- struct MyParam<'w, 's> { +-- query: Query<'w, 's, Entity> +-- } +++ struct MyParam<'w> { +++ query: Query<'w, 'w, Entity> +++ } + +```