diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 26440c0c64..b36be3fee7 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1335,7 +1335,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// ## Panics /// - /// This will panic if `NewD` is not a subset of the original fetch `Q` + /// This will panic if `NewD` is not a subset of the original fetch `D` /// /// ## Example /// @@ -1376,19 +1376,108 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// ## Allowed Transmutes /// - /// Besides removing parameters from the query, you can also - /// make limited changes to the types of parameters. + /// Besides removing parameters from the query, + /// you can also make limited changes to the types of parameters. + /// The new query must have a subset of the *read*, *write*, and *required* access of the original query. /// - /// * Can always add/remove [`Entity`] - /// * Can always add/remove [`EntityLocation`] - /// * Can always add/remove [`&Archetype`] - /// * `Ref` <-> `&T` - /// * `&mut T` -> `&T` - /// * `&mut T` -> `Ref` - /// * [`EntityMut`](crate::world::EntityMut) -> [`EntityRef`](crate::world::EntityRef) + /// * `&mut T` and [`Mut`](crate::change_detection::Mut) have read, write, and required access to `T` + /// * `&T` and [`Ref`](crate::change_detection::Ref) have read and required access to `T` + /// * [`Option`] and [`AnyOf<(D, ...)>`](crate::query::AnyOf) have the read and write access of the subqueries, but no required access + /// * Tuples of query data and `#[derive(QueryData)]` structs have the union of the access of their subqueries + /// * [`EntityMut`](crate::world::EntityMut) has read and write access to all components, but no required access + /// * [`EntityRef`](crate::world::EntityRef) has read access to all components, but no required access + /// * [`Entity`], [`EntityLocation`], [`&Archetype`], [`Has`], and [`PhantomData`] have no access at all, + /// so can be added to any query + /// * [`FilteredEntityRef`](crate::world::FilteredEntityRef) and [`FilteredEntityMut`](crate::world::FilteredEntityMut) + /// have access determined by the [`QueryBuilder`](crate::query::QueryBuilder) used to construct them. + /// Any query can be transmuted to them, and they will receive the access of the source query, + /// but only if they are the top-level query and not nested + /// * [`Added`](crate::query::Added) and [`Changed`](crate::query::Changed) filters have read and required access to `T` + /// * [`With`](crate::query::With) and [`Without`](crate::query::Without) filters have no access at all, + /// so can be added to any query + /// * Tuples of query filters and `#[derive(QueryFilter)]` structs have the union of the access of their subqueries + /// * [`Or<(F, ...)>`](crate::query::Or) filters have the read access of the subqueries, but no required access + /// + /// ### Examples of valid transmutes + /// + /// ```rust + /// # use bevy_ecs::{ + /// # prelude::*, + /// # archetype::Archetype, + /// # entity::EntityLocation, + /// # query::{QueryData, QueryFilter}, + /// # world::{FilteredEntityMut, FilteredEntityRef}, + /// # }; + /// # use std::marker::PhantomData; + /// # + /// # fn assert_valid_transmute() { + /// # assert_valid_transmute_filtered::(); + /// # } + /// # + /// # fn assert_valid_transmute_filtered() { + /// # let mut world = World::new(); + /// # // Make sure all components in the new query are initialized + /// # let state = world.query_filtered::(); + /// # let state = world.query_filtered::(); + /// # state.transmute_filtered::(&world); + /// # } + /// # + /// # #[derive(Component)] + /// # struct T; + /// # + /// # #[derive(Component)] + /// # struct U; + /// # + /// # #[derive(Component)] + /// # struct V; + /// # + /// // `&mut T` and `Mut` access the same data and can be transmuted to each other, + /// // `&T` and `Ref` access the same data and can be transmuted to each other, + /// // and mutable versions can be transmuted to read-only versions + /// assert_valid_transmute::<&mut T, &T>(); + /// assert_valid_transmute::<&mut T, Mut>(); + /// assert_valid_transmute::, &mut T>(); + /// assert_valid_transmute::<&T, Ref>(); + /// assert_valid_transmute::, &T>(); + /// + /// // The structure can be rearranged, or subqueries dropped + /// assert_valid_transmute::<(&T, &U), &T>(); + /// assert_valid_transmute::<((&T, &U), &V), (&T, (&U, &V))>(); + /// assert_valid_transmute::, (Option<&T>, Option<&U>)>(); + /// + /// // Queries with no access can be freely added + /// assert_valid_transmute::< + /// &T, + /// (&T, Entity, EntityLocation, &Archetype, Has, PhantomData), + /// >(); + /// + /// // Required access can be transmuted to optional, + /// // and optional access can be transmuted to other optional access + /// assert_valid_transmute::<&T, Option<&T>>(); + /// assert_valid_transmute::, Option<&T>>(); + /// // Note that removing subqueries from `AnyOf` will result + /// // in an `AnyOf` where all subqueries can yield `None`! + /// assert_valid_transmute::, AnyOf<(&T, &U)>>(); + /// assert_valid_transmute::>(); + /// + /// // Anything can be transmuted to `FilteredEntityRef` or `FilteredEntityMut` + /// // This will create a `FilteredEntityMut` that only has read access to `T` + /// assert_valid_transmute::<&T, FilteredEntityMut>(); + /// // This transmute will succeed, but the `FilteredEntityMut` will have no access! + /// // It must be the top-level query to be given access, but here it is nested in a tuple. + /// assert_valid_transmute::<&T, (Entity, FilteredEntityMut)>(); + /// + /// // `Added` and `Changed` filters have the same access as `&T` data + /// // Remember that they are only evaluated on the transmuted query, not the original query! + /// assert_valid_transmute_filtered::, &T, ()>(); + /// assert_valid_transmute_filtered::<&mut T, (), &T, Added>(); + /// // Nested inside of an `Or` filter, they have the same access as `Option<&T>`. + /// assert_valid_transmute_filtered::, (), Entity, Or<(Changed, With)>>(); + /// ``` /// /// [`EntityLocation`]: crate::entity::EntityLocation /// [`&Archetype`]: crate::archetype::Archetype + /// [`Has`]: crate::query::Has #[track_caller] pub fn transmute_lens(&mut self) -> QueryLens<'_, NewD> { self.transmute_lens_filtered::()