Skip to content

Commit 6f39e44

Browse files
authored
Introduce methods on QueryState to obtain a Query (#15858)
# Objective Simplify and expand the API for `QueryState`. `QueryState` has a lot of methods that mirror those on `Query`. These are then multiplied by variants that take `&World`, `&mut World`, and `UnsafeWorldCell`. In addition, many of them have `_manual` variants that take `&QueryState` and avoid calling `update_archetypes()`. Not all of the combinations exist, however, so some operations are not possible. ## Solution Introduce methods to get a `Query` from a `QueryState`. That will reduce duplication between the types, and ensure that the full `Query` API is always available for `QueryState`. Introduce methods on `Query` that consume the query to return types with the full `'w` lifetime. This avoids issues with borrowing where things like `query_state.query(&world).get(entity)` don't work because they borrow from the temporary `Query`. Finally, implement `Copy` for read-only `Query`s. `get_inner` and `iter_inner` currently take `&self`, so changing them to consume `self` would be a breaking change. By making `Query: Copy`, they can consume a copy of `self` and continue to work. The consuming methods also let us simplify the implementation of methods on `Query`, by doing `fn foo(&self) { self.as_readonly().foo_inner() }` and `fn foo_mut(&mut self) { self.reborrow().foo_inner() }`. That structure makes it more difficult to accidentally extend lifetimes, since the safe `as_readonly()` and `reborrow()` methods shrink them appropriately. The optimizer is able to see that they are both identity functions and inline them, so there should be no performance cost. Note that this change would conflict with #15848. If `QueryState` is stored as a `Cow`, then the consuming methods cannot be implemented, and `Copy` cannot be implemented. ## Future Work The next step is to mark the methods on `QueryState` as `#[deprecated]`, and move the implementations into `Query`. ## Migration Guide `Query::to_readonly` has been renamed to `Query::as_readonly`.
1 parent 2ea5e9b commit 6f39e44

File tree

8 files changed

+548
-243
lines changed

8 files changed

+548
-243
lines changed

crates/bevy_ecs/compile_fail/tests/ui/query_to_readonly.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,29 @@ struct Foo;
66
fn for_loops(mut query: Query<&mut Foo>) {
77
// this should fail to compile
88
for _ in query.iter_mut() {
9-
for _ in query.to_readonly().iter() {}
9+
for _ in query.as_readonly().iter() {}
1010
//~^ E0502
1111
}
1212

1313
// this should fail to compile
14-
for _ in query.to_readonly().iter() {
14+
for _ in query.as_readonly().iter() {
1515
for _ in query.iter_mut() {}
1616
//~^ E0502
1717
}
1818

1919
// this should *not* fail to compile
20-
for _ in query.to_readonly().iter() {
21-
for _ in query.to_readonly().iter() {}
20+
for _ in query.as_readonly().iter() {
21+
for _ in query.as_readonly().iter() {}
2222
}
2323

2424
// this should *not* fail to compile
25-
for _ in query.to_readonly().iter() {
25+
for _ in query.as_readonly().iter() {
2626
for _ in query.iter() {}
2727
}
2828

2929
// this should *not* fail to compile
3030
for _ in query.iter() {
31-
for _ in query.to_readonly().iter() {}
31+
for _ in query.as_readonly().iter() {}
3232
}
3333
}
3434

@@ -38,11 +38,11 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
3838
let mut mut_foo = query.single_mut();
3939

4040
// This solves "temporary value dropped while borrowed"
41-
let readonly_query = query.to_readonly();
41+
let readonly_query = query.as_readonly();
4242
//~^ E0502
4343

4444
let ref_foo = readonly_query.single();
45-
45+
4646
*mut_foo = Foo;
4747

4848
println!("{ref_foo:?}");
@@ -51,7 +51,7 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
5151
// this should fail to compile
5252
{
5353
// This solves "temporary value dropped while borrowed"
54-
let readonly_query = query.to_readonly();
54+
let readonly_query = query.as_readonly();
5555

5656
let ref_foo = readonly_query.single();
5757

@@ -66,7 +66,7 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
6666
// this should *not* fail to compile
6767
{
6868
// This solves "temporary value dropped while borrowed"
69-
let readonly_query = query.to_readonly();
69+
let readonly_query = query.as_readonly();
7070

7171
let readonly_foo = readonly_query.single();
7272

crates/bevy_ecs/src/query/iter.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -849,13 +849,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
849849
// SAFETY:
850850
// `self.world` has permission to access the required components.
851851
// The original query iter has not been iterated on, so no items are aliased from it.
852-
let query_lens = unsafe {
853-
query_lens_state.iter_unchecked_manual(
854-
world,
855-
world.last_change_tick(),
856-
world.change_tick(),
857-
)
858-
};
852+
let query_lens = unsafe { query_lens_state.query_unchecked_manual(world) }.into_iter();
859853
let mut keyed_query: Vec<_> = query_lens
860854
.map(|(key, entity)| (key, NeutralOrd(entity)))
861855
.collect();
@@ -1689,14 +1683,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityBorrow>>
16891683
// SAFETY:
16901684
// `self.world` has permission to access the required components.
16911685
// The original query iter has not been iterated on, so no items are aliased from it.
1692-
let query_lens = unsafe {
1693-
query_lens_state.iter_many_unchecked_manual(
1694-
self.entity_iter,
1695-
world,
1696-
world.last_change_tick(),
1697-
world.change_tick(),
1698-
)
1699-
};
1686+
let query_lens = unsafe { query_lens_state.query_unchecked_manual(world) }
1687+
.iter_many_inner(self.entity_iter);
17001688
let mut keyed_query: Vec<_> = query_lens
17011689
.map(|(key, entity)| (key, NeutralOrd(entity)))
17021690
.collect();

crates/bevy_ecs/src/query/state.rs

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
WorldQuery,
1111
},
1212
storage::{SparseSetIndex, TableId},
13+
system::Query,
1314
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
1415
};
1516

@@ -154,9 +155,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
154155
pub fn matched_archetypes(&self) -> impl Iterator<Item = ArchetypeId> + '_ {
155156
self.matched_archetypes.ones().map(ArchetypeId::new)
156157
}
157-
}
158158

159-
impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
160159
/// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`.
161160
pub fn new(world: &mut World) -> Self {
162161
let mut state = Self::new_uninitialized(world);
@@ -319,6 +318,124 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
319318
state
320319
}
321320

321+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
322+
///
323+
/// This will create read-only queries, see [`Self::query_mut`] for mutable queries.
324+
pub fn query<'w, 's>(&'s mut self, world: &'w World) -> Query<'w, 's, D::ReadOnly, F> {
325+
self.update_archetypes(world);
326+
self.query_manual(world)
327+
}
328+
329+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
330+
///
331+
/// This method is slightly more efficient than [`QueryState::query`] in some situations, since
332+
/// it does not update this instance's internal cache. The resulting query may skip an entity that
333+
/// belongs to an archetype that has not been cached.
334+
///
335+
/// To ensure that the cache is up to date, call [`QueryState::update_archetypes`] before this method.
336+
/// The cache is also updated in [`QueryState::new`], [`QueryState::get`], or any method with mutable
337+
/// access to `self`.
338+
///
339+
/// This will create read-only queries, see [`Self::query_mut`] for mutable queries.
340+
pub fn query_manual<'w, 's>(&'s self, world: &'w World) -> Query<'w, 's, D::ReadOnly, F> {
341+
// SAFETY: We have read access to the entire world, and we call `as_readonly()` so the query only performs read access.
342+
unsafe {
343+
self.as_readonly()
344+
.query_unchecked_manual(world.as_unsafe_world_cell_readonly())
345+
}
346+
}
347+
348+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
349+
pub fn query_mut<'w, 's>(&'s mut self, world: &'w mut World) -> Query<'w, 's, D, F> {
350+
let last_run = world.last_change_tick();
351+
let this_run = world.change_tick();
352+
// SAFETY: We have exclusive access to the entire world.
353+
unsafe { self.query_unchecked_with_ticks(world.as_unsafe_world_cell(), last_run, this_run) }
354+
}
355+
356+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
357+
///
358+
/// # Safety
359+
///
360+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
361+
/// have unique access to the components they query.
362+
pub unsafe fn query_unchecked<'w, 's>(
363+
&'s mut self,
364+
world: UnsafeWorldCell<'w>,
365+
) -> Query<'w, 's, D, F> {
366+
self.update_archetypes_unsafe_world_cell(world);
367+
// SAFETY: Caller ensures we have the required access
368+
unsafe { self.query_unchecked_manual(world) }
369+
}
370+
371+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
372+
///
373+
/// This method is slightly more efficient than [`QueryState::query_unchecked`] in some situations, since
374+
/// it does not update this instance's internal cache. The resulting query may skip an entity that
375+
/// belongs to an archetype that has not been cached.
376+
///
377+
/// To ensure that the cache is up to date, call [`QueryState::update_archetypes`] before this method.
378+
/// The cache is also updated in [`QueryState::new`], [`QueryState::get`], or any method with mutable
379+
/// access to `self`.
380+
///
381+
/// # Safety
382+
///
383+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
384+
/// have unique access to the components they query.
385+
pub unsafe fn query_unchecked_manual<'w, 's>(
386+
&'s self,
387+
world: UnsafeWorldCell<'w>,
388+
) -> Query<'w, 's, D, F> {
389+
let last_run = world.last_change_tick();
390+
let this_run = world.change_tick();
391+
// SAFETY: The caller ensured we have the correct access to the world.
392+
unsafe { self.query_unchecked_manual_with_ticks(world, last_run, this_run) }
393+
}
394+
395+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
396+
///
397+
/// # Safety
398+
///
399+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
400+
/// have unique access to the components they query.
401+
pub unsafe fn query_unchecked_with_ticks<'w, 's>(
402+
&'s mut self,
403+
world: UnsafeWorldCell<'w>,
404+
last_run: Tick,
405+
this_run: Tick,
406+
) -> Query<'w, 's, D, F> {
407+
self.update_archetypes_unsafe_world_cell(world);
408+
// SAFETY: The caller ensured we have the correct access to the world.
409+
unsafe { self.query_unchecked_manual_with_ticks(world, last_run, this_run) }
410+
}
411+
412+
/// Creates a [`Query`] from the given [`QueryState`] and [`World`].
413+
///
414+
/// This method is slightly more efficient than [`QueryState::query_unchecked_with_ticks`] in some situations, since
415+
/// it does not update this instance's internal cache. The resulting query may skip an entity that
416+
/// belongs to an archetype that has not been cached.
417+
///
418+
/// To ensure that the cache is up to date, call [`QueryState::update_archetypes`] before this method.
419+
/// The cache is also updated in [`QueryState::new`], [`QueryState::get`], or any method with mutable
420+
/// access to `self`.
421+
///
422+
/// # Safety
423+
///
424+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
425+
/// have unique access to the components they query.
426+
pub unsafe fn query_unchecked_manual_with_ticks<'w, 's>(
427+
&'s self,
428+
world: UnsafeWorldCell<'w>,
429+
last_run: Tick,
430+
this_run: Tick,
431+
) -> Query<'w, 's, D, F> {
432+
self.validate_world(world.id());
433+
// SAFETY:
434+
// - The caller ensured we have the correct access to the world.
435+
// - `validate_world` did not panic, so the world matches.
436+
unsafe { Query::new(world, self, last_run, this_run) }
437+
}
438+
322439
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
323440
///
324441
/// This is equivalent to `self.iter().next().is_none()`, and thus the worst case runtime will be `O(n)`
@@ -624,7 +741,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
624741
/// You should not call [`update_archetypes`](Self::update_archetypes) on the returned [`QueryState`] as the result will be unpredictable.
625742
/// You might end up with a mix of archetypes that only matched the original query + archetypes that only match
626743
/// the new [`QueryState`]. Most of the safe methods on [`QueryState`] call [`QueryState::update_archetypes`] internally, so this
627-
/// best used through a [`Query`](crate::system::Query).
744+
/// best used through a [`Query`]
628745
pub fn transmute<'a, NewD: QueryData>(
629746
&self,
630747
world: impl Into<UnsafeWorldCell<'a>>,

crates/bevy_ecs/src/system/mod.rs

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ mod tests {
334334
archetype::{ArchetypeComponentId, Archetypes},
335335
bundle::Bundles,
336336
change_detection::DetectChanges,
337-
component::{Component, Components, Tick},
337+
component::{Component, Components},
338338
entity::{Entities, Entity},
339339
prelude::{AnyOf, EntityRef},
340340
query::{Added, Changed, Or, With, Without},
@@ -1361,7 +1361,7 @@ mod tests {
13611361
fn mutable_query(mut query: Query<&mut A>) {
13621362
for _ in &mut query {}
13631363

1364-
immutable_query(query.to_readonly());
1364+
immutable_query(query.as_readonly());
13651365
}
13661366

13671367
fn immutable_query(_: Query<&A>) {}
@@ -1376,7 +1376,7 @@ mod tests {
13761376
fn mutable_query(mut query: Query<Option<&mut A>>) {
13771377
for _ in &mut query {}
13781378

1379-
immutable_query(query.to_readonly());
1379+
immutable_query(query.as_readonly());
13801380
}
13811381

13821382
fn immutable_query(_: Query<Option<&A>>) {}
@@ -1391,7 +1391,7 @@ mod tests {
13911391
fn mutable_query(mut query: Query<(&mut A, &B)>) {
13921392
for _ in &mut query {}
13931393

1394-
immutable_query(query.to_readonly());
1394+
immutable_query(query.as_readonly());
13951395
}
13961396

13971397
fn immutable_query(_: Query<(&A, &B)>) {}
@@ -1406,7 +1406,7 @@ mod tests {
14061406
fn mutable_query(mut query: Query<(&mut A, &mut B)>) {
14071407
for _ in &mut query {}
14081408

1409-
immutable_query(query.to_readonly());
1409+
immutable_query(query.as_readonly());
14101410
}
14111411

14121412
fn immutable_query(_: Query<(&A, &B)>) {}
@@ -1421,7 +1421,7 @@ mod tests {
14211421
fn mutable_query(mut query: Query<(&mut A, &mut B), With<C>>) {
14221422
for _ in &mut query {}
14231423

1424-
immutable_query(query.to_readonly());
1424+
immutable_query(query.as_readonly());
14251425
}
14261426

14271427
fn immutable_query(_: Query<(&A, &B), With<C>>) {}
@@ -1436,7 +1436,7 @@ mod tests {
14361436
fn mutable_query(mut query: Query<(&mut A, &mut B), Without<C>>) {
14371437
for _ in &mut query {}
14381438

1439-
immutable_query(query.to_readonly());
1439+
immutable_query(query.as_readonly());
14401440
}
14411441

14421442
fn immutable_query(_: Query<(&A, &B), Without<C>>) {}
@@ -1451,7 +1451,7 @@ mod tests {
14511451
fn mutable_query(mut query: Query<(&mut A, &mut B), Added<C>>) {
14521452
for _ in &mut query {}
14531453

1454-
immutable_query(query.to_readonly());
1454+
immutable_query(query.as_readonly());
14551455
}
14561456

14571457
fn immutable_query(_: Query<(&A, &B), Added<C>>) {}
@@ -1466,7 +1466,7 @@ mod tests {
14661466
fn mutable_query(mut query: Query<(&mut A, &mut B), Changed<C>>) {
14671467
for _ in &mut query {}
14681468

1469-
immutable_query(query.to_readonly());
1469+
immutable_query(query.as_readonly());
14701470
}
14711471

14721472
fn immutable_query(_: Query<(&A, &B), Changed<C>>) {}
@@ -1572,24 +1572,6 @@ mod tests {
15721572
});
15731573
}
15741574

1575-
#[test]
1576-
#[should_panic = "Encountered a mismatched World."]
1577-
fn query_validates_world_id() {
1578-
let mut world1 = World::new();
1579-
let world2 = World::new();
1580-
let qstate = world1.query::<()>();
1581-
// SAFETY: doesnt access anything
1582-
let query = unsafe {
1583-
Query::new(
1584-
world2.as_unsafe_world_cell_readonly(),
1585-
&qstate,
1586-
Tick::new(0),
1587-
Tick::new(0),
1588-
)
1589-
};
1590-
query.iter();
1591-
}
1592-
15931575
#[test]
15941576
#[should_panic]
15951577
fn assert_system_does_not_conflict() {

0 commit comments

Comments
 (0)