From 7e062b000e434d90b33c5a696c50227bc3fb3f0a Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Wed, 14 Jan 2026 02:43:14 -0600 Subject: [PATCH 1/5] `EntityRef`/`EntityMut` scopes --- crates/bevy_ecs/src/entity/clone_entities.rs | 9 +- crates/bevy_ecs/src/observer/runner.rs | 8 +- crates/bevy_ecs/src/query/fetch.rs | 31 +- crates/bevy_ecs/src/reflect/component.rs | 4 +- .../src/world/entity_access/access_scope.rs | 260 +++++++ .../bevy_ecs/src/world/entity_access/all.rs | 260 +++++++ .../world/entity_access/component_fetch.rs | 183 +++-- .../src/world/entity_access/entity_mut.rs | 386 +++------- .../src/world/entity_access/entity_ref.rs | 128 ++-- .../src/world/entity_access/except.rs | 537 +------------ .../src/world/entity_access/filtered.rs | 709 ++---------------- .../bevy_ecs/src/world/entity_access/mod.rs | 15 +- .../src/world/entity_access/world_mut.rs | 18 +- crates/bevy_ecs/src/world/entity_fetch.rs | 20 +- crates/bevy_ecs/src/world/mod.rs | 6 +- .../bevy_ecs/src/world/unsafe_world_cell.rs | 182 +++-- examples/ecs/dynamic.rs | 2 +- examples/stress_tests/many_components.rs | 2 +- .../migration-guides/scoped_entities.md | 35 + 19 files changed, 1121 insertions(+), 1674 deletions(-) create mode 100644 crates/bevy_ecs/src/world/entity_access/access_scope.rs create mode 100644 crates/bevy_ecs/src/world/entity_access/all.rs create mode 100644 release-content/migration-guides/scoped_entities.md diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 7b1884e54e5d1..94b409ba12090 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -11,7 +11,7 @@ use crate::{ entity::{hash_map::EntityHashMap, Entity, EntityAllocator, EntityMapper}, query::DebugCheckedUnwrap, relationship::RelationshipHookMode, - world::World, + world::{All, World}, }; use alloc::{boxed::Box, collections::VecDeque, vec::Vec}; use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet}; @@ -633,8 +633,11 @@ impl EntityCloner { // SAFETY: // - There are no other mutable references to source entity. // - `component` is from `source_entity`'s archetype - let source_component_ptr = - unsafe { source_entity.get_by_id(component).debug_checked_unwrap() }; + let source_component_ptr = unsafe { + source_entity + .get_by_id(&All, component) + .debug_checked_unwrap() + }; let source_component = SourceComponent { info, diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index dfffe3bec60cd..be4a5f886379f 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -9,7 +9,7 @@ use crate::{ prelude::*, query::DebugCheckedUnwrap, system::{ObserverSystem, RunSystemError}, - world::DeferredWorld, + world::{All, DeferredWorld}, }; use bevy_ptr::PtrMut; @@ -44,7 +44,11 @@ pub(super) unsafe fn observer_system_runner().debug_checked_unwrap() }; + let mut state = unsafe { + observer_cell + .get_mut::(&All) + .debug_checked_unwrap() + }; // TODO: Move this check into the observer cache to avoid dynamic dispatch let last_trigger = world.last_trigger_id(); diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index d0b191513e8ed..547325e0b8a7f 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -10,8 +10,8 @@ use crate::{ }, storage::{ComponentSparseSet, Table, TableRow}, world::{ - unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, - FilteredEntityMut, FilteredEntityRef, Mut, Ref, World, + unsafe_world_cell::UnsafeWorldCell, All, EntityMut, EntityMutExcept, EntityRef, + EntityRefExcept, Except, Filtered, FilteredEntityMut, FilteredEntityRef, Mut, Ref, World, }, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; @@ -366,10 +366,11 @@ pub type ROQueryItem<'w, 's, D> = QueryItem<'w, 's, ::ReadOnly>; /// A [`QueryData`] that does not borrow from its [`QueryState`](crate::query::QueryState). /// -/// This is implemented by most `QueryData` types. -/// The main exceptions are [`FilteredEntityRef`], [`FilteredEntityMut`], [`EntityRefExcept`], and [`EntityMutExcept`], -/// which borrow an access list from their query state. -/// Consider using a full [`EntityRef`] or [`EntityMut`] if you would need those. +/// This is implemented by most `QueryData` types. The main exceptions are +/// [`FilteredEntityRef`], [`FilteredEntityMut`], [`EntityRefExcept`], and +/// [`EntityMutExcept`], which borrow an access list from their query state. +/// Consider using a full [`EntityRef`] or [`EntityMut`] if you would +/// need those. pub trait ReleaseStateQueryData: QueryData { /// Releases the borrow from the query state by converting an item to have a `'static` state lifetime. fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static>; @@ -856,7 +857,7 @@ unsafe impl<'a> QueryData for EntityRef<'a> { .debug_checked_unwrap() }; // SAFETY: Read-only access to every component has been registered. - Some(unsafe { EntityRef::new(cell) }) + Some(unsafe { EntityRef::new(cell, All) }) } fn iter_access(_state: &Self::State) -> impl Iterator> { @@ -966,7 +967,7 @@ unsafe impl<'a> QueryData for EntityMut<'a> { .debug_checked_unwrap() }; // SAFETY: mutable access to every component has been registered. - Some(unsafe { EntityMut::new(cell) }) + Some(unsafe { EntityMut::new(cell, All) }) } fn iter_access(_state: &Self::State) -> impl Iterator> { @@ -1093,8 +1094,8 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityRef<'a, 'b> { .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) .debug_checked_unwrap() }; - // SAFETY: mutable access to every component has been registered. - Some(unsafe { FilteredEntityRef::new(cell, access) }) + // SAFETY: read-only access to the components in `access` has been registered. + Some(unsafe { EntityRef::new(cell, Filtered(access)) }) } fn iter_access(state: &Self::State) -> impl Iterator> { @@ -1216,8 +1217,8 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityMut<'a, 'b> { .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) .debug_checked_unwrap() }; - // SAFETY: mutable access to every component has been registered. - Some(unsafe { FilteredEntityMut::new(cell, access) }) + // SAFETY: mutable access to the components in `access` has been registered. + Some(unsafe { EntityMut::new(cell, Filtered(access)) }) } fn iter_access(state: &Self::State) -> impl Iterator> { @@ -1331,7 +1332,8 @@ where .world .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) .unwrap(); - Some(EntityRefExcept::new(cell, access)) + // SAFETY: `access` was constructed based on the components in `B`, and read-only access registered. + Some(unsafe { EntityRef::new(cell, Except::new(access)) }) } fn iter_access(state: &Self::State) -> impl Iterator> { @@ -1450,7 +1452,8 @@ where .world .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) .unwrap(); - Some(EntityMutExcept::new(cell, access)) + // SAFETY: `access` was constructed based on the components in `B`, and mutable access registered. + Some(unsafe { EntityMut::new(cell, Except::new(access)) }) } fn iter_access(state: &Self::State) -> impl Iterator> { diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index c38deeb31683c..bb6be597fbfd2 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -65,7 +65,7 @@ use crate::{ prelude::Component, relationship::RelationshipHookMode, world::{ - unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityWorldMut, FilteredEntityMut, + unsafe_world_cell::UnsafeEntityCell, All, EntityMut, EntityWorldMut, FilteredEntityMut, FilteredEntityRef, World, }, }; @@ -383,7 +383,7 @@ impl FromType for ReflectComponent { // SAFETY: reflect_unchecked_mut is an unsafe function pointer used by // `reflect_unchecked_mut` which must be called with an UnsafeEntityCell with access to the component `C` on the `entity` // guard ensures `C` is a mutable component - let c = unsafe { entity.get_mut_assume_mutable::() }; + let c = unsafe { entity.get_mut_assume_mutable::(&All) }; c.map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) }, register_component: |world: &mut World| -> ComponentId { diff --git a/crates/bevy_ecs/src/world/entity_access/access_scope.rs b/crates/bevy_ecs/src/world/entity_access/access_scope.rs new file mode 100644 index 0000000000000..3734afee55197 --- /dev/null +++ b/crates/bevy_ecs/src/world/entity_access/access_scope.rs @@ -0,0 +1,260 @@ +use core::{marker::PhantomData, ops::Deref}; + +use crate::{ + bundle::Bundle, + component::{ComponentId, Components}, + query::Access, +}; + +/// Defines the set of [`Component`]s accessible by the entity reference types +/// [`EntityRef`] and [`EntityMut`]. +/// +/// The following scopes are provided: +/// - [`All`]: Provides access to all components. This is the default scope. +/// - [`Filtered`]: Provides access only to the components specified in an +/// [`Access`]. This is used by [`FilteredEntityRef`] and [`FilteredEntityMut`]. +/// - [`Except`]: Provides access to all components except those in a specified +/// [`Bundle`]. This is used by [`EntityRefExcept`] and [`EntityMutExcept`]. +/// +/// # Safety +/// +/// Implementors must ensure that [`AccessScope::reborrow`] does not extend the +/// permissions of the scope with access it did not previously have. +/// +/// [`Component`]: crate::component::Component +/// [`EntityRef`]: crate::world::EntityRef +/// [`EntityMut`]: crate::world::EntityMut +/// [`FilteredEntityRef`]: crate::world::FilteredEntityRef +/// [`FilteredEntityMut`]: crate::world::FilteredEntityMut +/// [`EntityRefExcept`]: crate::world::EntityRefExcept +/// [`EntityMutExcept`]: crate::world::EntityMutExcept +pub unsafe trait AccessScope { + /// The reborrowed version of this scope. This is typically `Self`, but with + /// shorter lifetimes. + type Borrow<'a>: AccessScope + where + Self: 'a; + + /// Reborrows the scope for shorter lifetimes. + fn reborrow(&self) -> Self::Borrow<'_>; + + /// Returns `true` if the scope allows reading the specified component. + fn can_read(&self, id: ComponentId, components: &Components) -> bool; + + /// Returns `true` if the scope allows writing the specified component. + fn can_write(&self, id: ComponentId, components: &Components) -> bool; +} + +/// [`AccessScope`] that provides access to all of an entity's components. This +/// is the default scope of [`EntityRef`] and [`EntityMut`]. +/// +/// [`EntityRef`]: crate::world::EntityRef +/// [`EntityMut`]: crate::world::EntityMut +#[derive(Clone, Copy)] +pub struct All; + +// SAFETY: `reborrow` does not extend access permissions. +unsafe impl AccessScope for All { + type Borrow<'a> + = Self + where + Self: 'a; + + fn reborrow(&self) -> Self::Borrow<'_> { + *self + } + + fn can_read(&self, _id: ComponentId, _components: &Components) -> bool { + true + } + + fn can_write(&self, _id: ComponentId, _components: &Components) -> bool { + true + } +} + +/// [`AccessScope`] that provides access to only the components specified in the +/// provided [`Access`]. [`FilteredEntityRef`] and [`FilteredEntityMut`] use +/// this scope. +/// +/// [`FilteredEntityRef`]: crate::world::FilteredEntityRef +/// [`FilteredEntityMut`]: crate::world::FilteredEntityMut +#[derive(Clone, Copy)] +pub struct Filtered<'s>(pub &'s Access); + +// SAFETY: `reborrow` does not extend access permissions. +unsafe impl AccessScope for Filtered<'_> { + type Borrow<'a> + = Self + where + Self: 'a; + + fn reborrow(&self) -> Self::Borrow<'_> { + *self + } + + fn can_read(&self, id: ComponentId, _components: &Components) -> bool { + self.has_component_read(id) + } + + fn can_write(&self, id: ComponentId, _components: &Components) -> bool { + self.has_component_write(id) + } +} + +impl<'s, B: Bundle> From> for Filtered<'s> { + fn from(value: Except<'s, B>) -> Self { + // SAFETY: We're not discarding the `Except` semantics as long as the + // `Access` matched the `Bundle` `B`. + Filtered(value.0) + } +} + +impl Deref for Filtered<'_> { + type Target = Access; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +/// [`AccessScope`] that provides access to all components except those in the +/// provided [`Bundle`] `B`. [`EntityRefExcept`] and [`EntityMutExcept`] use +/// this scope. +/// +/// [`EntityRefExcept`]: crate::world::EntityRefExcept +/// [`EntityMutExcept`]: crate::world::EntityMutExcept +pub struct Except<'s, B: Bundle>(pub &'s Access, PhantomData); + +impl<'s, B: Bundle> Except<'s, B> { + /// Creates a new `Except` scope from the given [`Access`]. + /// + /// # Safety + /// + /// The provided `Access` must accurately reflect the components in `B`. + pub unsafe fn new(access: &'s Access) -> Self { + Except(access, PhantomData) + } +} + +impl<'s, B: Bundle> Copy for Except<'s, B> {} + +impl<'s, B: Bundle> Clone for Except<'s, B> { + fn clone(&self) -> Self { + *self + } +} + +// SAFETY: `reborrow` does not extend access permissions. +unsafe impl AccessScope for Except<'_, B> { + type Borrow<'a> + = Self + where + Self: 'a; + + fn reborrow(&self) -> Self::Borrow<'_> { + *self + } + + fn can_read(&self, id: ComponentId, components: &Components) -> bool { + B::get_component_ids(components) + .flatten() + .all(|b_id| b_id != id) + } + + fn can_write(&self, id: ComponentId, components: &Components) -> bool { + B::get_component_ids(components) + .flatten() + .all(|b_id| b_id != id) + } +} + +impl Deref for Except<'_, B> { + type Target = Access; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +#[cfg(test)] +mod tests { + use bevy_ecs_macros::Component; + + use crate::{ + query::Access, + world::{AccessScope, Except, World}, + }; + + #[derive(Component)] + pub struct TestComponent; + + #[test] + fn all() { + let mut world = World::new(); + + let c1 = world.register_component::>(); + let c2 = world.register_component::>(); + let c3 = world.register_component::>(); + + let scope = super::All; + + assert!(scope.can_read(c1, world.components())); + assert!(scope.can_write(c1, world.components())); + + assert!(scope.can_read(c2, world.components())); + assert!(scope.can_write(c2, world.components())); + + assert!(scope.can_read(c3, world.components())); + assert!(scope.can_write(c3, world.components())); + } + + #[test] + fn filtered() { + let mut world = World::new(); + + let c1 = world.register_component::>(); + let c2 = world.register_component::>(); + let c3 = world.register_component::>(); + + let mut access = Access::new(); + access.add_component_read(c1); + access.add_component_write(c2); + + let scope = super::Filtered(&access); + + assert!(scope.can_read(c1, world.components())); + assert!(!scope.can_write(c1, world.components())); + + assert!(scope.can_read(c2, world.components())); + assert!(scope.can_write(c2, world.components())); + + assert!(!scope.can_read(c3, world.components())); + assert!(!scope.can_write(c3, world.components())); + } + + #[test] + fn except() { + let mut world = World::new(); + + let c1 = world.register_component::>(); + let c2 = world.register_component::>(); + let c3 = world.register_component::>(); + + let mut access = Access::new_write_all(); + access.add_component_write(c1); + access.add_component_write(c2); + + // SAFETY: The `Access` accurately reflects the excluded components. + let scope = unsafe { Except::<(TestComponent<1>, TestComponent<2>)>::new(&access) }; + + assert!(!scope.can_read(c1, world.components())); + assert!(!scope.can_write(c1, world.components())); + + assert!(!scope.can_read(c2, world.components())); + assert!(!scope.can_write(c2, world.components())); + + assert!(scope.can_read(c3, world.components())); + assert!(scope.can_write(c3, world.components())); + } +} diff --git a/crates/bevy_ecs/src/world/entity_access/all.rs b/crates/bevy_ecs/src/world/entity_access/all.rs new file mode 100644 index 0000000000000..e4ce06c85f4ad --- /dev/null +++ b/crates/bevy_ecs/src/world/entity_access/all.rs @@ -0,0 +1,260 @@ +use crate::{ + query::{has_conflicts, Access, QueryAccessError, ReadOnlyQueryData, ReleaseStateQueryData}, + world::{All, EntityMut, EntityRef, Filtered, FilteredEntityMut, FilteredEntityRef}, +}; + +impl<'w> EntityRef<'w, All> { + /// Consumes `self` and returns a [`FilteredEntityRef`] with read access to + /// all components. + pub fn into_filtered(self) -> FilteredEntityRef<'w, 'static> { + // SAFETY: + // - `Access:new_read_all` equals the read permissions of `self`'s `All` scope. + unsafe { EntityRef::new(self.cell, Filtered(const { &Access::new_read_all() })) } + } + + /// Returns read-only components for the current entity that match the query `Q`. + /// + /// # Panics + /// + /// If the entity does not have the components required by the query `Q`. + pub fn components(&self) -> Q::Item<'w, 'static> { + self.get_components::() + .expect("Query does not match the current entity") + } + + /// Returns read-only components for the current entity that match the query `Q`, + /// or `None` if the entity does not have the components required by the query `Q`. + pub fn get_components( + &self, + ) -> Result, QueryAccessError> { + // SAFETY: + // - We have read-only access to all components of this entity. + // - The query is read-only, and read-only references cannot have conflicts. + unsafe { self.cell.get_components::() } + } +} + +impl<'w> From> for FilteredEntityRef<'w, 'static> { + #[inline] + fn from(entity: EntityRef<'w, All>) -> Self { + entity.into_filtered() + } +} + +impl<'w> From<&EntityRef<'w, All>> for FilteredEntityRef<'w, 'static> { + #[inline] + fn from(entity: &EntityRef<'w, All>) -> Self { + entity.into_filtered() + } +} + +impl<'w> EntityMut<'w, All> { + /// Consumes `self` and returns a [`FilteredEntityMut`] with read and write + /// access to all components. + #[inline] + pub fn into_filtered(self) -> FilteredEntityMut<'w, 'static> { + // SAFETY: + // - `Access::new_write_all` equals the read and write permissions of `entity`'s `All` scope. + // - Consuming `self` ensures there are no other accesses. + unsafe { EntityMut::new(self.cell, Filtered(const { &Access::new_write_all() })) } + } + + /// Returns read-only components for the current entity that match the query `Q`. + /// + /// # Panics + /// + /// If the entity does not have the components required by the query `Q`. + pub fn components(&self) -> Q::Item<'_, 'static> { + self.as_readonly().components::() + } + + /// Returns read-only components for the current entity that match the query `Q`, + /// or `None` if the entity does not have the components required by the query `Q`. + pub fn get_components( + &self, + ) -> Result, QueryAccessError> { + self.as_readonly().get_components::() + } + + /// Returns components for the current entity that match the query `Q`. + /// In the case of conflicting [`QueryData`](crate::query::QueryData), unregistered components, or missing components, + /// this will return a [`QueryAccessError`] + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Component)] + /// struct X(usize); + /// #[derive(Component)] + /// struct Y(usize); + /// + /// # let mut world = World::default(); + /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); + /// // Get mutable access to two components at once + /// // SAFETY: X and Y are different components + /// let (mut x, mut y) = entity.get_components_mut::<(&mut X, &mut Y)>().unwrap(); + /// ``` + /// + /// Note that this does a O(n^2) check that the [`QueryData`](crate::query::QueryData) does not conflict. If performance is a + /// consideration you should use [`Self::get_components_mut_unchecked`] instead. + pub fn get_components_mut( + &mut self, + ) -> Result, QueryAccessError> { + self.reborrow().into_components_mut::() + } + + /// Returns components for the current entity that match the query `Q`, + /// or `None` if the entity does not have the components required by the query `Q`. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Component)] + /// struct X(usize); + /// #[derive(Component)] + /// struct Y(usize); + /// + /// # let mut world = World::default(); + /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); + /// // Get mutable access to two components at once + /// // SAFETY: X and Y are different components + /// let (mut x, mut y) = + /// unsafe { entity.get_components_mut_unchecked::<(&mut X, &mut Y)>() }.unwrap(); + /// *x = X(1); + /// *y = Y(1); + /// // This would trigger undefined behavior, as the `&mut X`s would alias: + /// // entity.get_components_mut_unchecked::<(&mut X, &mut X)>(); + /// ``` + /// + /// # Safety + /// It is the caller's responsibility to ensure that + /// the `QueryData` does not provide aliasing mutable references to the same component. + pub unsafe fn get_components_mut_unchecked( + &mut self, + ) -> Result, QueryAccessError> { + // SAFETY: Caller the `QueryData` does not provide aliasing mutable references to the same component + unsafe { self.reborrow().into_components_mut_unchecked::() } + } + + /// Consumes self and returns components for the current entity that match the query `Q` for the world lifetime `'w`, + /// or `None` if the entity does not have the components required by the query `Q`. + /// + /// The checks for aliasing mutable references may be expensive. + /// If performance is a concern, consider making multiple calls to [`Self::get_mut`]. + /// If that is not possible, consider using [`Self::into_components_mut_unchecked`] to skip the checks. + /// + /// # Panics + /// + /// If the `QueryData` provides aliasing mutable references to the same component. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Component)] + /// struct X(usize); + /// #[derive(Component)] + /// struct Y(usize); + /// + /// # let mut world = World::default(); + /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); + /// // Get mutable access to two components at once + /// let (mut x, mut y) = entity.into_components_mut::<(&mut X, &mut Y)>().unwrap(); + /// *x = X(1); + /// *y = Y(1); + /// ``` + /// + /// ```should_panic + /// # use bevy_ecs::prelude::*; + /// # + /// # #[derive(Component)] + /// # struct X(usize); + /// # + /// # let mut world = World::default(); + /// let mut entity = world.spawn((X(0))).into_mutable(); + /// // This panics, as the `&mut X`s would alias: + /// entity.into_components_mut::<(&mut X, &mut X)>(); + /// ``` + pub fn into_components_mut( + self, + ) -> Result, QueryAccessError> { + has_conflicts::(self.cell.world().components())?; + + // SAFETY: we checked that there were not conflicting components above + unsafe { self.into_components_mut_unchecked::() } + } + + /// Consumes self and returns components for the current entity that match the query `Q` for the world lifetime `'w`, + /// or `None` if the entity does not have the components required by the query `Q`. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Component)] + /// struct X(usize); + /// #[derive(Component)] + /// struct Y(usize); + /// + /// # let mut world = World::default(); + /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); + /// // Get mutable access to two components at once + /// // SAFETY: X and Y are different components + /// let (mut x, mut y) = + /// unsafe { entity.into_components_mut_unchecked::<(&mut X, &mut Y)>() }.unwrap(); + /// *x = X(1); + /// *y = Y(1); + /// // This would trigger undefined behavior, as the `&mut X`s would alias: + /// // entity.into_components_mut_unchecked::<(&mut X, &mut X)>(); + /// ``` + /// + /// # Safety + /// It is the caller's responsibility to ensure that + /// the `QueryData` does not provide aliasing mutable references to the same component. + /// + /// # See also + /// + /// - [`Self::into_components_mut`] for the safe version that performs aliasing checks + pub unsafe fn into_components_mut_unchecked( + self, + ) -> Result, QueryAccessError> { + // SAFETY: + // - We have mutable access to all components of this entity. + // - Caller asserts the `QueryData` does not provide aliasing mutable references to the same component + unsafe { self.cell.get_components::() } + } +} + +impl<'w> From> for FilteredEntityRef<'w, 'static> { + #[inline] + fn from(entity: EntityMut<'w, All>) -> Self { + entity.into_readonly().into_filtered() + } +} + +impl<'w> From<&'w EntityMut<'_, All>> for FilteredEntityRef<'w, 'static> { + #[inline] + fn from(entity: &'w EntityMut<'_, All>) -> Self { + entity.as_readonly().into_filtered() + } +} + +impl<'w> From> for FilteredEntityMut<'w, 'static> { + #[inline] + fn from(entity: EntityMut<'w, All>) -> Self { + entity.into_filtered() + } +} + +impl<'w> From<&'w mut EntityMut<'_, All>> for FilteredEntityMut<'w, 'static> { + #[inline] + fn from(entity: &'w mut EntityMut<'_, All>) -> Self { + entity.reborrow().into_filtered() + } +} diff --git a/crates/bevy_ecs/src/world/entity_access/component_fetch.rs b/crates/bevy_ecs/src/world/entity_access/component_fetch.rs index 18dce1d725708..e35de153e8f1b 100644 --- a/crates/bevy_ecs/src/world/entity_access/component_fetch.rs +++ b/crates/bevy_ecs/src/world/entity_access/component_fetch.rs @@ -1,7 +1,7 @@ use crate::{ change_detection::MutUntyped, component::ComponentId, - world::{error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell}, + world::{error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AccessScope}, }; use alloc::vec::Vec; @@ -44,35 +44,37 @@ pub unsafe trait DynamicComponentFetch { /// /// # Safety /// - /// It is the caller's responsibility to ensure that: - /// - The given [`UnsafeEntityCell`] has read-only access to the fetched components. - /// - No other mutable references to the fetched components exist at the same time. + /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// permissions of `cell` in a way that would violate Rust's aliasing rules, + /// including via copies of `cell` or other indirect means. /// /// # Errors /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. - unsafe fn fetch_ref( + unsafe fn fetch_ref<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError>; + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError>; /// Returns untyped mutable reference(s) to the component(s) with the /// given [`ComponentId`]s, as determined by `self`. /// /// # Safety /// - /// It is the caller's responsibility to ensure that: - /// - The given [`UnsafeEntityCell`] has mutable access to the fetched components. - /// - No other references to the fetched components exist at the same time. + /// Caller must ensure the provided [`AccessScope`] does not exceed the write + /// permissions of `cell` in a way that would violate Rust's aliasing rules, + /// including via copies of `cell` or other indirect means. /// /// # Errors /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. /// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times. - unsafe fn fetch_mut( + unsafe fn fetch_mut<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError>; + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError>; /// Returns untyped mutable reference(s) to the component(s) with the /// given [`ComponentId`]s, as determined by `self`. @@ -80,19 +82,21 @@ pub unsafe trait DynamicComponentFetch { /// /// # Safety /// - /// It is the caller's responsibility to ensure that: - /// - The given [`UnsafeEntityCell`] has mutable access to the fetched components. - /// - No other references to the fetched components exist at the same time. + /// Caller must ensure that: + /// - The provided [`AccessScope`] does not exceed the write permissions of + /// `cell` in a way that would violate Rust's aliasing rules, including + /// via copies of `cell` or other indirect means. /// - The requested components are all mutable. /// /// # Errors /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. /// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times. - unsafe fn fetch_mut_assume_mutable( + unsafe fn fetch_mut_assume_mutable<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError>; + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError>; } // SAFETY: @@ -102,29 +106,32 @@ unsafe impl DynamicComponentFetch for ComponentId { type Ref<'w> = Ptr<'w>; type Mut<'w> = MutUntyped<'w>; - unsafe fn fetch_ref( + unsafe fn fetch_ref<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(self) }.ok_or(EntityComponentError::MissingComponent(self)) + unsafe { cell.get_by_id(scope, self) }.ok_or(EntityComponentError::MissingComponent(self)) } - unsafe fn fetch_mut( + unsafe fn fetch_mut<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(self) } + unsafe { cell.get_mut_by_id(scope, self) } .map_err(|_| EntityComponentError::MissingComponent(self)) } - unsafe fn fetch_mut_assume_mutable( + unsafe fn fetch_mut_assume_mutable<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(self) } + unsafe { cell.get_mut_assume_mutable_by_id(scope, self) } .map_err(|_| EntityComponentError::MissingComponent(self)) } } @@ -136,28 +143,28 @@ unsafe impl DynamicComponentFetch for [ComponentId; N] { type Ref<'w> = [Ptr<'w>; N]; type Mut<'w> = [MutUntyped<'w>; N]; - unsafe fn fetch_ref( + unsafe fn fetch_ref<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { - // SAFETY: Uphelp by caller. - unsafe { <&Self>::fetch_ref(&self, cell) } + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { + <&Self>::fetch_ref(&self, cell, scope) } - unsafe fn fetch_mut( + unsafe fn fetch_mut<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { - // SAFETY: Uphelp by caller. - unsafe { <&Self>::fetch_mut(&self, cell) } + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { + <&Self>::fetch_mut(&self, cell, scope) } - unsafe fn fetch_mut_assume_mutable( + unsafe fn fetch_mut_assume_mutable<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { - // SAFETY: Uphelp by caller. - unsafe { <&Self>::fetch_mut_assume_mutable(&self, cell) } + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { + <&Self>::fetch_mut_assume_mutable(&self, cell, scope) } } @@ -168,15 +175,17 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { type Ref<'w> = [Ptr<'w>; N]; type Mut<'w> = [MutUntyped<'w>; N]; - unsafe fn fetch_ref( + unsafe fn fetch_ref<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { let mut ptrs = [const { MaybeUninit::uninit() }; N]; for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(id) }.ok_or(EntityComponentError::MissingComponent(id))?, + unsafe { cell.get_by_id(scope, id) } + .ok_or(EntityComponentError::MissingComponent(id))?, ); } @@ -186,10 +195,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { Ok(ptrs) } - unsafe fn fetch_mut( + unsafe fn fetch_mut<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -203,7 +213,7 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(id) } + unsafe { cell.get_mut_by_id(scope, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } @@ -214,10 +224,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable( + unsafe fn fetch_mut_assume_mutable<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -231,7 +242,7 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(id) } + unsafe { cell.get_mut_assume_mutable_by_id(scope, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } @@ -250,24 +261,27 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { type Ref<'w> = Vec>; type Mut<'w> = Vec>; - unsafe fn fetch_ref( + unsafe fn fetch_ref<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { let mut ptrs = Vec::with_capacity(self.len()); for &id in self { ptrs.push( // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(id) }.ok_or(EntityComponentError::MissingComponent(id))?, + unsafe { cell.get_by_id(scope, id) } + .ok_or(EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut( + unsafe fn fetch_mut<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -281,17 +295,18 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { for &id in self { ptrs.push( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(id) } + unsafe { cell.get_mut_by_id(scope, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable( + unsafe fn fetch_mut_assume_mutable<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -305,7 +320,7 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { for &id in self { ptrs.push( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(id) } + unsafe { cell.get_mut_assume_mutable_by_id(scope, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } @@ -320,47 +335,51 @@ unsafe impl DynamicComponentFetch for &'_ HashSet { type Ref<'w> = HashMap>; type Mut<'w> = HashMap>; - unsafe fn fetch_ref( + unsafe fn fetch_ref<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( id, // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(id) }.ok_or(EntityComponentError::MissingComponent(id))?, + unsafe { cell.get_by_id(scope, id) } + .ok_or(EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut( + unsafe fn fetch_mut<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( id, // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(id) } + unsafe { cell.get_mut_by_id(scope, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable( + unsafe fn fetch_mut_assume_mutable<'w, 's>( self, - cell: UnsafeEntityCell<'_>, - ) -> Result, EntityComponentError> { + cell: UnsafeEntityCell<'w>, + scope: &'s impl AccessScope, + ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( id, // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(id) } + unsafe { cell.get_mut_assume_mutable_by_id(scope, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } diff --git a/crates/bevy_ecs/src/world/entity_access/entity_mut.rs b/crates/bevy_ecs/src/world/entity_access/entity_mut.rs index 1be7c3dd9962c..59f0994afff41 100644 --- a/crates/bevy_ecs/src/world/entity_access/entity_mut.rs +++ b/crates/bevy_ecs/src/world/entity_access/entity_mut.rs @@ -3,10 +3,9 @@ use crate::{ change_detection::{ComponentTicks, MaybeLocation, Tick}, component::{Component, ComponentId, Mutable}, entity::{ContainsEntity, Entity, EntityEquivalent, EntityLocation}, - query::{has_conflicts, Access, QueryAccessError, ReadOnlyQueryData, ReleaseStateQueryData}, world::{ - error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, DynamicComponentFetch, - EntityRef, FilteredEntityMut, FilteredEntityRef, Mut, Ref, + error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AccessScope, All, + DynamicComponentFetch, EntityRef, Mut, Ref, }, }; @@ -16,12 +15,23 @@ use core::{ hash::{Hash, Hasher}, }; -/// Provides mutable access to a single entity and all of its components. +/// Provides mutable access to a single [`Entity`] and the components allowed by +/// the [`AccessScope`] `S`. Plain `EntityMut`s have an [`AccessScope`] +/// of [`All`], providing access to all components of the entity. /// /// Contrast with [`EntityWorldMut`], which allows adding and removing components, /// despawning the entity, and provides mutable access to the entire world. /// Because of this, `EntityWorldMut` cannot coexist with any other world accesses. /// +/// # [`AccessScope`]s +/// +/// Access scopes describe what you can access on an `EntityMut`. The default +/// scope is [`All`], which provides access to all components of the entity. +/// Other scopes, such as [`Filtered`] and [`Except`], can restrict access to +/// only a subset of components. +/// +/// See the documentation of [`AccessScope`] for more details. +/// /// # Examples /// /// Disjoint mutable access. @@ -39,57 +49,51 @@ use core::{ /// ``` /// /// [`EntityWorldMut`]: crate::world::EntityWorldMut -pub struct EntityMut<'w> { - cell: UnsafeEntityCell<'w>, +/// [`Filtered`]: crate::world::Filtered +/// [`Except`]: crate::world::Except +pub struct EntityMut<'w, S: AccessScope = All> { + pub(super) cell: UnsafeEntityCell<'w>, + scope: S, } -impl<'w> EntityMut<'w> { +impl<'w, S: AccessScope> EntityMut<'w, S> { /// # Safety - /// - `cell` must have permission to mutate every component of the entity. - /// - No accesses to any of the entity's components may exist - /// at the same time as the returned [`EntityMut`]. + /// + /// Caller must ensure `scope` does not exceed the read or write permissions + /// of `cell` in a way that would violate Rust's aliasing rules, including + /// simultaneous access of `cell` via another `EntityMut`, `EntityRef`, or + /// any other means. #[inline] - pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>) -> Self { - Self { cell } + pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>, scope: S) -> Self { + Self { cell, scope } } /// Returns a new instance with a shorter lifetime. /// This is useful if you have `&mut EntityMut`, but you need `EntityMut`. #[inline] - pub fn reborrow(&mut self) -> EntityMut<'_> { - // SAFETY: - // - We have exclusive access to the entire entity and its components. - // - `&mut self` ensures there are no other accesses. - unsafe { Self::new(self.cell) } + pub fn reborrow(&mut self) -> EntityMut<'_, S::Borrow<'_>> { + // SAFETY: We have exclusive access to the entire entity and its components. + unsafe { EntityMut::new(self.cell, self.scope.reborrow()) } } - /// Consumes `self` and returns read-only access to all of the entity's - /// components, with the world `'w` lifetime. + /// Consumes `self` and returns a [`EntityRef`] with the same access + /// permissions. #[inline] - pub fn into_readonly(self) -> EntityRef<'w> { + pub fn into_readonly(self) -> EntityRef<'w, S> { // SAFETY: - // - We have exclusive access to the entire entity and its components. - // - Consuming `self` ensures there are no other accesses. - unsafe { EntityRef::new(self.cell) } + // - Read permissions of `entity.scope` are preserved. + // - Consuming `entity` ensures there are no mutable accesses. + unsafe { EntityRef::new(self.cell, self.scope) } } - /// Gets read-only access to all of the entity's components. + /// Borrows `self` and returns a [`EntityRef`] with the same access + /// permissions. #[inline] - pub fn as_readonly(&self) -> EntityRef<'_> { + pub fn as_readonly(&self) -> EntityRef<'_, S::Borrow<'_>> { // SAFETY: - // - We have exclusive access to the entire entity and its components. - // - `&self` ensures there are no mutable accesses. - unsafe { EntityRef::new(self.cell) } - } - - /// Consumes `self` and returns a [`FilteredEntityMut`] which has mutable - /// access to all of the entity's components, with the world `'w` lifetime. - #[inline] - pub fn into_filtered(self) -> FilteredEntityMut<'w, 'static> { - // SAFETY: - // - We have exclusive access to the entire entity and its components. - // - Consuming `self` ensures there are no other accesses. - unsafe { FilteredEntityMut::new(self.cell, const { &Access::new_write_all() }) } + // - Read permissions of `&entity.scope` are preserved. + // - `&entity` ensures there are no mutable accesses. + unsafe { EntityRef::new(self.cell, self.scope.reborrow()) } } /// Get access to the underlying [`UnsafeEntityCell`]. @@ -98,6 +102,18 @@ impl<'w> EntityMut<'w> { self.cell } + /// Returns a reference to the current [`AccessScope`]. + #[inline] + pub fn scope(&self) -> &S { + &self.scope + } + + /// Consumes self and returns the current [`AccessScope`]. + #[inline] + pub fn into_scope(self) -> S { + self.scope + } + /// Returns the [ID](Entity) of the current entity. #[inline] #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] @@ -161,181 +177,6 @@ impl<'w> EntityMut<'w> { self.as_readonly().get() } - /// Returns read-only components for the current entity that match the query `Q`. - /// - /// # Panics - /// - /// If the entity does not have the components required by the query `Q`. - pub fn components(&self) -> Q::Item<'_, 'static> { - self.as_readonly().components::() - } - - /// Returns read-only components for the current entity that match the query `Q`, - /// or `None` if the entity does not have the components required by the query `Q`. - pub fn get_components( - &self, - ) -> Result, QueryAccessError> { - self.as_readonly().get_components::() - } - - /// Returns components for the current entity that match the query `Q`, - /// or `None` if the entity does not have the components required by the query `Q`. - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// # - /// #[derive(Component)] - /// struct X(usize); - /// #[derive(Component)] - /// struct Y(usize); - /// - /// # let mut world = World::default(); - /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); - /// // Get mutable access to two components at once - /// // SAFETY: X and Y are different components - /// let (mut x, mut y) = - /// unsafe { entity.get_components_mut_unchecked::<(&mut X, &mut Y)>() }.unwrap(); - /// *x = X(1); - /// *y = Y(1); - /// // This would trigger undefined behavior, as the `&mut X`s would alias: - /// // entity.get_components_mut_unchecked::<(&mut X, &mut X)>(); - /// ``` - /// - /// # Safety - /// It is the caller's responsibility to ensure that - /// the `QueryData` does not provide aliasing mutable references to the same component. - /// - /// # See also - /// - /// - [`Self::get_components_mut`] for the safe version that performs aliasing checks - pub unsafe fn get_components_mut_unchecked( - &mut self, - ) -> Result, QueryAccessError> { - // SAFETY: Caller ensures the `QueryData` does not provide aliasing mutable references to the same component - unsafe { self.reborrow().into_components_mut_unchecked::() } - } - - /// Returns components for the current entity that match the query `Q`. - /// In the case of conflicting [`QueryData`](crate::query::QueryData), unregistered components, or missing components, - /// this will return a [`QueryAccessError`] - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// # - /// #[derive(Component)] - /// struct X(usize); - /// #[derive(Component)] - /// struct Y(usize); - /// - /// # let mut world = World::default(); - /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); - /// // Get mutable access to two components at once - /// // SAFETY: X and Y are different components - /// let (mut x, mut y) = entity.get_components_mut::<(&mut X, &mut Y)>().unwrap(); - /// ``` - /// - /// Note that this does a O(n^2) check that the [`QueryData`](crate::query::QueryData) does not conflict. If performance is a - /// consideration you should use [`Self::get_components_mut_unchecked`] instead. - pub fn get_components_mut( - &mut self, - ) -> Result, QueryAccessError> { - self.reborrow().into_components_mut::() - } - - /// Consumes self and returns components for the current entity that match the query `Q` for the world lifetime `'w`, - /// or `None` if the entity does not have the components required by the query `Q`. - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// # - /// #[derive(Component)] - /// struct X(usize); - /// #[derive(Component)] - /// struct Y(usize); - /// - /// # let mut world = World::default(); - /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); - /// // Get mutable access to two components at once - /// // SAFETY: X and Y are different components - /// let (mut x, mut y) = - /// unsafe { entity.into_components_mut_unchecked::<(&mut X, &mut Y)>() }.unwrap(); - /// *x = X(1); - /// *y = Y(1); - /// // This would trigger undefined behavior, as the `&mut X`s would alias: - /// // entity.into_components_mut_unchecked::<(&mut X, &mut X)>(); - /// ``` - /// - /// # Safety - /// It is the caller's responsibility to ensure that - /// the `QueryData` does not provide aliasing mutable references to the same component. - /// - /// # See also - /// - /// - [`Self::into_components_mut`] for the safe version that performs aliasing checks - pub unsafe fn into_components_mut_unchecked( - self, - ) -> Result, QueryAccessError> { - // SAFETY: - // - We have mutable access to all components of this entity. - // - Caller asserts the `QueryData` does not provide aliasing mutable references to the same component - unsafe { self.cell.get_components::() } - } - - /// Consumes self and returns components for the current entity that match the query `Q` for the world lifetime `'w`, - /// or `None` if the entity does not have the components required by the query `Q`. - /// - /// The checks for aliasing mutable references may be expensive. - /// If performance is a concern, consider making multiple calls to [`Self::get_mut`]. - /// If that is not possible, consider using [`Self::into_components_mut_unchecked`] to skip the checks. - /// - /// # Panics - /// - /// If the `QueryData` provides aliasing mutable references to the same component. - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// # - /// #[derive(Component)] - /// struct X(usize); - /// #[derive(Component)] - /// struct Y(usize); - /// - /// # let mut world = World::default(); - /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); - /// // Get mutable access to two components at once - /// let (mut x, mut y) = entity.into_components_mut::<(&mut X, &mut Y)>().unwrap(); - /// *x = X(1); - /// *y = Y(1); - /// ``` - /// - /// ```should_panic - /// # use bevy_ecs::prelude::*; - /// # - /// # #[derive(Component)] - /// # struct X(usize); - /// # - /// # let mut world = World::default(); - /// let mut entity = world.spawn((X(0))).into_mutable(); - /// // This panics, as the `&mut X`s would alias: - /// entity.into_components_mut::<(&mut X, &mut X)>(); - /// ``` - pub fn into_components_mut( - self, - ) -> Result, QueryAccessError> { - has_conflicts::(self.cell.world().components())?; - - // SAFETY: we checked that there were not conflicting components above - unsafe { self.into_components_mut_unchecked::() } - } - /// Consumes `self` and gets access to the component of type `T` with the /// world `'w` lifetime for the current entity. /// @@ -368,8 +209,7 @@ impl<'w> EntityMut<'w> { /// Returns `None` if the entity does not have a component of type `T`. #[inline] pub fn get_mut>(&mut self) -> Option> { - // SAFETY: &mut self implies exclusive access for duration of returned value - unsafe { self.cell.get_mut() } + self.reborrow().into_mut() } /// Gets mutable access to the component of type `T` for the current entity. @@ -380,10 +220,7 @@ impl<'w> EntityMut<'w> { /// - `T` must be a mutable component #[inline] pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { - // SAFETY: - // - &mut self implies exclusive access for duration of returned value - // - Caller ensures `T` is a mutable component - unsafe { self.cell.get_mut_assume_mutable() } + self.reborrow().into_mut_assume_mutable() } /// Consumes self and gets mutable access to the component of type `T` @@ -391,8 +228,11 @@ impl<'w> EntityMut<'w> { /// Returns `None` if the entity does not have a component of type `T`. #[inline] pub fn into_mut>(self) -> Option> { - // SAFETY: consuming `self` implies exclusive access - unsafe { self.cell.get_mut() } + // SAFETY: + // - `self` was constructed with a `scope` that doesn't violate aliasing + // rules for `cell`. + // - Consuming `self` implies exclusive access to components in `scope`. + unsafe { self.cell.get_mut(&self.scope) } } /// Gets mutable access to the component of type `T` for the current entity. @@ -404,9 +244,11 @@ impl<'w> EntityMut<'w> { #[inline] pub unsafe fn into_mut_assume_mutable(self) -> Option> { // SAFETY: - // - Consuming `self` implies exclusive access - // - Caller ensures `T` is a mutable component - unsafe { self.cell.get_mut_assume_mutable() } + // - `self` was constructed with a `scope` that doesn't violate aliasing + // rules for `cell`. + // - Consuming `self` implies exclusive access to components in `scope`. + // - Caller ensures `T` is a mutable component. + unsafe { self.cell.get_mut_assume_mutable(&self.scope) } } /// Retrieves the change ticks for the given component. This can be useful for implementing change @@ -602,10 +444,7 @@ impl<'w> EntityMut<'w> { &mut self, component_ids: F, ) -> Result, EntityComponentError> { - // SAFETY: - // - `&mut self` ensures that no references exist to this entity's components. - // - We have exclusive access to all components of this entity. - unsafe { component_ids.fetch_mut(self.cell) } + self.reborrow().into_mut_by_id(component_ids) } /// Returns untyped mutable reference(s) to component(s) for @@ -635,10 +474,7 @@ impl<'w> EntityMut<'w> { &mut self, component_ids: F, ) -> Result, EntityComponentError> { - // SAFETY: - // - `&mut self` ensures that no references exist to this entity's components. - // - We have exclusive access to all components of this entity. - unsafe { component_ids.fetch_mut_assume_mutable(self.cell) } + self.reborrow().into_mut_assume_mutable_by_id(component_ids) } /// Returns untyped mutable reference to component for @@ -664,9 +500,11 @@ impl<'w> EntityMut<'w> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - The caller must ensure simultaneous access is limited - // - to components that are mutually independent. - unsafe { component_ids.fetch_mut(self.cell) } + // - `self` was constructed with a `scope` that doesn't violate aliasing + // rules for `cell`. + // - Caller ensures exclusive access to components in `scope` for + // duration of returned value. + unsafe { component_ids.fetch_mut(self.cell, &self.scope) } } /// Returns untyped mutable reference to component for @@ -694,9 +532,12 @@ impl<'w> EntityMut<'w> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - The caller must ensure simultaneous access is limited - // - to components that are mutually independent. - unsafe { component_ids.fetch_mut_assume_mutable(self.cell) } + // - `self` was constructed with a `scope` that doesn't violate aliasing + // rules for `cell`. + // - Caller ensures exclusive access to components in `scope` for + // duration of returned value. + // - Caller ensures provided `ComponentId`s refer to mutable components. + unsafe { component_ids.fetch_mut_assume_mutable(self.cell, &self.scope) } } /// Consumes `self` and returns untyped mutable reference(s) @@ -727,9 +568,10 @@ impl<'w> EntityMut<'w> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - consuming `self` ensures that no references exist to this entity's components. - // - We have exclusive access to all components of this entity. - unsafe { component_ids.fetch_mut(self.cell) } + // - `self` was constructed with a `scope` that doesn't violate aliasing + // rules for `cell`. + // - Consuming `self` implies exclusive access to components in `scope`. + unsafe { component_ids.fetch_mut(self.cell, &self.scope) } } /// Consumes `self` and returns untyped mutable reference(s) @@ -761,9 +603,11 @@ impl<'w> EntityMut<'w> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - consuming `self` ensures that no references exist to this entity's components. - // - We have exclusive access to all components of this entity. - unsafe { component_ids.fetch_mut_assume_mutable(self.cell) } + // - `self` was constructed with a `scope` that doesn't violate aliasing + // rules for `cell`. + // - Consuming `self` implies exclusive access to components in `scope`. + // - Caller ensures provided `ComponentId`s refer to mutable components. + unsafe { component_ids.fetch_mut_assume_mutable(self.cell, &self.scope) } } /// Returns the source code location from which this entity has been spawned. @@ -777,64 +621,36 @@ impl<'w> EntityMut<'w> { } } -impl<'w> From> for EntityRef<'w> { +impl<'w, S: AccessScope> From> for EntityRef<'w, S> { #[inline] - fn from(entity: EntityMut<'w>) -> Self { + fn from(entity: EntityMut<'w, S>) -> Self { entity.into_readonly() } } -impl<'a> From<&'a EntityMut<'_>> for EntityRef<'a> { +impl<'w, S: AccessScope> From<&'w EntityMut<'_, S>> for EntityRef<'w, S::Borrow<'w>> { #[inline] - fn from(entity: &'a EntityMut<'_>) -> Self { + fn from(entity: &'w EntityMut<'_, S>) -> Self { entity.as_readonly() } } -impl<'w> From<&'w mut EntityMut<'_>> for EntityMut<'w> { +impl<'w, S: AccessScope> From<&'w mut EntityMut<'_, S>> for EntityMut<'w, S::Borrow<'w>> { #[inline] - fn from(entity: &'w mut EntityMut<'_>) -> Self { + fn from(entity: &'w mut EntityMut<'_, S>) -> Self { entity.reborrow() } } -impl<'a> From> for FilteredEntityRef<'a, 'static> { - #[inline] - fn from(entity: EntityMut<'a>) -> Self { - entity.into_readonly().into_filtered() - } -} - -impl<'a> From<&'a EntityMut<'_>> for FilteredEntityRef<'a, 'static> { - #[inline] - fn from(entity: &'a EntityMut<'_>) -> Self { - entity.as_readonly().into_filtered() - } -} - -impl<'a> From> for FilteredEntityMut<'a, 'static> { - #[inline] - fn from(entity: EntityMut<'a>) -> Self { - entity.into_filtered() - } -} - -impl<'a> From<&'a mut EntityMut<'_>> for FilteredEntityMut<'a, 'static> { - #[inline] - fn from(entity: &'a mut EntityMut<'_>) -> Self { - entity.reborrow().into_filtered() - } -} - -impl PartialEq for EntityMut<'_> { +impl PartialEq for EntityMut<'_, S> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() } } -impl Eq for EntityMut<'_> {} +impl Eq for EntityMut<'_, S> {} -impl PartialOrd for EntityMut<'_> { +impl PartialOrd for EntityMut<'_, S> { /// [`EntityMut`]'s comparison trait implementations match the underlying [`Entity`], /// and cannot discern between different worlds. fn partial_cmp(&self, other: &Self) -> Option { @@ -842,23 +658,23 @@ impl PartialOrd for EntityMut<'_> { } } -impl Ord for EntityMut<'_> { +impl Ord for EntityMut<'_, S> { fn cmp(&self, other: &Self) -> Ordering { self.entity().cmp(&other.entity()) } } -impl Hash for EntityMut<'_> { +impl Hash for EntityMut<'_, S> { fn hash(&self, state: &mut H) { self.entity().hash(state); } } -impl ContainsEntity for EntityMut<'_> { +impl ContainsEntity for EntityMut<'_, S> { fn entity(&self) -> Entity { self.id() } } // SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for EntityMut<'_> {} +unsafe impl EntityEquivalent for EntityMut<'_, S> {} diff --git a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs index fd38ddc48e5e3..8a3f663e1ee89 100644 --- a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs @@ -3,10 +3,9 @@ use crate::{ change_detection::{ComponentTicks, MaybeLocation, Tick}, component::{Component, ComponentId}, entity::{ContainsEntity, Entity, EntityEquivalent, EntityLocation}, - query::{Access, QueryAccessError, ReadOnlyQueryData, ReleaseStateQueryData}, world::{ - error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, DynamicComponentFetch, - FilteredEntityRef, Ref, + error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AccessScope, All, + DynamicComponentFetch, Ref, }, }; @@ -16,7 +15,18 @@ use core::{ hash::{Hash, Hasher}, }; -/// A read-only reference to a particular [`Entity`] and all of its components. +/// Provides read-only access to a single [`Entity`] and the components allowed +/// by the [`AccessScope`] `S`. Plain `EntityRef`s have an [`AccessScope`] of +/// [`All`], providing access to all components of the entity. +/// +/// # [`AccessScope`]s +/// +/// Access scopes describe what you can access on an `EntityRef`. The default +/// scope is [`All`], which provides access to all components of the entity. +/// Other scopes, such as [`Filtered`] and [`Except`], can restrict access to +/// only a subset of components. +/// +/// See the documentation of [`AccessScope`] for more details. /// /// # Examples /// @@ -34,28 +44,36 @@ use core::{ /// } /// # bevy_ecs::system::assert_is_system(disjoint_system); /// ``` +/// +/// [`Filtered`]: crate::world::Filtered +/// [`Except`]: crate::world::Except #[derive(Copy, Clone)] -pub struct EntityRef<'w> { - cell: UnsafeEntityCell<'w>, +pub struct EntityRef<'w, S: AccessScope = All> { + pub(super) cell: UnsafeEntityCell<'w>, + scope: S, } -impl<'w> EntityRef<'w> { +impl<'w, S: AccessScope> EntityRef<'w, S> { /// # Safety - /// - `cell` must have permission to read every component of the entity. - /// - No mutable accesses to any of the entity's components may exist - /// at the same time as the returned [`EntityRef`]. + /// + /// Caller must ensure `scope` does not exceed the read permissions of `cell` + /// in a way that would violate Rust's aliasing rules, including simultaneous + /// access of `cell` via an `EntityMut` or any other means. + #[inline] + pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>, scope: S) -> Self { + Self { cell, scope } + } + + /// Returns a reference to the current [`AccessScope`]. #[inline] - pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>) -> Self { - Self { cell } + pub fn scope(&self) -> &S { + &self.scope } - /// Consumes `self` and returns a [`FilteredEntityRef`] which has read-only - /// access to all of the entity's components, with the world `'w` lifetime. + /// Consumes self and returns the current [`AccessScope`]. #[inline] - pub fn into_filtered(self) -> FilteredEntityRef<'w, 'static> { - // SAFETY: - // - `EntityRef` guarantees exclusive access to all components in the new `FilteredEntityRef`. - unsafe { FilteredEntityRef::new(self.cell, const { &Access::new_read_all() }) } + pub fn into_scope(self) -> S { + self.scope } /// Returns the [ID](Entity) of the current entity. @@ -118,8 +136,9 @@ impl<'w> EntityRef<'w> { /// Returns `None` if the entity does not have a component of type `T`. #[inline] pub fn get(&self) -> Option<&'w T> { - // SAFETY: We have read-only access to all components of this entity. - unsafe { self.cell.get::() } + // SAFETY: `self` was constructed with a `scope` that doesn't violate + // Rust's aliasing rules for `cell`. + unsafe { self.cell.get::(&self.scope) } } /// Gets access to the component of type `T` for the current entity, @@ -128,16 +147,18 @@ impl<'w> EntityRef<'w> { /// Returns `None` if the entity does not have a component of type `T`. #[inline] pub fn get_ref(&self) -> Option> { - // SAFETY: We have read-only access to all components of this entity. - unsafe { self.cell.get_ref::() } + // SAFETY: `self` was constructed with a `scope` that doesn't violate + // Rust's aliasing rules for `cell`. + unsafe { self.cell.get_ref::(&self.scope) } } /// Retrieves the change ticks for the given component. This can be useful for implementing change /// detection in custom runtimes. #[inline] pub fn get_change_ticks(&self) -> Option { - // SAFETY: We have read-only access to all components of this entity. - unsafe { self.cell.get_change_ticks::() } + // SAFETY: `self` was constructed with a `scope` that doesn't violate + // Rust's aliasing rules for `cell`. + unsafe { self.cell.get_change_ticks::(&self.scope) } } /// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change @@ -148,8 +169,9 @@ impl<'w> EntityRef<'w> { /// compile time.** #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - // SAFETY: We have read-only access to all components of this entity. - unsafe { self.cell.get_change_ticks_by_id(component_id) } + // SAFETY: `self` was constructed with a `scope` that doesn't violate + // Rust's aliasing rules for `cell`. + unsafe { self.cell.get_change_ticks_by_id(&self.scope, component_id) } } /// Returns untyped read-only reference(s) to component(s) for the @@ -261,29 +283,9 @@ impl<'w> EntityRef<'w> { &self, component_ids: F, ) -> Result, EntityComponentError> { - // SAFETY: We have read-only access to all components of this entity. - unsafe { component_ids.fetch_ref(self.cell) } - } - - /// Returns read-only components for the current entity that match the query `Q`. - /// - /// # Panics - /// - /// If the entity does not have the components required by the query `Q`. - pub fn components(&self) -> Q::Item<'w, 'static> { - self.get_components::() - .expect("Query does not match the current entity") - } - - /// Returns read-only components for the current entity that match the query `Q`, - /// or `None` if the entity does not have the components required by the query `Q`. - pub fn get_components( - &self, - ) -> Result, QueryAccessError> { - // SAFETY: - // - We have read-only access to all components of this entity. - // - The query is read-only, and read-only references cannot have conflicts. - unsafe { self.cell.get_components::() } + // SAFETY: `self` was constructed with a `scope` that doesn't violate + // Rust's aliasing rules for `cell`. + unsafe { component_ids.fetch_ref(self.cell, &self.scope) } } /// Returns the source code location from which this entity has been spawned. @@ -297,29 +299,15 @@ impl<'w> EntityRef<'w> { } } -impl<'a> From> for FilteredEntityRef<'a, 'static> { - #[inline] - fn from(entity: EntityRef<'a>) -> Self { - entity.into_filtered() - } -} - -impl<'a> From<&EntityRef<'a>> for FilteredEntityRef<'a, 'static> { - #[inline] - fn from(entity: &EntityRef<'a>) -> Self { - entity.into_filtered() - } -} - -impl PartialEq for EntityRef<'_> { +impl PartialEq for EntityRef<'_, S> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() } } -impl Eq for EntityRef<'_> {} +impl Eq for EntityRef<'_, S> {} -impl PartialOrd for EntityRef<'_> { +impl PartialOrd for EntityRef<'_, S> { /// [`EntityRef`]'s comparison trait implementations match the underlying [`Entity`], /// and cannot discern between different worlds. fn partial_cmp(&self, other: &Self) -> Option { @@ -327,23 +315,23 @@ impl PartialOrd for EntityRef<'_> { } } -impl Ord for EntityRef<'_> { +impl Ord for EntityRef<'_, S> { fn cmp(&self, other: &Self) -> Ordering { self.entity().cmp(&other.entity()) } } -impl Hash for EntityRef<'_> { +impl Hash for EntityRef<'_, S> { fn hash(&self, state: &mut H) { self.entity().hash(state); } } -impl ContainsEntity for EntityRef<'_> { +impl ContainsEntity for EntityRef<'_, S> { fn entity(&self) -> Entity { self.id() } } // SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for EntityRef<'_> {} +unsafe impl EntityEquivalent for EntityRef<'_, S> {} diff --git a/crates/bevy_ecs/src/world/entity_access/except.rs b/crates/bevy_ecs/src/world/entity_access/except.rs index 224d4011489d6..7a280dcde63f2 100644 --- a/crates/bevy_ecs/src/world/entity_access/except.rs +++ b/crates/bevy_ecs/src/world/entity_access/except.rs @@ -1,472 +1,78 @@ use crate::{ bundle::Bundle, - change_detection::{ComponentTicks, MaybeLocation, MutUntyped, Tick}, - component::{Component, ComponentId, Components, Mutable}, - entity::{ContainsEntity, Entity, EntityEquivalent}, - query::Access, - world::{ - unsafe_world_cell::UnsafeEntityCell, DynamicComponentFetch, FilteredEntityMut, - FilteredEntityRef, Mut, Ref, - }, + world::{EntityMut, EntityRef, Except, Filtered, FilteredEntityMut, FilteredEntityRef}, }; -use bevy_ptr::Ptr; -use core::{ - any::TypeId, - cmp::Ordering, - hash::{Hash, Hasher}, - marker::PhantomData, -}; - -/// Provides read-only access to a single entity and all its components, save -/// for an explicitly-enumerated set. -pub struct EntityRefExcept<'w, 's, B> -where - B: Bundle, -{ - entity: UnsafeEntityCell<'w>, - access: &'s Access, - phantom: PhantomData, -} - -impl<'w, 's, B> EntityRefExcept<'w, 's, B> -where - B: Bundle, -{ - /// # Safety - /// Other users of `UnsafeEntityCell` must only have mutable access to the components in `B`. - pub(crate) unsafe fn new(entity: UnsafeEntityCell<'w>, access: &'s Access) -> Self { - Self { - entity, - access, - phantom: PhantomData, - } - } +/// Provides read-only access to a single [`Entity`] and all its components, +/// except those mentioned in the [`Bundle`] `B` at compile time. This is an +/// [`EntityRef`] with an [`AccessScope`] of [`Except`]. +/// +/// [`Entity`]: crate::world::Entity +/// [`AccessScope`]: crate::world::AccessScope +pub type EntityRefExcept<'w, 's, B> = EntityRef<'w, Except<'s, B>>; - /// Consumes `self` and returns a [`FilteredEntityRef`], which provides - /// read-only access to all of the entity's components, except for the ones - /// in `B`. - #[inline] +impl<'w, 's, B: Bundle> EntityRefExcept<'w, 's, B> { + /// Consumes `self` and returns a [`FilteredEntityRef`] with the same access + /// permissions. pub fn into_filtered(self) -> FilteredEntityRef<'w, 's> { // SAFETY: - // - The FilteredEntityRef has the same component access as the given EntityRefExcept. - unsafe { FilteredEntityRef::new(self.entity, self.access) } - } - - /// Returns the [ID](Entity) of the current entity. - #[inline] - #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] - pub fn id(&self) -> Entity { - self.entity.id() - } - - /// Gets access to the component of type `C` for the current entity. Returns - /// `None` if the component doesn't have a component of that type or if the - /// type is one of the excluded components. - #[inline] - pub fn get(&self) -> Option<&'w C> - where - C: Component, - { - let components = self.entity.world().components(); - let id = components.valid_component_id::()?; - if bundle_contains_component::(components, id) { - None - } else { - // SAFETY: We have read access for all components that weren't - // covered by the `contains` check above. - unsafe { self.entity.get() } - } - } - - /// Gets access to the component of type `C` for the current entity, - /// including change detection information. Returns `None` if the component - /// doesn't have a component of that type or if the type is one of the - /// excluded components. - #[inline] - pub fn get_ref(&self) -> Option> - where - C: Component, - { - let components = self.entity.world().components(); - let id = components.valid_component_id::()?; - if bundle_contains_component::(components, id) { - None - } else { - // SAFETY: We have read access for all components that weren't - // covered by the `contains` check above. - unsafe { self.entity.get_ref() } - } - } - - /// Returns the source code location from which this entity has been spawned. - pub fn spawned_by(&self) -> MaybeLocation { - self.entity.spawned_by() - } - - /// Returns the [`Tick`] at which this entity has been spawned. - pub fn spawn_tick(&self) -> Tick { - self.entity.spawn_tick() - } - - /// Gets the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`EntityRefExcept::get`], this returns a raw pointer to the component, - /// which is only valid while the [`EntityRefExcept`] is alive. - #[inline] - pub fn get_by_id(&self, component_id: ComponentId) -> Option> { - let components = self.entity.world().components(); - (!bundle_contains_component::(components, component_id)) - .then(|| { - // SAFETY: We have read access for this component - unsafe { self.entity.get_by_id(component_id) } - }) - .flatten() - } - - /// Returns `true` if the current entity has a component of type `T`. - /// Otherwise, this returns `false`. - /// - /// ## Notes - /// - /// If you do not know the concrete type of a component, consider using - /// [`Self::contains_id`] or [`Self::contains_type_id`]. - #[inline] - pub fn contains(&self) -> bool { - self.contains_type_id(TypeId::of::()) - } - - /// Returns `true` if the current entity has a component identified by `component_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using - /// [`Self::contains_type_id`]. - #[inline] - pub fn contains_id(&self, component_id: ComponentId) -> bool { - self.entity.contains_id(component_id) - } - - /// Returns `true` if the current entity has a component with the type identified by `type_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`]. - #[inline] - pub fn contains_type_id(&self, type_id: TypeId) -> bool { - self.entity.contains_type_id(type_id) - } - - /// Retrieves the change ticks for the given component. This can be useful for implementing change - /// detection in custom runtimes. - #[inline] - pub fn get_change_ticks(&self) -> Option { - let component_id = self - .entity - .world() - .components() - .get_valid_id(TypeId::of::())?; - let components = self.entity.world().components(); - (!bundle_contains_component::(components, component_id)) - .then(|| { - // SAFETY: We have read access - unsafe { self.entity.get_change_ticks::() } - }) - .flatten() - } - - /// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change - /// detection in custom runtimes. - /// - /// **You should prefer to use the typed API [`Self::get_change_ticks`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - #[inline] - pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - let components = self.entity.world().components(); - (!bundle_contains_component::(components, component_id)) - .then(|| { - // SAFETY: We have read access - unsafe { self.entity.get_change_ticks_by_id(component_id) } - }) - .flatten() + // - Read permissions of the `Except` scope are preserved in the `Filtered` scope. + unsafe { EntityRef::new(self.cell, Filtered(self.scope().0)) } } } impl<'w, 's, B: Bundle> From> for FilteredEntityRef<'w, 's> { + #[inline] fn from(entity: EntityRefExcept<'w, 's, B>) -> Self { entity.into_filtered() } } impl<'w, 's, B: Bundle> From<&EntityRefExcept<'w, 's, B>> for FilteredEntityRef<'w, 's> { + #[inline] fn from(entity: &EntityRefExcept<'w, 's, B>) -> Self { entity.into_filtered() } } -impl Clone for EntityRefExcept<'_, '_, B> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for EntityRefExcept<'_, '_, B> {} - -impl PartialEq for EntityRefExcept<'_, '_, B> { - fn eq(&self, other: &Self) -> bool { - self.entity() == other.entity() - } -} - -impl Eq for EntityRefExcept<'_, '_, B> {} - -impl PartialOrd for EntityRefExcept<'_, '_, B> { - /// [`EntityRefExcept`]'s comparison trait implementations match the underlying [`Entity`], - /// and cannot discern between different worlds. - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for EntityRefExcept<'_, '_, B> { - fn cmp(&self, other: &Self) -> Ordering { - self.entity().cmp(&other.entity()) - } -} - -impl Hash for EntityRefExcept<'_, '_, B> { - fn hash(&self, state: &mut H) { - self.entity().hash(state); - } -} - -impl ContainsEntity for EntityRefExcept<'_, '_, B> { - fn entity(&self) -> Entity { - self.id() - } -} - -// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for EntityRefExcept<'_, '_, B> {} - -/// Provides mutable access to all components of an entity, with the exception -/// of an explicit set. +/// Provides mutable access to a single [`Entity`] and all its components, +/// except those mentioned in the [`Bundle`] `B` at compile time. This is an +/// [`EntityMut`] with an [`AccessScope`] of [`Except`]. /// /// This is a rather niche type that should only be used if you need access to /// *all* components of an entity, while still allowing you to consult other /// queries that might match entities that this query also matches. If you don't /// need access to all components, prefer a standard query with a /// [`Without`](`crate::query::Without`) filter. -pub struct EntityMutExcept<'w, 's, B> -where - B: Bundle, -{ - entity: UnsafeEntityCell<'w>, - access: &'s Access, - phantom: PhantomData, -} - -impl<'w, 's, B> EntityMutExcept<'w, 's, B> -where - B: Bundle, -{ - /// # Safety - /// Other users of `UnsafeEntityCell` must not have access to any components not in `B`. - pub(crate) unsafe fn new(entity: UnsafeEntityCell<'w>, access: &'s Access) -> Self { - Self { - entity, - access, - phantom: PhantomData, - } - } - - /// Returns the [ID](Entity) of the current entity. - #[inline] - #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] - pub fn id(&self) -> Entity { - self.entity.id() - } - - /// Returns a new instance with a shorter lifetime. - /// - /// This is useful if you have `&mut EntityMutExcept`, but you need - /// `EntityMutExcept`. - #[inline] - pub fn reborrow(&mut self) -> EntityMutExcept<'_, 's, B> { - // SAFETY: - // - We have exclusive access to the entire entity and the applicable components. - // - `&mut self` ensures there are no other accesses to the applicable components. - unsafe { Self::new(self.entity, self.access) } - } - - /// Consumes `self` and returns read-only access to all of the entity's - /// components, except for the ones in `B`. - #[inline] - pub fn into_readonly(self) -> EntityRefExcept<'w, 's, B> { - // SAFETY: - // - We have exclusive access to the entire entity and the applicable components. - // - Consuming `self` ensures there are no other accesses to the applicable components. - unsafe { EntityRefExcept::new(self.entity, self.access) } - } - - /// Gets read-only access to all of the entity's components, except for the - /// ones in `B`. - #[inline] - pub fn as_readonly(&self) -> EntityRefExcept<'_, 's, B> { - // SAFETY: - // - We have exclusive access to the entire entity and the applicable components. - // - `&self` ensures there are no mutable accesses to the applicable components. - unsafe { EntityRefExcept::new(self.entity, self.access) } - } +/// +/// [`Entity`]: crate::world::Entity +/// [`AccessScope`]: crate::world::AccessScope +pub type EntityMutExcept<'w, 's, B> = EntityMut<'w, Except<'s, B>>; - /// Consumes `self` and returns a [`FilteredEntityMut`], which provides - /// mutable access to all of the entity's components, except for the ones in - /// `B`. +impl<'w, 's, B: Bundle> EntityMutExcept<'w, 's, B> { + /// Consumes `self` and returns a [`FilteredEntityMut`] with the same access + /// permissions. #[inline] pub fn into_filtered(self) -> FilteredEntityMut<'w, 's> { // SAFETY: - // - The FilteredEntityMut has the same component access as the given EntityMutExcept. - // - Consuming `self` ensures there are no other accesses to the applicable components. - unsafe { FilteredEntityMut::new(self.entity, self.access) } - } - - /// Get access to the underlying [`UnsafeEntityCell`] - #[inline] - pub fn as_unsafe_entity_cell(&mut self) -> UnsafeEntityCell<'_> { - self.entity - } - - /// Gets access to the component of type `C` for the current entity. Returns - /// `None` if the component doesn't have a component of that type or if the - /// type is one of the excluded components. - #[inline] - pub fn get(&self) -> Option<&'_ C> - where - C: Component, - { - self.as_readonly().get() - } - - /// Gets access to the component of type `C` for the current entity, - /// including change detection information. Returns `None` if the component - /// doesn't have a component of that type or if the type is one of the - /// excluded components. - #[inline] - pub fn get_ref(&self) -> Option> - where - C: Component, - { - self.as_readonly().get_ref() - } - - /// Gets mutable access to the component of type `C` for the current entity. - /// Returns `None` if the component doesn't have a component of that type or - /// if the type is one of the excluded components. - #[inline] - pub fn get_mut(&mut self) -> Option> - where - C: Component, - { - let components = self.entity.world().components(); - let id = components.valid_component_id::()?; - if bundle_contains_component::(components, id) { - None - } else { - // SAFETY: We have write access for all components that weren't - // covered by the `contains` check above. - unsafe { self.entity.get_mut() } - } - } - - /// Returns the source code location from which this entity has been spawned. - pub fn spawned_by(&self) -> MaybeLocation { - self.entity.spawned_by() - } - - /// Returns the [`Tick`] at which this entity has been spawned. - pub fn spawn_tick(&self) -> Tick { - self.entity.spawn_tick() - } - - /// Returns `true` if the current entity has a component of type `T`. - /// Otherwise, this returns `false`. - /// - /// ## Notes - /// - /// If you do not know the concrete type of a component, consider using - /// [`Self::contains_id`] or [`Self::contains_type_id`]. - #[inline] - pub fn contains(&self) -> bool { - self.contains_type_id(TypeId::of::()) - } - - /// Returns `true` if the current entity has a component identified by `component_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using - /// [`Self::contains_type_id`]. - #[inline] - pub fn contains_id(&self, component_id: ComponentId) -> bool { - self.entity.contains_id(component_id) - } - - /// Returns `true` if the current entity has a component with the type identified by `type_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`]. - #[inline] - pub fn contains_type_id(&self, type_id: TypeId) -> bool { - self.entity.contains_type_id(type_id) + // - Read and write permissions of the `Except` scope are preserved in + // the `Filtered` scope. + // - Consuming `self` ensures there are no other accesses. + unsafe { EntityMut::new(self.cell, Filtered(self.scope().0)) } } +} - /// Gets the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`EntityMutExcept::get`], this returns a raw pointer to the component, - /// which is only valid while the [`EntityMutExcept`] is alive. +impl<'w, 's, B: Bundle> From> for FilteredEntityRef<'w, 's> { #[inline] - pub fn get_by_id(&'w self, component_id: ComponentId) -> Option> { - self.as_readonly().get_by_id(component_id) + fn from(entity: EntityMutExcept<'w, 's, B>) -> Self { + entity.into_readonly().into_filtered() } +} - /// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get_mut`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`EntityMutExcept::get_mut`], this returns a raw pointer to the component, - /// which is only valid while the [`EntityMutExcept`] is alive. +impl<'w, 's, B: Bundle> From<&'w EntityMutExcept<'_, 's, B>> for FilteredEntityRef<'w, 's> { #[inline] - pub fn get_mut_by_id( - &mut self, - component_id: ComponentId, - ) -> Option> { - let components = self.entity.world().components(); - (!bundle_contains_component::(components, component_id)) - .then(|| { - // SAFETY: We have write access - unsafe { self.entity.get_mut_by_id(component_id).ok() } - }) - .flatten() + fn from(entity: &'w EntityMutExcept<'_, 's, B>) -> Self { + entity.as_readonly().into_filtered() } } @@ -483,72 +89,3 @@ impl<'w, 's, B: Bundle> From<&'w mut EntityMutExcept<'_, 's, B>> for FilteredEnt entity.reborrow().into_filtered() } } - -impl<'w, 's, B: Bundle> From<&'w mut EntityMutExcept<'_, 's, B>> for EntityMutExcept<'w, 's, B> { - #[inline] - fn from(entity: &'w mut EntityMutExcept<'_, 's, B>) -> Self { - entity.reborrow() - } -} - -impl<'w, 's, B: Bundle> From> for EntityRefExcept<'w, 's, B> { - #[inline] - fn from(entity: EntityMutExcept<'w, 's, B>) -> Self { - entity.into_readonly() - } -} - -impl<'w, 's, B: Bundle> From<&'w EntityMutExcept<'_, 's, B>> for EntityRefExcept<'w, 's, B> { - #[inline] - fn from(entity: &'w EntityMutExcept<'_, 's, B>) -> Self { - entity.as_readonly() - } -} - -impl PartialEq for EntityMutExcept<'_, '_, B> { - fn eq(&self, other: &Self) -> bool { - self.entity() == other.entity() - } -} - -impl Eq for EntityMutExcept<'_, '_, B> {} - -impl PartialOrd for EntityMutExcept<'_, '_, B> { - /// [`EntityMutExcept`]'s comparison trait implementations match the underlying [`Entity`], - /// and cannot discern between different worlds. - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for EntityMutExcept<'_, '_, B> { - fn cmp(&self, other: &Self) -> Ordering { - self.entity().cmp(&other.entity()) - } -} - -impl Hash for EntityMutExcept<'_, '_, B> { - fn hash(&self, state: &mut H) { - self.entity().hash(state); - } -} - -impl ContainsEntity for EntityMutExcept<'_, '_, B> { - fn entity(&self) -> Entity { - self.id() - } -} - -// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for EntityMutExcept<'_, '_, B> {} - -fn bundle_contains_component(components: &Components, query_id: ComponentId) -> bool -where - B: Bundle, -{ - let mut found = false; - for id in B::get_component_ids(components).flatten() { - found = found || id == query_id; - } - found -} diff --git a/crates/bevy_ecs/src/world/entity_access/filtered.rs b/crates/bevy_ecs/src/world/entity_access/filtered.rs index de366205f94ed..01880589da98d 100644 --- a/crates/bevy_ecs/src/world/entity_access/filtered.rs +++ b/crates/bevy_ecs/src/world/entity_access/filtered.rs @@ -1,25 +1,19 @@ use crate::{ - archetype::Archetype, - change_detection::{ComponentTicks, MaybeLocation, MutUntyped, Tick}, - component::{Component, ComponentId, Mutable}, - entity::{ContainsEntity, Entity, EntityEquivalent, EntityLocation}, query::Access, - world::{unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityRef, Mut, Ref}, + world::{ + entity_access::Filtered, unsafe_world_cell::UnsafeEntityCell, All, EntityMut, EntityRef, + }, }; -use bevy_ptr::Ptr; -use core::{ - any::TypeId, - cmp::Ordering, - hash::{Hash, Hasher}, -}; use thiserror::Error; -/// Provides read-only access to a single entity and some of its components defined by the contained [`Access`]. +/// Provides read-only access to a single [`Entity`] and some of its components, +/// as defined by the contained [`Access`] at runtime. This is an [`EntityRef`] +/// with an [`AccessScope`] of [`Filtered`]. /// -/// To define the access when used as a [`QueryData`](crate::query::QueryData), -/// use a [`QueryBuilder`](crate::query::QueryBuilder) or [`QueryParamBuilder`](crate::system::QueryParamBuilder). -/// The [`FilteredEntityRef`] must be the entire [`QueryData`](crate::query::QueryData), and not nested inside a tuple with other data. +/// To define the access when used as a [`QueryData`], use a [`QueryBuilder`] or +/// [`QueryParamBuilder`]. The [`FilteredEntityRef`] must be the entire +/// [`QueryData`], and not nested inside a tuple with other data. /// /// ``` /// # use bevy_ecs::{prelude::*, world::FilteredEntityRef}; @@ -38,248 +32,58 @@ use thiserror::Error; /// let filtered_entity: FilteredEntityRef = query.single(&mut world).unwrap(); /// let component: &A = filtered_entity.get().unwrap(); /// ``` -#[derive(Clone, Copy)] -pub struct FilteredEntityRef<'w, 's> { - entity: UnsafeEntityCell<'w>, - access: &'s Access, -} +/// +/// [`Entity`]: crate::world::Entity +/// [`AccessScope`]: crate::world::AccessScope +/// [`QueryData`]: crate::query::QueryData +/// [`QueryBuilder`]: crate::query::QueryBuilder +/// [`QueryParamBuilder`]: crate::system::QueryParamBuilder +pub type FilteredEntityRef<'w, 's> = EntityRef<'w, Filtered<'s>>; impl<'w, 's> FilteredEntityRef<'w, 's> { - /// # Safety - /// - No `&mut World` can exist from the underlying `UnsafeWorldCell` - /// - If `access` takes read access to a component no mutable reference to that - /// component can exist at the same time as the returned [`FilteredEntityMut`] - /// - If `access` takes any access for a component `entity` must have that component. + /// Returns a reference to the underlying [`Access`]. #[inline] - pub(crate) unsafe fn new(entity: UnsafeEntityCell<'w>, access: &'s Access) -> Self { - Self { entity, access } + #[deprecated(since = "0.19.0", note = "Use `EntityRef::scope()` instead.")] + pub fn access(&self) -> &Access { + self.scope().0 } - /// Consumes self and returns read-only access to the entity and *all* of - /// its components, with the world `'w` lifetime. Returns an error if the - /// access does not include read access to all components. + /// Consumes `self` and attempts to return an [`EntityRef`] with [`All`] + /// access. /// /// # Errors /// - /// - [`TryFromFilteredError::MissingReadAllAccess`] - if the access does not include read access to all components. - #[inline] + /// Returns [`TryFromFilteredError::MissingReadAllAccess`] if the contained + /// [`Access`] does not have read access to all components. pub fn try_into_all(self) -> Result, TryFromFilteredError> { - if !self.access.has_read_all() { + if !self.scope().has_read_all() { Err(TryFromFilteredError::MissingReadAllAccess) } else { - // SAFETY: check above guarantees read-only access to all components of the entity. - Ok(unsafe { EntityRef::new(self.entity) }) + // SAFETY: `Access::has_read_all` check satisfies the `All` scope + // for `EntityRef`. + Ok(unsafe { EntityRef::new(self.cell, All) }) } } - - /// Returns the [ID](Entity) of the current entity. - #[inline] - #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] - pub fn id(&self) -> Entity { - self.entity.id() - } - - /// Gets metadata indicating the location where the current entity is stored. - #[inline] - pub fn location(&self) -> EntityLocation { - self.entity.location() - } - - /// Returns the archetype that the current entity belongs to. - #[inline] - pub fn archetype(&self) -> &Archetype { - self.entity.archetype() - } - - /// Returns a reference to the underlying [`Access`]. - #[inline] - pub fn access(&self) -> &Access { - self.access - } - - /// Returns `true` if the current entity has a component of type `T`. - /// Otherwise, this returns `false`. - /// - /// ## Notes - /// - /// If you do not know the concrete type of a component, consider using - /// [`Self::contains_id`] or [`Self::contains_type_id`]. - #[inline] - pub fn contains(&self) -> bool { - self.contains_type_id(TypeId::of::()) - } - - /// Returns `true` if the current entity has a component identified by `component_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using - /// [`Self::contains_type_id`]. - #[inline] - pub fn contains_id(&self, component_id: ComponentId) -> bool { - self.entity.contains_id(component_id) - } - - /// Returns `true` if the current entity has a component with the type identified by `type_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`]. - #[inline] - pub fn contains_type_id(&self, type_id: TypeId) -> bool { - self.entity.contains_type_id(type_id) - } - - /// Gets access to the component of type `T` for the current entity. - /// Returns `None` if the entity does not have a component of type `T`. - #[inline] - pub fn get(&self) -> Option<&'w T> { - let id = self - .entity - .world() - .components() - .get_valid_id(TypeId::of::())?; - self.access - .has_component_read(id) - // SAFETY: We have read access - .then(|| unsafe { self.entity.get() }) - .flatten() - } - - /// Gets access to the component of type `T` for the current entity, - /// including change detection information as a [`Ref`]. - /// - /// Returns `None` if the entity does not have a component of type `T`. - #[inline] - pub fn get_ref(&self) -> Option> { - let id = self - .entity - .world() - .components() - .get_valid_id(TypeId::of::())?; - self.access - .has_component_read(id) - // SAFETY: We have read access - .then(|| unsafe { self.entity.get_ref() }) - .flatten() - } - - /// Retrieves the change ticks for the given component. This can be useful for implementing change - /// detection in custom runtimes. - #[inline] - pub fn get_change_ticks(&self) -> Option { - let id = self - .entity - .world() - .components() - .get_valid_id(TypeId::of::())?; - self.access - .has_component_read(id) - // SAFETY: We have read access - .then(|| unsafe { self.entity.get_change_ticks::() }) - .flatten() - } - - /// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change - /// detection in custom runtimes. - /// - /// **You should prefer to use the typed API [`Self::get_change_ticks`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - #[inline] - pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - self.access - .has_component_read(component_id) - // SAFETY: We have read access - .then(|| unsafe { self.entity.get_change_ticks_by_id(component_id) }) - .flatten() - } - - /// Gets the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`FilteredEntityRef::get`], this returns a raw pointer to the component, - /// which is only valid while the [`FilteredEntityRef`] is alive. - #[inline] - pub fn get_by_id(&self, component_id: ComponentId) -> Option> { - self.access - .has_component_read(component_id) - // SAFETY: We have read access - .then(|| unsafe { self.entity.get_by_id(component_id) }) - .flatten() - } - - /// Returns the source code location from which this entity has been spawned. - pub fn spawned_by(&self) -> MaybeLocation { - self.entity.spawned_by() - } - - /// Returns the [`Tick`] at which this entity has been spawned. - pub fn spawn_tick(&self) -> Tick { - self.entity.spawn_tick() - } } -impl<'a> TryFrom> for EntityRef<'a> { +impl<'w> TryFrom> for EntityRef<'w> { type Error = TryFromFilteredError; - fn try_from(entity: FilteredEntityRef<'a, '_>) -> Result { + #[inline] + fn try_from(entity: FilteredEntityRef<'w, '_>) -> Result { entity.try_into_all() } } -impl<'a> TryFrom<&FilteredEntityRef<'a, '_>> for EntityRef<'a> { +impl<'w> TryFrom<&FilteredEntityRef<'w, '_>> for EntityRef<'w> { type Error = TryFromFilteredError; - fn try_from(entity: &FilteredEntityRef<'a, '_>) -> Result { + #[inline] + fn try_from(entity: &FilteredEntityRef<'w, '_>) -> Result { entity.try_into_all() } } -impl PartialEq for FilteredEntityRef<'_, '_> { - fn eq(&self, other: &Self) -> bool { - self.entity() == other.entity() - } -} - -impl Eq for FilteredEntityRef<'_, '_> {} - -impl PartialOrd for FilteredEntityRef<'_, '_> { - /// [`FilteredEntityRef`]'s comparison trait implementations match the underlying [`Entity`], - /// and cannot discern between different worlds. - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for FilteredEntityRef<'_, '_> { - fn cmp(&self, other: &Self) -> Ordering { - self.entity().cmp(&other.entity()) - } -} - -impl Hash for FilteredEntityRef<'_, '_> { - fn hash(&self, state: &mut H) { - self.entity().hash(state); - } -} - -impl ContainsEntity for FilteredEntityRef<'_, '_> { - fn entity(&self) -> Entity { - self.id() - } -} - -// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for FilteredEntityRef<'_, '_> {} - /// Variant of [`FilteredEntityMut`] that can be used to create copies of a [`FilteredEntityMut`], as long /// as the user ensures that these won't cause aliasing violations. /// @@ -321,8 +125,8 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { #[inline] pub fn new_readonly(filtered_entity_mut: &FilteredEntityMut<'w, 's>) -> Self { Self { - entity: filtered_entity_mut.entity, - access: filtered_entity_mut.access, + entity: filtered_entity_mut.cell, + access: filtered_entity_mut.scope().0, } } @@ -332,16 +136,17 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { /// - The user must ensure that no aliasing violations occur when using the returned `FilteredEntityMut`. #[inline] pub unsafe fn into_mut(self) -> FilteredEntityMut<'w, 's> { - // SAFETY: Upheld by caller. - unsafe { FilteredEntityMut::new(self.entity, self.access) } + EntityMut::new(self.entity, Filtered(self.access)) } } -/// Provides mutable access to a single entity and some of its components defined by the contained [`Access`]. +/// Provides mutable access to a single [`Entity`] and some of its components, +/// as defined by the contained [`Access`] at runtime. This is an [`EntityMut`] +/// with an [`AccessScope`] of [`Filtered`]. /// -/// To define the access when used as a [`QueryData`](crate::query::QueryData), -/// use a [`QueryBuilder`](crate::query::QueryBuilder) or [`QueryParamBuilder`](crate::system::QueryParamBuilder). -/// The `FilteredEntityMut` must be the entire `QueryData`, and not nested inside a tuple with other data. +/// To define the access when used as a [`QueryData`], use a [`QueryBuilder`] or +/// [`QueryParamBuilder`]. The `FilteredEntityMut` must be the entire +/// `QueryData`, and not nested inside a tuple with other data. /// /// ``` /// # use bevy_ecs::{prelude::*, world::FilteredEntityMut}; @@ -362,444 +167,82 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { /// ``` /// /// Also see [`UnsafeFilteredEntityMut`] for a way to bypass borrow-checker restrictions. -pub struct FilteredEntityMut<'w, 's> { - entity: UnsafeEntityCell<'w>, - access: &'s Access, -} +/// +/// [`Entity`]: crate::world::Entity +/// [`AccessScope`]: crate::world::AccessScope +/// [`QueryData`]: crate::query::QueryData +/// [`QueryBuilder`]: crate::query::QueryBuilder +/// [`QueryParamBuilder`]: crate::system::QueryParamBuilder +pub type FilteredEntityMut<'w, 's> = EntityMut<'w, Filtered<'s>>; impl<'w, 's> FilteredEntityMut<'w, 's> { - /// # Safety - /// - No `&mut World` can exist from the underlying `UnsafeWorldCell` - /// - If `access` takes read access to a component no mutable reference to that - /// component can exist at the same time as the returned [`FilteredEntityMut`] - /// - If `access` takes write access to a component, no reference to that component - /// may exist at the same time as the returned [`FilteredEntityMut`] - /// - If `access` takes any access for a component `entity` must have that component. - #[inline] - pub(crate) unsafe fn new(entity: UnsafeEntityCell<'w>, access: &'s Access) -> Self { - Self { entity, access } - } - - /// Returns a new instance with a shorter lifetime. - /// This is useful if you have `&mut FilteredEntityMut`, but you need `FilteredEntityMut`. - #[inline] - pub fn reborrow(&mut self) -> FilteredEntityMut<'_, 's> { - // SAFETY: - // - We have exclusive access to the entire entity and the applicable components. - // - `&mut self` ensures there are no other accesses to the applicable components. - unsafe { Self::new(self.entity, self.access) } - } - - /// Consumes `self` and returns read-only access to all of the entity's - /// components, with the world `'w` lifetime. - #[inline] - pub fn into_readonly(self) -> FilteredEntityRef<'w, 's> { - // SAFETY: - // - We have exclusive access to the entire entity and the applicable components. - // - Consuming `self` ensures there are no other accesses to the applicable components. - unsafe { FilteredEntityRef::new(self.entity, self.access) } - } - - /// Gets read-only access to all of the entity's components. + /// Returns a reference to the underlying [`Access`]. #[inline] - pub fn as_readonly(&self) -> FilteredEntityRef<'_, 's> { - // SAFETY: - // - We have exclusive access to the entire entity and the applicable components. - // - `&self` ensures there are no mutable accesses to the applicable components. - unsafe { FilteredEntityRef::new(self.entity, self.access) } + #[deprecated(since = "0.19.0", note = "Use `EntityMut::scope()` instead.")] + pub fn access(&self) -> &Access { + self.scope().0 } - /// Consumes self and returns mutable access to the entity and *all* of - /// its components, with the world `'w` lifetime. Returns an error if the - /// access does not include read and write access to all components. + /// Consumes `self` and attempts to return an [`EntityMut`] with [`All`] access. /// /// # Errors /// - /// - [`TryFromFilteredError::MissingReadAllAccess`] - if the access does not include read access to all components. - /// - [`TryFromFilteredError::MissingWriteAllAccess`] - if the access does not include write access to all components. - #[inline] + /// - Returns [`TryFromFilteredError::MissingReadAllAccess`] if the contained + /// [`Access`] does not have read access to all components. + /// - Returns [`TryFromFilteredError::MissingWriteAllAccess`] if the contained + /// [`Access`] does not have write access to all components. pub fn try_into_all(self) -> Result, TryFromFilteredError> { - if !self.access.has_read_all() { + if !self.scope().has_read_all() { Err(TryFromFilteredError::MissingReadAllAccess) - } else if !self.access.has_write_all() { + } else if !self.scope().has_write_all() { Err(TryFromFilteredError::MissingWriteAllAccess) } else { - // SAFETY: check above guarantees exclusive access to all components of the entity. - Ok(unsafe { EntityMut::new(self.entity) }) + // SAFETY: `Access::has_read_all` and `Access::has_write_all` checks + // satisfy the `All` scope for `EntityMut`. + Ok(unsafe { EntityMut::new(self.cell, All) }) } } - - /// Get access to the underlying [`UnsafeEntityCell`]. - #[inline] - pub fn as_unsafe_entity_cell(&mut self) -> UnsafeEntityCell<'_> { - self.entity - } - - /// Returns the [ID](Entity) of the current entity. - #[inline] - #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] - pub fn id(&self) -> Entity { - self.entity.id() - } - - /// Gets metadata indicating the location where the current entity is stored. - #[inline] - pub fn location(&self) -> EntityLocation { - self.entity.location() - } - - /// Returns the archetype that the current entity belongs to. - #[inline] - pub fn archetype(&self) -> &Archetype { - self.entity.archetype() - } - - /// Returns a reference to the underlying [`Access`]. - #[inline] - pub fn access(&self) -> &Access { - self.access - } - - /// Returns `true` if the current entity has a component of type `T`. - /// Otherwise, this returns `false`. - /// - /// ## Notes - /// - /// If you do not know the concrete type of a component, consider using - /// [`Self::contains_id`] or [`Self::contains_type_id`]. - #[inline] - pub fn contains(&self) -> bool { - self.contains_type_id(TypeId::of::()) - } - - /// Returns `true` if the current entity has a component identified by `component_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using - /// [`Self::contains_type_id`]. - #[inline] - pub fn contains_id(&self, component_id: ComponentId) -> bool { - self.entity.contains_id(component_id) - } - - /// Returns `true` if the current entity has a component with the type identified by `type_id`. - /// Otherwise, this returns false. - /// - /// ## Notes - /// - /// - If you know the concrete type of the component, you should prefer [`Self::contains`]. - /// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`]. - #[inline] - pub fn contains_type_id(&self, type_id: TypeId) -> bool { - self.entity.contains_type_id(type_id) - } - - /// Gets access to the component of type `T` for the current entity. - /// Returns `None` if the entity does not have a component of type `T`. - #[inline] - pub fn get(&self) -> Option<&'_ T> { - self.as_readonly().get() - } - - /// Gets access to the component of type `T` for the current entity, - /// including change detection information as a [`Ref`]. - /// - /// Returns `None` if the entity does not have a component of type `T`. - #[inline] - pub fn get_ref(&self) -> Option> { - self.as_readonly().get_ref() - } - - /// Gets mutable access to the component of type `T` for the current entity. - /// Returns `None` if the entity does not have a component of type `T` or if - /// the access does not include write access to `T`. - #[inline] - pub fn get_mut>(&mut self) -> Option> { - // SAFETY: we use a mutable reference to self, so we cannot use the `FilteredEntityMut` to access - // another component - unsafe { self.get_mut_unchecked() } - } - - /// Gets mutable access to the component of type `T` for the current entity. - /// Returns `None` if the entity does not have a component of type `T` or if - /// the access does not include write access to `T`. - /// - /// This only requires `&self`, and so may be used to get mutable access to multiple components. - /// - /// # Example - /// - /// ``` - /// # use bevy_ecs::{prelude::*, world::FilteredEntityMut}; - /// # - /// #[derive(Component)] - /// struct X(usize); - /// #[derive(Component)] - /// struct Y(usize); - /// - /// # let mut world = World::default(); - /// let mut entity = world.spawn((X(0), Y(0))).into_mutable(); - /// - /// // This gives the `FilteredEntityMut` access to `&mut X` and `&mut Y`. - /// let mut query = QueryBuilder::::new(&mut world) - /// .data::<(&mut X, &mut Y)>() - /// .build(); - /// - /// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap(); - /// - /// // Get mutable access to two components at once - /// // SAFETY: We don't take any other references to `X` from this entity - /// let mut x = unsafe { filtered_entity.get_mut_unchecked::() }.unwrap(); - /// // SAFETY: We don't take any other references to `Y` from this entity - /// let mut y = unsafe { filtered_entity.get_mut_unchecked::() }.unwrap(); - /// *x = X(1); - /// *y = Y(1); - /// ``` - /// - /// # Safety - /// - /// No other references to the same component may exist at the same time as the returned reference. - /// - /// # See also - /// - /// - [`get_mut`](Self::get_mut) for the safe version. - #[inline] - pub unsafe fn get_mut_unchecked>( - &self, - ) -> Option> { - let id = self - .entity - .world() - .components() - .get_valid_id(TypeId::of::())?; - self.access - .has_component_write(id) - // SAFETY: We have permission to access the component mutable - // and we promise to not create other references to the same component - .then(|| unsafe { self.entity.get_mut() }) - .flatten() - } - - /// Consumes self and gets mutable access to the component of type `T` - /// with the world `'w` lifetime for the current entity. - /// Returns `None` if the entity does not have a component of type `T`. - #[inline] - pub fn into_mut>(self) -> Option> { - // SAFETY: - // - We have write access - // - The bound `T: Component` ensures the component is mutable - unsafe { self.into_mut_assume_mutable() } - } - - /// Consumes self and gets mutable access to the component of type `T` - /// with the world `'w` lifetime for the current entity. - /// Returns `None` if the entity does not have a component of type `T`. - /// - /// # Safety - /// - /// - `T` must be a mutable component - #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { - let id = self - .entity - .world() - .components() - .get_valid_id(TypeId::of::())?; - self.access - .has_component_write(id) - // SAFETY: - // - We have write access - // - Caller ensures `T` is a mutable component - .then(|| unsafe { self.entity.get_mut_assume_mutable() }) - .flatten() - } - - /// Retrieves the change ticks for the given component. This can be useful for implementing change - /// detection in custom runtimes. - #[inline] - pub fn get_change_ticks(&self) -> Option { - self.as_readonly().get_change_ticks::() - } - - /// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change - /// detection in custom runtimes. - /// - /// **You should prefer to use the typed API [`Self::get_change_ticks`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - #[inline] - pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - self.as_readonly().get_change_ticks_by_id(component_id) - } - - /// Gets the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`FilteredEntityMut::get`], this returns a raw pointer to the component, - /// which is only valid while the [`FilteredEntityMut`] is alive. - #[inline] - pub fn get_by_id(&self, component_id: ComponentId) -> Option> { - self.as_readonly().get_by_id(component_id) - } - - /// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get_mut`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`FilteredEntityMut::get_mut`], this returns a raw pointer to the component, - /// which is only valid while the [`FilteredEntityMut`] is alive. - #[inline] - pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option> { - // SAFETY: we use a mutable reference to self, so we cannot use the `FilteredEntityMut` to access - // another component - unsafe { self.get_mut_by_id_unchecked(component_id) } - } - - /// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity. - /// - /// **You should prefer to use the typed API [`Self::get_mut`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** - /// - /// Unlike [`FilteredEntityMut::get_mut`], this returns a raw pointer to the component, - /// which is only valid while the [`FilteredEntityMut`] is alive. - /// - /// This only requires `&self`, and so may be used to get mutable access to multiple components. - /// - /// # Safety - /// - /// No other references to the same component may exist at the same time as the returned reference. - /// - /// # See also - /// - /// - [`get_mut_by_id`](Self::get_mut_by_id) for the safe version. - #[inline] - pub unsafe fn get_mut_by_id_unchecked( - &self, - component_id: ComponentId, - ) -> Option> { - self.access - .has_component_write(component_id) - // SAFETY: We have permission to access the component mutable - // and we promise to not create other references to the same component - .then(|| unsafe { self.entity.get_mut_by_id(component_id).ok() }) - .flatten() - } - - /// Returns the source code location from which this entity has last been spawned. - pub fn spawned_by(&self) -> MaybeLocation { - self.entity.spawned_by() - } - - /// Returns the [`Tick`] at which this entity has been spawned. - pub fn spawn_tick(&self) -> Tick { - self.entity.spawn_tick() - } } -impl<'a> TryFrom> for EntityRef<'a> { +impl<'w> TryFrom> for EntityRef<'w> { type Error = TryFromFilteredError; #[inline] - fn try_from(entity: FilteredEntityMut<'a, '_>) -> Result { + fn try_from(entity: FilteredEntityMut<'w, '_>) -> Result { entity.into_readonly().try_into_all() } } -impl<'a> TryFrom<&'a FilteredEntityMut<'_, '_>> for EntityRef<'a> { +impl<'w> TryFrom<&'w FilteredEntityMut<'_, '_>> for EntityRef<'w> { type Error = TryFromFilteredError; #[inline] - fn try_from(entity: &'a FilteredEntityMut<'_, '_>) -> Result { + fn try_from(entity: &'w FilteredEntityMut<'_, '_>) -> Result { entity.as_readonly().try_into_all() } } -impl<'a> TryFrom> for EntityMut<'a> { +impl<'w> TryFrom> for EntityMut<'w> { type Error = TryFromFilteredError; - fn try_from(entity: FilteredEntityMut<'a, '_>) -> Result { + #[inline] + fn try_from(entity: FilteredEntityMut<'w, '_>) -> Result { entity.try_into_all() } } -impl<'a> TryFrom<&'a mut FilteredEntityMut<'_, '_>> for EntityMut<'a> { +impl<'w> TryFrom<&'w mut FilteredEntityMut<'_, '_>> for EntityMut<'w> { type Error = TryFromFilteredError; #[inline] - fn try_from(entity: &'a mut FilteredEntityMut<'_, '_>) -> Result { + fn try_from(entity: &'w mut FilteredEntityMut<'_, '_>) -> Result { entity.reborrow().try_into_all() } } -impl<'w, 's> From<&'w mut FilteredEntityMut<'_, 's>> for FilteredEntityMut<'w, 's> { - #[inline] - fn from(entity: &'w mut FilteredEntityMut<'_, 's>) -> Self { - entity.reborrow() - } -} - -impl<'w, 's> From> for FilteredEntityRef<'w, 's> { - #[inline] - fn from(entity: FilteredEntityMut<'w, 's>) -> Self { - entity.into_readonly() - } -} - -impl<'w, 's> From<&'w FilteredEntityMut<'_, 's>> for FilteredEntityRef<'w, 's> { - #[inline] - fn from(entity: &'w FilteredEntityMut<'_, 's>) -> Self { - entity.as_readonly() - } -} - -impl PartialEq for FilteredEntityMut<'_, '_> { - fn eq(&self, other: &Self) -> bool { - self.entity() == other.entity() - } -} - -impl Eq for FilteredEntityMut<'_, '_> {} - -impl PartialOrd for FilteredEntityMut<'_, '_> { - /// [`FilteredEntityMut`]'s comparison trait implementations match the underlying [`Entity`], - /// and cannot discern between different worlds. - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for FilteredEntityMut<'_, '_> { - fn cmp(&self, other: &Self) -> Ordering { - self.entity().cmp(&other.entity()) - } -} - -impl Hash for FilteredEntityMut<'_, '_> { - fn hash(&self, state: &mut H) { - self.entity().hash(state); - } -} - -impl ContainsEntity for FilteredEntityMut<'_, '_> { - fn entity(&self) -> Entity { - self.id() - } -} - -// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for FilteredEntityMut<'_, '_> {} - -/// Error type returned by [`TryFrom`] conversions from filtered entity types -/// ([`FilteredEntityRef`]/[`FilteredEntityMut`]) to full-access entity types -/// ([`EntityRef`]/[`EntityMut`]). +/// Error type returned by [`TryFrom`] conversions from [`EntityRef`]/[`EntityMut`] +/// entity reference types with [`Filtered`] access scopes to ones with [`All`] +/// access scopes. #[derive(Error, Debug)] pub enum TryFromFilteredError { /// Error indicating that the filtered entity does not have read access to diff --git a/crates/bevy_ecs/src/world/entity_access/mod.rs b/crates/bevy_ecs/src/world/entity_access/mod.rs index cc1bd85449761..2e7d96991583c 100644 --- a/crates/bevy_ecs/src/world/entity_access/mod.rs +++ b/crates/bevy_ecs/src/world/entity_access/mod.rs @@ -1,3 +1,5 @@ +mod access_scope; +mod all; mod component_fetch; mod entity_mut; mod entity_ref; @@ -6,6 +8,7 @@ mod except; mod filtered; mod world_mut; +pub use access_scope::*; pub use component_fetch::*; pub use entity_mut::*; pub use entity_ref::*; @@ -752,7 +755,7 @@ mod tests { assert!(e.get::().is_some()); assert!(e.get_ref::().is_some()); assert!(e.get_change_ticks::().is_some()); - assert!(e.get_by_id(a_id).is_some()); + assert!(e.get_by_id(a_id).is_ok()); assert!(e.get_change_ticks_by_id(a_id).is_some()); } @@ -766,7 +769,7 @@ mod tests { assert!(e.get::().is_none()); assert!(e.get_ref::().is_none()); assert!(e.get_change_ticks::().is_none()); - assert!(e.get_by_id(a_id).is_none()); + assert!(e.get_by_id(a_id).is_err()); assert!(e.get_change_ticks_by_id(a_id).is_none()); } @@ -781,8 +784,8 @@ mod tests { assert!(e.get_ref::().is_some()); assert!(e.get_mut::().is_some()); assert!(e.get_change_ticks::().is_some()); - assert!(e.get_by_id(a_id).is_some()); - assert!(e.get_mut_by_id(a_id).is_some()); + assert!(e.get_by_id(a_id).is_ok()); + assert!(e.get_mut_by_id(a_id).is_ok()); assert!(e.get_change_ticks_by_id(a_id).is_some()); } @@ -797,8 +800,8 @@ mod tests { assert!(e.get_ref::().is_none()); assert!(e.get_mut::().is_none()); assert!(e.get_change_ticks::().is_none()); - assert!(e.get_by_id(a_id).is_none()); - assert!(e.get_mut_by_id(a_id).is_none()); + assert!(e.get_by_id(a_id).is_err()); + assert!(e.get_mut_by_id(a_id).is_err()); assert!(e.get_change_ticks_by_id(a_id).is_none()); } diff --git a/crates/bevy_ecs/src/world/entity_access/world_mut.rs b/crates/bevy_ecs/src/world/entity_access/world_mut.rs index 10e5dae4765db..08b6806f9ba0b 100644 --- a/crates/bevy_ecs/src/world/entity_access/world_mut.rs +++ b/crates/bevy_ecs/src/world/entity_access/world_mut.rs @@ -18,7 +18,7 @@ use crate::{ storage::{SparseSets, Table}, system::IntoObserverSystem, world::{ - error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, ComponentEntry, + error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, All, ComponentEntry, DynamicComponentFetch, EntityMut, EntityRef, FilteredEntityMut, FilteredEntityRef, Mut, OccupiedComponentEntry, Ref, VacantComponentEntry, World, }, @@ -131,11 +131,12 @@ impl<'w> EntityWorldMut<'w> { /// Consumes `self` and returns read-only access to all of the entity's /// components, with the world `'w` lifetime. + #[inline] pub fn into_readonly(self) -> EntityRef<'w> { // SAFETY: // - We have exclusive access to the entire world. // - Consuming `self` ensures no mutable accesses are active. - unsafe { EntityRef::new(self.into_unsafe_entity_cell()) } + unsafe { EntityRef::new(self.into_unsafe_entity_cell(), All) } } /// Gets read-only access to all of the entity's components. @@ -144,16 +145,17 @@ impl<'w> EntityWorldMut<'w> { // SAFETY: // - We have exclusive access to the entire world. // - `&self` ensures no mutable accesses are active. - unsafe { EntityRef::new(self.as_unsafe_entity_cell_readonly()) } + unsafe { EntityRef::new(self.as_unsafe_entity_cell_readonly(), All) } } /// Consumes `self` and returns non-structural mutable access to all of the /// entity's components, with the world `'w` lifetime. + #[inline] pub fn into_mutable(self) -> EntityMut<'w> { // SAFETY: // - We have exclusive access to the entire world. // - Consuming `self` ensures there are no other accesses. - unsafe { EntityMut::new(self.into_unsafe_entity_cell()) } + unsafe { EntityMut::new(self.into_unsafe_entity_cell(), All) } } /// Gets non-structural mutable access to all of the entity's components. @@ -162,7 +164,7 @@ impl<'w> EntityWorldMut<'w> { // SAFETY: // - We have exclusive access to the entire world. // - `&mut self` ensures there are no other accesses. - unsafe { EntityMut::new(self.as_unsafe_entity_cell()) } + unsafe { EntityMut::new(self.as_unsafe_entity_cell(), All) } } /// Returns the [ID](Entity) of the current entity. @@ -614,8 +616,7 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[inline] pub fn into_mut>(self) -> Option> { - // SAFETY: consuming `self` implies exclusive access - unsafe { self.into_unsafe_entity_cell().get_mut() } + self.into_mutable().into_mut() } /// Consumes `self` and gets mutable access to the component of type `T` @@ -631,8 +632,7 @@ impl<'w> EntityWorldMut<'w> { /// - `T` must be a mutable component #[inline] pub unsafe fn into_mut_assume_mutable(self) -> Option> { - // SAFETY: consuming `self` implies exclusive access - unsafe { self.into_unsafe_entity_cell().get_mut_assume_mutable() } + self.into_mutable().into_mut_assume_mutable() } /// Gets a reference to the resource of the given type diff --git a/crates/bevy_ecs/src/world/entity_fetch.rs b/crates/bevy_ecs/src/world/entity_fetch.rs index 457f176d0457d..09e4b359d0336 100644 --- a/crates/bevy_ecs/src/world/entity_fetch.rs +++ b/crates/bevy_ecs/src/world/entity_fetch.rs @@ -5,8 +5,8 @@ use crate::{ entity::{Entity, EntityHashMap, EntityHashSet, EntityNotSpawnedError}, error::Result, world::{ - error::EntityMutableFetchError, unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityRef, - EntityWorldMut, + error::EntityMutableFetchError, unsafe_world_cell::UnsafeWorldCell, All, EntityMut, + EntityRef, EntityWorldMut, }, }; @@ -207,7 +207,7 @@ unsafe impl WorldEntityFetch for Entity { ) -> Result, EntityNotSpawnedError> { let ecell = cell.get_entity(self)?; // SAFETY: caller ensures that the world cell has read-only access to the entity. - Ok(unsafe { EntityRef::new(ecell) }) + Ok(unsafe { EntityRef::new(ecell, All) }) } #[inline] @@ -229,7 +229,7 @@ unsafe impl WorldEntityFetch for Entity { ) -> Result, EntityMutableFetchError> { let ecell = cell.get_entity(self)?; // SAFETY: caller ensures that the world cell has mutable access to the entity. - Ok(unsafe { EntityMut::new(ecell) }) + Ok(unsafe { EntityMut::new(ecell, All) }) } } @@ -288,7 +288,7 @@ unsafe impl WorldEntityFetch for &'_ [Entity; N] { for (r, &id) in core::iter::zip(&mut refs, self) { let ecell = cell.get_entity(id)?; // SAFETY: caller ensures that the world cell has read-only access to the entity. - *r = MaybeUninit::new(unsafe { EntityRef::new(ecell) }); + *r = MaybeUninit::new(unsafe { EntityRef::new(ecell, All) }); } // SAFETY: Each item was initialized in the loop above. @@ -315,7 +315,7 @@ unsafe impl WorldEntityFetch for &'_ [Entity; N] { for (r, &id) in core::iter::zip(&mut refs, self) { let ecell = cell.get_entity(id)?; // SAFETY: caller ensures that the world cell has mutable access to the entity. - *r = MaybeUninit::new(unsafe { EntityMut::new(ecell) }); + *r = MaybeUninit::new(unsafe { EntityMut::new(ecell, All) }); } // SAFETY: Each item was initialized in the loop above. @@ -353,7 +353,7 @@ unsafe impl WorldEntityFetch for &'_ [Entity] { for &id in self { let ecell = cell.get_entity(id)?; // SAFETY: caller ensures that the world cell has read-only access to the entity. - refs.push(unsafe { EntityRef::new(ecell) }); + refs.push(unsafe { EntityRef::new(ecell, All) }); } Ok(refs) @@ -377,7 +377,7 @@ unsafe impl WorldEntityFetch for &'_ [Entity] { for &id in self { let ecell = cell.get_entity(id)?; // SAFETY: caller ensures that the world cell has mutable access to the entity. - refs.push(unsafe { EntityMut::new(ecell) }); + refs.push(unsafe { EntityMut::new(ecell, All) }); } Ok(refs) @@ -412,7 +412,7 @@ unsafe impl WorldEntityFetch for &'_ EntityHashSet { for &id in self { let ecell = cell.get_entity(id)?; // SAFETY: caller ensures that the world cell has read-only access to the entity. - refs.insert(id, unsafe { EntityRef::new(ecell) }); + refs.insert(id, unsafe { EntityRef::new(ecell, All) }); } Ok(refs) } @@ -426,7 +426,7 @@ unsafe impl WorldEntityFetch for &'_ EntityHashSet { for &id in self { let ecell = cell.get_entity(id)?; // SAFETY: caller ensures that the world cell has mutable access to the entity. - refs.insert(id, unsafe { EntityMut::new(ecell) }); + refs.insert(id, unsafe { EntityMut::new(ecell, All) }); } Ok(refs) } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 8157b03a61afc..ea2f4303b910b 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -25,9 +25,9 @@ pub use crate::{ pub use bevy_ecs_macros::FromWorld; pub use deferred_world::DeferredWorld; pub use entity_access::{ - ComponentEntry, DynamicComponentFetch, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, - EntityWorldMut, FilteredEntityMut, FilteredEntityRef, OccupiedComponentEntry, - TryFromFilteredError, UnsafeFilteredEntityMut, VacantComponentEntry, + AccessScope, All, ComponentEntry, DynamicComponentFetch, EntityMut, EntityMutExcept, EntityRef, + EntityRefExcept, EntityWorldMut, Except, Filtered, FilteredEntityMut, FilteredEntityRef, + OccupiedComponentEntry, TryFromFilteredError, UnsafeFilteredEntityMut, VacantComponentEntry, }; pub use entity_fetch::{EntityFetcher, WorldEntityFetch}; pub use filtered_resource::*; diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index cae3f5d333db7..2eb1bddfdaae5 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -19,7 +19,7 @@ use crate::{ query::{DebugCheckedUnwrap, QueryAccessError, ReleaseStateQueryData}, resource::Resource, storage::{ComponentSparseSet, Storages, Table}, - world::RawCommandQueue, + world::{AccessScope, RawCommandQueue}, }; use bevy_platform::sync::atomic::Ordering; use bevy_ptr::Ptr; @@ -806,16 +806,22 @@ impl<'w> UnsafeEntityCell<'w> { } /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component - /// - no other mutable references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get(self) -> Option<&'w T> { + pub unsafe fn get(self, scope: &impl AccessScope) -> Option<&'w T> { let component_id = self.world.components().get_valid_id(TypeId::of::())?; + + if !scope.can_read(component_id, self.world.components()) { + return None; + } + // SAFETY: // - `storage_type` is correct (T component_id + T::STORAGE_TYPE) // - `location` is valid - // - proper aliasing is promised by caller + // - proper world access is promised by caller unsafe { get_component( self.world, @@ -830,19 +836,25 @@ impl<'w> UnsafeEntityCell<'w> { } /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component - /// - no other mutable references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get_ref(self) -> Option> { + pub unsafe fn get_ref(self, scope: &impl AccessScope) -> Option> { + let component_id = self.world.components().get_valid_id(TypeId::of::())?; + + if !scope.can_read(component_id, self.world.components()) { + return None; + } + let last_change_tick = self.last_run; let change_tick = self.this_run; - let component_id = self.world.components().get_valid_id(TypeId::of::())?; // SAFETY: // - `storage_type` is correct (T component_id + T::STORAGE_TYPE) // - `location` is valid - // - proper aliasing is promised by caller + // - proper world access is promised by caller unsafe { get_component_and_ticks( self.world, @@ -863,13 +875,21 @@ impl<'w> UnsafeEntityCell<'w> { /// detection in custom runtimes. /// /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component - /// - no other mutable references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get_change_ticks(self) -> Option { + pub unsafe fn get_change_ticks( + self, + scope: &impl AccessScope, + ) -> Option { let component_id = self.world.components().get_valid_id(TypeId::of::())?; + if !scope.can_read(component_id, self.world.components()) { + return None; + } + // SAFETY: // - entity location is valid // - proper world access is promised by caller @@ -892,19 +912,26 @@ impl<'w> UnsafeEntityCell<'w> { /// compile time.** /// /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component - /// - no other mutable references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_change_ticks_by_id( &self, + scope: &impl AccessScope, component_id: ComponentId, ) -> Option { + if !scope.can_read(component_id, self.world.components()) { + return None; + } + let info = self.world.components().get_info(component_id)?; // SAFETY: // - entity location and entity is valid // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T + // - proper world access is promised by caller unsafe { get_ticks( self.world, @@ -917,36 +944,48 @@ impl<'w> UnsafeEntityCell<'w> { } /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component mutably - /// - no other references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the write + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get_mut>(self) -> Option> { + pub unsafe fn get_mut>( + self, + scope: &impl AccessScope, + ) -> Option> { // SAFETY: // - trait bound `T: Component` ensures component is mutable - // - same safety requirements - unsafe { self.get_mut_assume_mutable() } + // - proper world access is promised by caller + unsafe { self.get_mut_assume_mutable(scope) } } /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component mutably - /// - no other references to the component exist at the same time - /// - the component `T` is mutable + /// + /// Caller must ensure that: + /// - The provided [`AccessScope`] does not exceed the write permissions of + /// `self` in a way that would violate Rust's aliasing rules, including + /// via copies of `self` or other indirect means. + /// - The component `T` is mutable. #[inline] - pub unsafe fn get_mut_assume_mutable(self) -> Option> { - // SAFETY: same safety requirements - unsafe { self.get_mut_using_ticks_assume_mutable(self.last_run, self.this_run) } + pub unsafe fn get_mut_assume_mutable( + self, + scope: &impl AccessScope, + ) -> Option> { + // SAFETY: proper world access is promised by caller + unsafe { self.get_mut_using_ticks_assume_mutable(scope, self.last_run, self.this_run) } } /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component mutably - /// - no other references to the component exist at the same time - /// - The component `T` is mutable + /// + /// Caller must ensure that: + /// - The provided [`AccessScope`] does not exceed the write permissions of + /// `self` in a way that would violate Rust's aliasing rules, including + /// via copies of `self` or other indirect means. + /// - The component `T` is mutable. #[inline] pub(crate) unsafe fn get_mut_using_ticks_assume_mutable( &self, + scope: &impl AccessScope, last_change_tick: Tick, change_tick: Tick, ) -> Option> { @@ -954,10 +993,14 @@ impl<'w> UnsafeEntityCell<'w> { let component_id = self.world.components().get_valid_id(TypeId::of::())?; + if !scope.can_write(component_id, self.world.components()) { + return None; + } + // SAFETY: // - `storage_type` is correct // - `location` is valid - // - aliasing rules are ensured by caller + // - proper world access is promised by caller unsafe { get_component_and_ticks( self.world, @@ -979,7 +1022,7 @@ impl<'w> UnsafeEntityCell<'w> { /// /// # Safety /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the queried data immutably + /// - `self` has permission to access the queried data immutably /// - no mutable references to the queried data exist at the same time /// - The `QueryData` does not provide aliasing mutable references to the same component. pub(crate) unsafe fn get_components( @@ -1032,13 +1075,25 @@ impl<'w> UnsafeEntityCell<'w> { /// which is only valid while the `'w` borrow of the lifetime is active. /// /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component - /// - no other mutable references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get_by_id(self, component_id: ComponentId) -> Option> { + pub unsafe fn get_by_id( + self, + scope: &impl AccessScope, + component_id: ComponentId, + ) -> Option> { + if !scope.can_read(component_id, self.world.components()) { + return None; + } + let info = self.world.components().get_info(component_id)?; - // SAFETY: entity_location is valid, component_id is valid as checked by the line above + // SAFETY: + // - entity_location is valid, + // - component_id is valid as checked by the line above + // - proper world access is promised by caller unsafe { get_component( self.world, @@ -1057,16 +1112,22 @@ impl<'w> UnsafeEntityCell<'w> { /// use this in cases where the actual types are not known at compile time.** /// /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component mutably - /// - no other references to the component exist at the same time + /// + /// Caller must ensure the provided [`AccessScope`] does not exceed the write + /// permissions of `self` in a way that would violate Rust's aliasing rules, + /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_mut_by_id( self, + scope: &impl AccessScope, component_id: ComponentId, ) -> Result, GetEntityMutByIdError> { self.world.assert_allows_mutable_access(); + if !scope.can_write(component_id, self.world.components()) { + return Err(GetEntityMutByIdError::ComponentNotFound); + } + let info = self .world .components() @@ -1078,7 +1139,10 @@ impl<'w> UnsafeEntityCell<'w> { return Err(GetEntityMutByIdError::ComponentIsImmutable); } - // SAFETY: entity_location is valid, component_id is valid as checked by the line above + // SAFETY: + // - entity_location is valid + // - component_id is valid as checked by the line above + // - proper world access is promised by caller unsafe { get_component_and_ticks( self.world, @@ -1104,24 +1168,34 @@ impl<'w> UnsafeEntityCell<'w> { /// use this in cases where the actual types are not known at compile time.** /// /// # Safety - /// It is the caller's responsibility to ensure that - /// - the [`UnsafeEntityCell`] has permission to access the component mutably - /// - no other references to the component exist at the same time - /// - the component `T` is mutable + /// + /// Caller must ensure that: + /// - The provided [`AccessScope`] does not exceed the write permissions of + /// `self` in a way that would violate Rust's aliasing rules, including + /// via copies of `self` or other indirect means. + /// - The component `T` is mutable. #[inline] pub unsafe fn get_mut_assume_mutable_by_id( self, + scope: &impl AccessScope, component_id: ComponentId, ) -> Result, GetEntityMutByIdError> { self.world.assert_allows_mutable_access(); + if !scope.can_write(component_id, self.world.components()) { + return Err(GetEntityMutByIdError::ComponentNotFound); + } + let info = self .world .components() .get_info(component_id) .ok_or(GetEntityMutByIdError::InfoNotFound)?; - // SAFETY: entity_location is valid, component_id is valid as checked by the line above + // SAFETY: + // - entity_location is valid + // - component_id is valid as checked by the line above + // - proper world access is promised by caller unsafe { get_component_and_ticks( self.world, @@ -1298,6 +1372,8 @@ impl ContainsEntity for UnsafeEntityCell<'_> { #[cfg(test)] mod tests { + use crate::world::All; + use super::*; #[test] @@ -1333,6 +1409,6 @@ mod tests { let world_cell = world.as_unsafe_world_cell_readonly(); let entity_cell = world_cell.get_entity(entity).unwrap(); // SAFETY: this invalid usage will be caught by a runtime panic. - let _ = unsafe { entity_cell.get_mut::() }; + let _ = unsafe { entity_cell.get_mut::(&All) }; } } diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index e5dd3c40b50a2..a2e359293be1b 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -157,7 +157,7 @@ fn main() { let mut query = builder.build(); query.iter_mut(&mut world).for_each(|filtered_entity| { let terms = filtered_entity - .access() + .scope() .try_iter_component_access() .unwrap() .map(|component_access| { diff --git a/examples/stress_tests/many_components.rs b/examples/stress_tests/many_components.rs index 60ace001f5333..0620d6de19d35 100644 --- a/examples/stress_tests/many_components.rs +++ b/examples/stress_tests/many_components.rs @@ -65,7 +65,7 @@ fn base_system(access_components: In>, mut query: Query(); diff --git a/release-content/migration-guides/scoped_entities.md b/release-content/migration-guides/scoped_entities.md new file mode 100644 index 0000000000000..9537e81c387a8 --- /dev/null +++ b/release-content/migration-guides/scoped_entities.md @@ -0,0 +1,35 @@ +--- +title: "`UnsafeEntityCell` functions now have an `AccessScope` parameter" +pull_requests: [22538] +--- + +The following functions now return a `Result` with a proper error, instead of an +`Option`. Handle accordingly. + +- `FilteredEntityRef::get_by_id` +- `FilteredEntityMut::get_by_id` +- `FilteredEntityMut::get_mut_by_id` +- `FilteredEntityMut::get_mut_by_id_unchecked` +- `EntityRefExcept::get_by_id` +- `EntityMutExcept::get_by_id` +- `EntityMutExcept::get_mut_by_id` + +The following functions now take an `AccessScope` as an additional argument. +You should pass a scope that most closely matches your access patterns, and +ensure it abides by Rust aliasing rules. + +- `UnsafeEntityCell::get` +- `UnsafeEntityCell::get_ref` +- `UnsafeEntityCell::get_change_ticks` +- `UnsafeEntityCell::get_change_ticks_by_id` +- `UnsafeEntityCell::get_mut` +- `UnsafeEntityCell::get_mut_assume_mutable` +- `UnsafeEntityCell::get_by_id` +- `UnsafeEntityCell::get_mut_by_id` +- `UnsafeEntityCell::get_mut_assume_mutable_by_id` + +For example, if your cell can access all components without violating aliasing +rules, use the `All` scope. If your cell can only access a specific set of +components without violating aliasing rules, consider using `Filtered` or `Except` +scopes. If you are able to validate externally that you won't violate aliasing +rules by accesing a particular component, you may use `All`. From 3a024714150bc523fc86a89c2f80f814a8bda43f Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Thu, 15 Jan 2026 22:39:32 -0600 Subject: [PATCH 2/5] typo --- release-content/migration-guides/scoped_entities.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-content/migration-guides/scoped_entities.md b/release-content/migration-guides/scoped_entities.md index 9537e81c387a8..b05e0d33a5eeb 100644 --- a/release-content/migration-guides/scoped_entities.md +++ b/release-content/migration-guides/scoped_entities.md @@ -32,4 +32,4 @@ For example, if your cell can access all components without violating aliasing rules, use the `All` scope. If your cell can only access a specific set of components without violating aliasing rules, consider using `Filtered` or `Except` scopes. If you are able to validate externally that you won't violate aliasing -rules by accesing a particular component, you may use `All`. +rules by accessing a particular component, you may use `All`. From 7796fe6bd80fba9db25a3fe56e6ddf23071e6fc1 Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Sun, 18 Jan 2026 15:59:34 -0600 Subject: [PATCH 3/5] replace `AccessScope` with `AsAccess` --- crates/bevy_ecs/src/entity/clone_entities.rs | 2 +- crates/bevy_ecs/src/observer/runner.rs | 2 +- crates/bevy_ecs/src/reflect/component.rs | 2 +- .../src/world/entity_access/access_scope.rs | 260 ------------------ .../bevy_ecs/src/world/entity_access/all.rs | 4 +- .../src/world/entity_access/as_access.rs | 189 +++++++++++++ .../world/entity_access/component_fetch.rs | 110 ++++---- .../src/world/entity_access/entity_mut.rs | 116 ++++---- .../src/world/entity_access/entity_ref.rs | 72 +++-- .../src/world/entity_access/except.rs | 18 +- .../src/world/entity_access/filtered.rs | 39 +-- .../bevy_ecs/src/world/entity_access/mod.rs | 4 +- crates/bevy_ecs/src/world/mod.rs | 2 +- .../bevy_ecs/src/world/unsafe_world_cell.rs | 64 ++--- examples/ecs/dynamic.rs | 2 +- ...ed_entities.md => entity_deduplication.md} | 4 +- 16 files changed, 396 insertions(+), 494 deletions(-) delete mode 100644 crates/bevy_ecs/src/world/entity_access/access_scope.rs create mode 100644 crates/bevy_ecs/src/world/entity_access/as_access.rs rename release-content/migration-guides/{scoped_entities.md => entity_deduplication.md} (89%) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 94b409ba12090..e5a23e1417ab9 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -635,7 +635,7 @@ impl EntityCloner { // - `component` is from `source_entity`'s archetype let source_component_ptr = unsafe { source_entity - .get_by_id(&All, component) + .get_by_id(All, component) .debug_checked_unwrap() }; diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index be4a5f886379f..37c2219369c10 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -46,7 +46,7 @@ pub(super) unsafe fn observer_system_runner(&All) + .get_mut::(All) .debug_checked_unwrap() }; diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index bb6be597fbfd2..df0f733106365 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -383,7 +383,7 @@ impl FromType for ReflectComponent { // SAFETY: reflect_unchecked_mut is an unsafe function pointer used by // `reflect_unchecked_mut` which must be called with an UnsafeEntityCell with access to the component `C` on the `entity` // guard ensures `C` is a mutable component - let c = unsafe { entity.get_mut_assume_mutable::(&All) }; + let c = unsafe { entity.get_mut_assume_mutable::(All) }; c.map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) }, register_component: |world: &mut World| -> ComponentId { diff --git a/crates/bevy_ecs/src/world/entity_access/access_scope.rs b/crates/bevy_ecs/src/world/entity_access/access_scope.rs deleted file mode 100644 index 3734afee55197..0000000000000 --- a/crates/bevy_ecs/src/world/entity_access/access_scope.rs +++ /dev/null @@ -1,260 +0,0 @@ -use core::{marker::PhantomData, ops::Deref}; - -use crate::{ - bundle::Bundle, - component::{ComponentId, Components}, - query::Access, -}; - -/// Defines the set of [`Component`]s accessible by the entity reference types -/// [`EntityRef`] and [`EntityMut`]. -/// -/// The following scopes are provided: -/// - [`All`]: Provides access to all components. This is the default scope. -/// - [`Filtered`]: Provides access only to the components specified in an -/// [`Access`]. This is used by [`FilteredEntityRef`] and [`FilteredEntityMut`]. -/// - [`Except`]: Provides access to all components except those in a specified -/// [`Bundle`]. This is used by [`EntityRefExcept`] and [`EntityMutExcept`]. -/// -/// # Safety -/// -/// Implementors must ensure that [`AccessScope::reborrow`] does not extend the -/// permissions of the scope with access it did not previously have. -/// -/// [`Component`]: crate::component::Component -/// [`EntityRef`]: crate::world::EntityRef -/// [`EntityMut`]: crate::world::EntityMut -/// [`FilteredEntityRef`]: crate::world::FilteredEntityRef -/// [`FilteredEntityMut`]: crate::world::FilteredEntityMut -/// [`EntityRefExcept`]: crate::world::EntityRefExcept -/// [`EntityMutExcept`]: crate::world::EntityMutExcept -pub unsafe trait AccessScope { - /// The reborrowed version of this scope. This is typically `Self`, but with - /// shorter lifetimes. - type Borrow<'a>: AccessScope - where - Self: 'a; - - /// Reborrows the scope for shorter lifetimes. - fn reborrow(&self) -> Self::Borrow<'_>; - - /// Returns `true` if the scope allows reading the specified component. - fn can_read(&self, id: ComponentId, components: &Components) -> bool; - - /// Returns `true` if the scope allows writing the specified component. - fn can_write(&self, id: ComponentId, components: &Components) -> bool; -} - -/// [`AccessScope`] that provides access to all of an entity's components. This -/// is the default scope of [`EntityRef`] and [`EntityMut`]. -/// -/// [`EntityRef`]: crate::world::EntityRef -/// [`EntityMut`]: crate::world::EntityMut -#[derive(Clone, Copy)] -pub struct All; - -// SAFETY: `reborrow` does not extend access permissions. -unsafe impl AccessScope for All { - type Borrow<'a> - = Self - where - Self: 'a; - - fn reborrow(&self) -> Self::Borrow<'_> { - *self - } - - fn can_read(&self, _id: ComponentId, _components: &Components) -> bool { - true - } - - fn can_write(&self, _id: ComponentId, _components: &Components) -> bool { - true - } -} - -/// [`AccessScope`] that provides access to only the components specified in the -/// provided [`Access`]. [`FilteredEntityRef`] and [`FilteredEntityMut`] use -/// this scope. -/// -/// [`FilteredEntityRef`]: crate::world::FilteredEntityRef -/// [`FilteredEntityMut`]: crate::world::FilteredEntityMut -#[derive(Clone, Copy)] -pub struct Filtered<'s>(pub &'s Access); - -// SAFETY: `reborrow` does not extend access permissions. -unsafe impl AccessScope for Filtered<'_> { - type Borrow<'a> - = Self - where - Self: 'a; - - fn reborrow(&self) -> Self::Borrow<'_> { - *self - } - - fn can_read(&self, id: ComponentId, _components: &Components) -> bool { - self.has_component_read(id) - } - - fn can_write(&self, id: ComponentId, _components: &Components) -> bool { - self.has_component_write(id) - } -} - -impl<'s, B: Bundle> From> for Filtered<'s> { - fn from(value: Except<'s, B>) -> Self { - // SAFETY: We're not discarding the `Except` semantics as long as the - // `Access` matched the `Bundle` `B`. - Filtered(value.0) - } -} - -impl Deref for Filtered<'_> { - type Target = Access; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -/// [`AccessScope`] that provides access to all components except those in the -/// provided [`Bundle`] `B`. [`EntityRefExcept`] and [`EntityMutExcept`] use -/// this scope. -/// -/// [`EntityRefExcept`]: crate::world::EntityRefExcept -/// [`EntityMutExcept`]: crate::world::EntityMutExcept -pub struct Except<'s, B: Bundle>(pub &'s Access, PhantomData); - -impl<'s, B: Bundle> Except<'s, B> { - /// Creates a new `Except` scope from the given [`Access`]. - /// - /// # Safety - /// - /// The provided `Access` must accurately reflect the components in `B`. - pub unsafe fn new(access: &'s Access) -> Self { - Except(access, PhantomData) - } -} - -impl<'s, B: Bundle> Copy for Except<'s, B> {} - -impl<'s, B: Bundle> Clone for Except<'s, B> { - fn clone(&self) -> Self { - *self - } -} - -// SAFETY: `reborrow` does not extend access permissions. -unsafe impl AccessScope for Except<'_, B> { - type Borrow<'a> - = Self - where - Self: 'a; - - fn reborrow(&self) -> Self::Borrow<'_> { - *self - } - - fn can_read(&self, id: ComponentId, components: &Components) -> bool { - B::get_component_ids(components) - .flatten() - .all(|b_id| b_id != id) - } - - fn can_write(&self, id: ComponentId, components: &Components) -> bool { - B::get_component_ids(components) - .flatten() - .all(|b_id| b_id != id) - } -} - -impl Deref for Except<'_, B> { - type Target = Access; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -#[cfg(test)] -mod tests { - use bevy_ecs_macros::Component; - - use crate::{ - query::Access, - world::{AccessScope, Except, World}, - }; - - #[derive(Component)] - pub struct TestComponent; - - #[test] - fn all() { - let mut world = World::new(); - - let c1 = world.register_component::>(); - let c2 = world.register_component::>(); - let c3 = world.register_component::>(); - - let scope = super::All; - - assert!(scope.can_read(c1, world.components())); - assert!(scope.can_write(c1, world.components())); - - assert!(scope.can_read(c2, world.components())); - assert!(scope.can_write(c2, world.components())); - - assert!(scope.can_read(c3, world.components())); - assert!(scope.can_write(c3, world.components())); - } - - #[test] - fn filtered() { - let mut world = World::new(); - - let c1 = world.register_component::>(); - let c2 = world.register_component::>(); - let c3 = world.register_component::>(); - - let mut access = Access::new(); - access.add_component_read(c1); - access.add_component_write(c2); - - let scope = super::Filtered(&access); - - assert!(scope.can_read(c1, world.components())); - assert!(!scope.can_write(c1, world.components())); - - assert!(scope.can_read(c2, world.components())); - assert!(scope.can_write(c2, world.components())); - - assert!(!scope.can_read(c3, world.components())); - assert!(!scope.can_write(c3, world.components())); - } - - #[test] - fn except() { - let mut world = World::new(); - - let c1 = world.register_component::>(); - let c2 = world.register_component::>(); - let c3 = world.register_component::>(); - - let mut access = Access::new_write_all(); - access.add_component_write(c1); - access.add_component_write(c2); - - // SAFETY: The `Access` accurately reflects the excluded components. - let scope = unsafe { Except::<(TestComponent<1>, TestComponent<2>)>::new(&access) }; - - assert!(!scope.can_read(c1, world.components())); - assert!(!scope.can_write(c1, world.components())); - - assert!(!scope.can_read(c2, world.components())); - assert!(!scope.can_write(c2, world.components())); - - assert!(scope.can_read(c3, world.components())); - assert!(scope.can_write(c3, world.components())); - } -} diff --git a/crates/bevy_ecs/src/world/entity_access/all.rs b/crates/bevy_ecs/src/world/entity_access/all.rs index e4ce06c85f4ad..ee4faa9ed25b4 100644 --- a/crates/bevy_ecs/src/world/entity_access/all.rs +++ b/crates/bevy_ecs/src/world/entity_access/all.rs @@ -8,7 +8,7 @@ impl<'w> EntityRef<'w, All> { /// all components. pub fn into_filtered(self) -> FilteredEntityRef<'w, 'static> { // SAFETY: - // - `Access:new_read_all` equals the read permissions of `self`'s `All` scope. + // - `Access:new_read_all` equals the read permissions of `self`'s `All` access. unsafe { EntityRef::new(self.cell, Filtered(const { &Access::new_read_all() })) } } @@ -54,7 +54,7 @@ impl<'w> EntityMut<'w, All> { #[inline] pub fn into_filtered(self) -> FilteredEntityMut<'w, 'static> { // SAFETY: - // - `Access::new_write_all` equals the read and write permissions of `entity`'s `All` scope. + // - `Access::new_write_all` equals the read and write permissions of `entity`'s `All` access. // - Consuming `self` ensures there are no other accesses. unsafe { EntityMut::new(self.cell, Filtered(const { &Access::new_write_all() })) } } diff --git a/crates/bevy_ecs/src/world/entity_access/as_access.rs b/crates/bevy_ecs/src/world/entity_access/as_access.rs new file mode 100644 index 0000000000000..927d458ba9c8d --- /dev/null +++ b/crates/bevy_ecs/src/world/entity_access/as_access.rs @@ -0,0 +1,189 @@ +use core::{marker::PhantomData, ops::Deref}; + +use crate::{bundle::Bundle, query::Access}; + +/// Defines the set of [`Component`]s accessible by the entity reference types +/// [`EntityRef`] and [`EntityMut`]. +/// +/// The following accesses are provided: +/// - [`All`]: Provides access to all components. This is the default access. +/// - [`Filtered`]: Provides access only to the components specified in an +/// [`Access`]. This is used by [`FilteredEntityRef`] and [`FilteredEntityMut`]. +/// - [`Except`]: Provides access to all components except those in a specified +/// [`Bundle`]. This is used by [`EntityRefExcept`] and [`EntityMutExcept`]. +/// +/// [`Component`]: crate::component::Component +/// [`EntityRef`]: crate::world::EntityRef +/// [`EntityMut`]: crate::world::EntityMut +/// [`FilteredEntityRef`]: crate::world::FilteredEntityRef +/// [`FilteredEntityMut`]: crate::world::FilteredEntityMut +/// [`EntityRefExcept`]: crate::world::EntityRefExcept +/// [`EntityMutExcept`]: crate::world::EntityMutExcept +pub trait AsAccess: Copy + Deref {} + +/// [`AsAccess`] that provides access to all of an entity's components. This +/// is the default access of [`EntityRef`] and [`EntityMut`]. +/// +/// [`EntityRef`]: crate::world::EntityRef +/// [`EntityMut`]: crate::world::EntityMut +#[derive(Clone, Copy)] +pub struct All; + +impl AsAccess for All {} + +impl Deref for All { + type Target = Access; + + fn deref(&self) -> &Access { + static WRITE_ALL: Access = Access::new_write_all(); + &WRITE_ALL + } +} + +/// [`AsAccess`] that provides access to only the components specified in the +/// provided [`Access`]. [`FilteredEntityRef`] and [`FilteredEntityMut`] use +/// this access. +/// +/// [`FilteredEntityRef`]: crate::world::FilteredEntityRef +/// [`FilteredEntityMut`]: crate::world::FilteredEntityMut +#[derive(Clone, Copy)] +pub struct Filtered<'s>(pub &'s Access); + +impl AsAccess for Filtered<'_> {} + +impl<'s, B: Bundle> From> for Filtered<'s> { + fn from(value: Except<'s, B>) -> Self { + // SAFETY: We're not discarding the `Except` semantics as long as the + // `Access` matched the `Bundle` `B`. + Filtered(value.0) + } +} + +impl Deref for Filtered<'_> { + type Target = Access; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +/// [`AsAccess`] that provides access to all components except those in the +/// provided [`Bundle`] `B`. [`EntityRefExcept`] and [`EntityMutExcept`] use +/// this access. +/// +/// [`EntityRefExcept`]: crate::world::EntityRefExcept +/// [`EntityMutExcept`]: crate::world::EntityMutExcept +pub struct Except<'s, B: Bundle>(pub &'s Access, PhantomData); + +impl<'s, B: Bundle> Except<'s, B> { + /// Creates a new `Except` access from the given [`Access`]. + /// + /// # Safety + /// + /// The provided `Access` must accurately reflect the components in `B`. + pub unsafe fn new(access: &'s Access) -> Self { + Except(access, PhantomData) + } +} + +impl<'s, B: Bundle> Copy for Except<'s, B> {} + +impl<'s, B: Bundle> Clone for Except<'s, B> { + fn clone(&self) -> Self { + *self + } +} + +impl AsAccess for Except<'_, B> {} + +impl Deref for Except<'_, B> { + type Target = Access; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +#[cfg(test)] +mod tests { + use bevy_ecs_macros::Component; + + use crate::{ + query::Access, + world::{Except, World}, + }; + + #[derive(Component)] + pub struct TestComponent; + + #[test] + fn all() { + let mut world = World::new(); + + let c1 = world.register_component::>(); + let c2 = world.register_component::>(); + let c3 = world.register_component::>(); + + let all = super::All; + + assert!(all.has_component_read(c1)); + assert!(all.has_component_write(c1)); + + assert!(all.has_component_read(c2)); + assert!(all.has_component_write(c2)); + + assert!(all.has_component_read(c3)); + assert!(all.has_component_write(c3)); + } + + #[test] + fn filtered() { + let mut world = World::new(); + + let c1 = world.register_component::>(); + let c2 = world.register_component::>(); + let c3 = world.register_component::>(); + + let mut access = Access::new(); + access.add_component_read(c1); + access.add_component_write(c2); + + let filtered = super::Filtered(&access); + + assert!(filtered.has_component_read(c1)); + assert!(!filtered.has_component_write(c1)); + + assert!(filtered.has_component_read(c2)); + assert!(filtered.has_component_write(c2)); + + assert!(!filtered.has_component_read(c3)); + assert!(!filtered.has_component_write(c3)); + } + + #[test] + fn except() { + let mut world = World::new(); + + let c1 = world.register_component::>(); + let c2 = world.register_component::>(); + let c3 = world.register_component::>(); + + let mut access = Access::new_write_all(); + access.remove_component_read(c1); + access.remove_component_write(c1); + access.remove_component_read(c2); + access.remove_component_write(c2); + + // SAFETY: The `Access` accurately reflects the excluded components. + let except = unsafe { Except::<(TestComponent<1>, TestComponent<2>)>::new(&access) }; + + assert!(!except.has_component_read(c1)); + assert!(!except.has_component_write(c1)); + + assert!(!except.has_component_read(c2)); + assert!(!except.has_component_write(c2)); + + assert!(except.has_component_read(c3)); + assert!(except.has_component_write(c3)); + } +} diff --git a/crates/bevy_ecs/src/world/entity_access/component_fetch.rs b/crates/bevy_ecs/src/world/entity_access/component_fetch.rs index e35de153e8f1b..9dfe06d7cf3da 100644 --- a/crates/bevy_ecs/src/world/entity_access/component_fetch.rs +++ b/crates/bevy_ecs/src/world/entity_access/component_fetch.rs @@ -1,7 +1,7 @@ use crate::{ change_detection::MutUntyped, component::ComponentId, - world::{error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AccessScope}, + world::{error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AsAccess}, }; use alloc::vec::Vec; @@ -44,17 +44,17 @@ pub unsafe trait DynamicComponentFetch { /// /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// Caller must ensure the provided [`AsAccess`] does not exceed the read /// permissions of `cell` in a way that would violate Rust's aliasing rules, /// including via copies of `cell` or other indirect means. /// /// # Errors /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. - unsafe fn fetch_ref<'w, 's>( + unsafe fn fetch_ref<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError>; /// Returns untyped mutable reference(s) to the component(s) with the @@ -62,7 +62,7 @@ pub unsafe trait DynamicComponentFetch { /// /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the write + /// Caller must ensure the provided [`AsAccess`] does not exceed the write /// permissions of `cell` in a way that would violate Rust's aliasing rules, /// including via copies of `cell` or other indirect means. /// @@ -70,10 +70,10 @@ pub unsafe trait DynamicComponentFetch { /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. /// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times. - unsafe fn fetch_mut<'w, 's>( + unsafe fn fetch_mut<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError>; /// Returns untyped mutable reference(s) to the component(s) with the @@ -83,7 +83,7 @@ pub unsafe trait DynamicComponentFetch { /// # Safety /// /// Caller must ensure that: - /// - The provided [`AccessScope`] does not exceed the write permissions of + /// - The provided [`AsAccess`] does not exceed the write permissions of /// `cell` in a way that would violate Rust's aliasing rules, including /// via copies of `cell` or other indirect means. /// - The requested components are all mutable. @@ -92,10 +92,10 @@ pub unsafe trait DynamicComponentFetch { /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. /// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times. - unsafe fn fetch_mut_assume_mutable<'w, 's>( + unsafe fn fetch_mut_assume_mutable<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError>; } @@ -106,32 +106,32 @@ unsafe impl DynamicComponentFetch for ComponentId { type Ref<'w> = Ptr<'w>; type Mut<'w> = MutUntyped<'w>; - unsafe fn fetch_ref<'w, 's>( + unsafe fn fetch_ref<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(scope, self) }.ok_or(EntityComponentError::MissingComponent(self)) + unsafe { cell.get_by_id(access, self) }.ok_or(EntityComponentError::MissingComponent(self)) } - unsafe fn fetch_mut<'w, 's>( + unsafe fn fetch_mut<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(scope, self) } + unsafe { cell.get_mut_by_id(access, self) } .map_err(|_| EntityComponentError::MissingComponent(self)) } - unsafe fn fetch_mut_assume_mutable<'w, 's>( + unsafe fn fetch_mut_assume_mutable<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(scope, self) } + unsafe { cell.get_mut_assume_mutable_by_id(access, self) } .map_err(|_| EntityComponentError::MissingComponent(self)) } } @@ -143,28 +143,28 @@ unsafe impl DynamicComponentFetch for [ComponentId; N] { type Ref<'w> = [Ptr<'w>; N]; type Mut<'w> = [MutUntyped<'w>; N]; - unsafe fn fetch_ref<'w, 's>( + unsafe fn fetch_ref<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { - <&Self>::fetch_ref(&self, cell, scope) + <&Self>::fetch_ref(&self, cell, access) } - unsafe fn fetch_mut<'w, 's>( + unsafe fn fetch_mut<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { - <&Self>::fetch_mut(&self, cell, scope) + <&Self>::fetch_mut(&self, cell, access) } - unsafe fn fetch_mut_assume_mutable<'w, 's>( + unsafe fn fetch_mut_assume_mutable<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { - <&Self>::fetch_mut_assume_mutable(&self, cell, scope) + <&Self>::fetch_mut_assume_mutable(&self, cell, access) } } @@ -175,16 +175,16 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { type Ref<'w> = [Ptr<'w>; N]; type Mut<'w> = [MutUntyped<'w>; N]; - unsafe fn fetch_ref<'w, 's>( + unsafe fn fetch_ref<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { let mut ptrs = [const { MaybeUninit::uninit() }; N]; for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(scope, id) } + unsafe { cell.get_by_id(access, id) } .ok_or(EntityComponentError::MissingComponent(id))?, ); } @@ -195,10 +195,10 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { Ok(ptrs) } - unsafe fn fetch_mut<'w, 's>( + unsafe fn fetch_mut<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { @@ -213,7 +213,7 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(scope, id) } + unsafe { cell.get_mut_by_id(access, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } @@ -224,10 +224,10 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable<'w, 's>( + unsafe fn fetch_mut_assume_mutable<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { @@ -242,7 +242,7 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(scope, id) } + unsafe { cell.get_mut_assume_mutable_by_id(access, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } @@ -261,26 +261,26 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { type Ref<'w> = Vec>; type Mut<'w> = Vec>; - unsafe fn fetch_ref<'w, 's>( + unsafe fn fetch_ref<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { let mut ptrs = Vec::with_capacity(self.len()); for &id in self { ptrs.push( // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(scope, id) } + unsafe { cell.get_by_id(access, id) } .ok_or(EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut<'w, 's>( + unsafe fn fetch_mut<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { @@ -295,17 +295,17 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { for &id in self { ptrs.push( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(scope, id) } + unsafe { cell.get_mut_by_id(access, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable<'w, 's>( + unsafe fn fetch_mut_assume_mutable<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { @@ -320,7 +320,7 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { for &id in self { ptrs.push( // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(scope, id) } + unsafe { cell.get_mut_assume_mutable_by_id(access, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } @@ -335,51 +335,51 @@ unsafe impl DynamicComponentFetch for &'_ HashSet { type Ref<'w> = HashMap>; type Mut<'w> = HashMap>; - unsafe fn fetch_ref<'w, 's>( + unsafe fn fetch_ref<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( id, // SAFETY: caller ensures that the cell has read access to the component. - unsafe { cell.get_by_id(scope, id) } + unsafe { cell.get_by_id(access, id) } .ok_or(EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut<'w, 's>( + unsafe fn fetch_mut<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( id, // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_by_id(scope, id) } + unsafe { cell.get_mut_by_id(access, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable<'w, 's>( + unsafe fn fetch_mut_assume_mutable<'w>( self, cell: UnsafeEntityCell<'w>, - scope: &'s impl AccessScope, + access: impl AsAccess, ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( id, // SAFETY: caller ensures that the cell has mutable access to the component. - unsafe { cell.get_mut_assume_mutable_by_id(scope, id) } + unsafe { cell.get_mut_assume_mutable_by_id(access, id) } .map_err(|_| EntityComponentError::MissingComponent(id))?, ); } diff --git a/crates/bevy_ecs/src/world/entity_access/entity_mut.rs b/crates/bevy_ecs/src/world/entity_access/entity_mut.rs index 59f0994afff41..5f4b8320a5ed9 100644 --- a/crates/bevy_ecs/src/world/entity_access/entity_mut.rs +++ b/crates/bevy_ecs/src/world/entity_access/entity_mut.rs @@ -4,7 +4,7 @@ use crate::{ component::{Component, ComponentId, Mutable}, entity::{ContainsEntity, Entity, EntityEquivalent, EntityLocation}, world::{ - error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AccessScope, All, + error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, All, AsAccess, DynamicComponentFetch, EntityRef, Mut, Ref, }, }; @@ -16,21 +16,21 @@ use core::{ }; /// Provides mutable access to a single [`Entity`] and the components allowed by -/// the [`AccessScope`] `S`. Plain `EntityMut`s have an [`AccessScope`] +/// the [`AsAccess`] `A`. Plain `EntityMut`s have an [`AsAccess`] /// of [`All`], providing access to all components of the entity. /// /// Contrast with [`EntityWorldMut`], which allows adding and removing components, /// despawning the entity, and provides mutable access to the entire world. /// Because of this, `EntityWorldMut` cannot coexist with any other world accesses. /// -/// # [`AccessScope`]s +/// # [`AsAccess`]s /// -/// Access scopes describe what you can access on an `EntityMut`. The default -/// scope is [`All`], which provides access to all components of the entity. -/// Other scopes, such as [`Filtered`] and [`Except`], can restrict access to +/// Access kinds describe what you can access on an `EntityMut`. The default +/// kind is [`All`], which provides access to all components of the entity. +/// Other kinds, such as [`Filtered`] and [`Except`], can restrict access to /// only a subset of components. /// -/// See the documentation of [`AccessScope`] for more details. +/// See the documentation of [`AsAccess`] for more details. /// /// # Examples /// @@ -51,49 +51,49 @@ use core::{ /// [`EntityWorldMut`]: crate::world::EntityWorldMut /// [`Filtered`]: crate::world::Filtered /// [`Except`]: crate::world::Except -pub struct EntityMut<'w, S: AccessScope = All> { +pub struct EntityMut<'w, A: AsAccess = All> { pub(super) cell: UnsafeEntityCell<'w>, - scope: S, + access: A, } -impl<'w, S: AccessScope> EntityMut<'w, S> { +impl<'w, A: AsAccess> EntityMut<'w, A> { /// # Safety /// - /// Caller must ensure `scope` does not exceed the read or write permissions + /// Caller must ensure `access` does not exceed the read or write permissions /// of `cell` in a way that would violate Rust's aliasing rules, including /// simultaneous access of `cell` via another `EntityMut`, `EntityRef`, or /// any other means. #[inline] - pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>, scope: S) -> Self { - Self { cell, scope } + pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>, access: A) -> Self { + Self { cell, access } } /// Returns a new instance with a shorter lifetime. /// This is useful if you have `&mut EntityMut`, but you need `EntityMut`. #[inline] - pub fn reborrow(&mut self) -> EntityMut<'_, S::Borrow<'_>> { + pub fn reborrow(&mut self) -> EntityMut<'_, A> { // SAFETY: We have exclusive access to the entire entity and its components. - unsafe { EntityMut::new(self.cell, self.scope.reborrow()) } + unsafe { EntityMut::new(self.cell, self.access) } } /// Consumes `self` and returns a [`EntityRef`] with the same access /// permissions. #[inline] - pub fn into_readonly(self) -> EntityRef<'w, S> { + pub fn into_readonly(self) -> EntityRef<'w, A> { // SAFETY: - // - Read permissions of `entity.scope` are preserved. + // - Read permissions of `entity.access` are preserved. // - Consuming `entity` ensures there are no mutable accesses. - unsafe { EntityRef::new(self.cell, self.scope) } + unsafe { EntityRef::new(self.cell, self.access) } } /// Borrows `self` and returns a [`EntityRef`] with the same access /// permissions. #[inline] - pub fn as_readonly(&self) -> EntityRef<'_, S::Borrow<'_>> { + pub fn as_readonly(&self) -> EntityRef<'_, A> { // SAFETY: - // - Read permissions of `&entity.scope` are preserved. + // - Read permissions of `&entity.access` are preserved. // - `&entity` ensures there are no mutable accesses. - unsafe { EntityRef::new(self.cell, self.scope.reborrow()) } + unsafe { EntityRef::new(self.cell, self.access) } } /// Get access to the underlying [`UnsafeEntityCell`]. @@ -102,16 +102,10 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { self.cell } - /// Returns a reference to the current [`AccessScope`]. + /// Returns a copy of the current [`AsAccess`]. #[inline] - pub fn scope(&self) -> &S { - &self.scope - } - - /// Consumes self and returns the current [`AccessScope`]. - #[inline] - pub fn into_scope(self) -> S { - self.scope + pub fn access(&self) -> A { + self.access } /// Returns the [ID](Entity) of the current entity. @@ -229,10 +223,10 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { #[inline] pub fn into_mut>(self) -> Option> { // SAFETY: - // - `self` was constructed with a `scope` that doesn't violate aliasing + // - `self` was constructed with an `access` that doesn't violate aliasing // rules for `cell`. - // - Consuming `self` implies exclusive access to components in `scope`. - unsafe { self.cell.get_mut(&self.scope) } + // - Consuming `self` implies exclusive access to components in `access`. + unsafe { self.cell.get_mut(self.access) } } /// Gets mutable access to the component of type `T` for the current entity. @@ -244,11 +238,11 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { #[inline] pub unsafe fn into_mut_assume_mutable(self) -> Option> { // SAFETY: - // - `self` was constructed with a `scope` that doesn't violate aliasing + // - `self` was constructed with an `access` that doesn't violate aliasing // rules for `cell`. - // - Consuming `self` implies exclusive access to components in `scope`. + // - Consuming `self` implies exclusive access to components in `access`. // - Caller ensures `T` is a mutable component. - unsafe { self.cell.get_mut_assume_mutable(&self.scope) } + unsafe { self.cell.get_mut_assume_mutable(self.access) } } /// Retrieves the change ticks for the given component. This can be useful for implementing change @@ -500,11 +494,11 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - `self` was constructed with a `scope` that doesn't violate aliasing + // - `self` was constructed with an `access` that doesn't violate aliasing // rules for `cell`. - // - Caller ensures exclusive access to components in `scope` for + // - Caller ensures exclusive access to components in `access` for // duration of returned value. - unsafe { component_ids.fetch_mut(self.cell, &self.scope) } + unsafe { component_ids.fetch_mut(self.cell, self.access) } } /// Returns untyped mutable reference to component for @@ -532,12 +526,12 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - `self` was constructed with a `scope` that doesn't violate aliasing + // - `self` was constructed with an `access` that doesn't violate aliasing // rules for `cell`. - // - Caller ensures exclusive access to components in `scope` for + // - Caller ensures exclusive access to components in `access` for // duration of returned value. // - Caller ensures provided `ComponentId`s refer to mutable components. - unsafe { component_ids.fetch_mut_assume_mutable(self.cell, &self.scope) } + unsafe { component_ids.fetch_mut_assume_mutable(self.cell, self.access) } } /// Consumes `self` and returns untyped mutable reference(s) @@ -568,10 +562,10 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - `self` was constructed with a `scope` that doesn't violate aliasing + // - `self` was constructed with an `access` that doesn't violate aliasing // rules for `cell`. - // - Consuming `self` implies exclusive access to components in `scope`. - unsafe { component_ids.fetch_mut(self.cell, &self.scope) } + // - Consuming `self` implies exclusive access to components in `access`. + unsafe { component_ids.fetch_mut(self.cell, self.access) } } /// Consumes `self` and returns untyped mutable reference(s) @@ -603,11 +597,11 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { component_ids: F, ) -> Result, EntityComponentError> { // SAFETY: - // - `self` was constructed with a `scope` that doesn't violate aliasing + // - `self` was constructed with an `access` that doesn't violate aliasing // rules for `cell`. - // - Consuming `self` implies exclusive access to components in `scope`. + // - Consuming `self` implies exclusive access to components in `access`. // - Caller ensures provided `ComponentId`s refer to mutable components. - unsafe { component_ids.fetch_mut_assume_mutable(self.cell, &self.scope) } + unsafe { component_ids.fetch_mut_assume_mutable(self.cell, self.access) } } /// Returns the source code location from which this entity has been spawned. @@ -621,36 +615,36 @@ impl<'w, S: AccessScope> EntityMut<'w, S> { } } -impl<'w, S: AccessScope> From> for EntityRef<'w, S> { +impl<'w, A: AsAccess> From> for EntityRef<'w, A> { #[inline] - fn from(entity: EntityMut<'w, S>) -> Self { + fn from(entity: EntityMut<'w, A>) -> Self { entity.into_readonly() } } -impl<'w, S: AccessScope> From<&'w EntityMut<'_, S>> for EntityRef<'w, S::Borrow<'w>> { +impl<'w, A: AsAccess> From<&'w EntityMut<'_, A>> for EntityRef<'w, A> { #[inline] - fn from(entity: &'w EntityMut<'_, S>) -> Self { + fn from(entity: &'w EntityMut<'_, A>) -> Self { entity.as_readonly() } } -impl<'w, S: AccessScope> From<&'w mut EntityMut<'_, S>> for EntityMut<'w, S::Borrow<'w>> { +impl<'w, A: AsAccess> From<&'w mut EntityMut<'_, A>> for EntityMut<'w, A> { #[inline] - fn from(entity: &'w mut EntityMut<'_, S>) -> Self { + fn from(entity: &'w mut EntityMut<'_, A>) -> Self { entity.reborrow() } } -impl PartialEq for EntityMut<'_, S> { +impl PartialEq for EntityMut<'_, A> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() } } -impl Eq for EntityMut<'_, S> {} +impl Eq for EntityMut<'_, A> {} -impl PartialOrd for EntityMut<'_, S> { +impl PartialOrd for EntityMut<'_, A> { /// [`EntityMut`]'s comparison trait implementations match the underlying [`Entity`], /// and cannot discern between different worlds. fn partial_cmp(&self, other: &Self) -> Option { @@ -658,23 +652,23 @@ impl PartialOrd for EntityMut<'_, S> { } } -impl Ord for EntityMut<'_, S> { +impl Ord for EntityMut<'_, A> { fn cmp(&self, other: &Self) -> Ordering { self.entity().cmp(&other.entity()) } } -impl Hash for EntityMut<'_, S> { +impl Hash for EntityMut<'_, A> { fn hash(&self, state: &mut H) { self.entity().hash(state); } } -impl ContainsEntity for EntityMut<'_, S> { +impl ContainsEntity for EntityMut<'_, A> { fn entity(&self) -> Entity { self.id() } } // SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for EntityMut<'_, S> {} +unsafe impl EntityEquivalent for EntityMut<'_, A> {} diff --git a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs index 8a3f663e1ee89..f80a2cba3afc3 100644 --- a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs @@ -4,7 +4,7 @@ use crate::{ component::{Component, ComponentId}, entity::{ContainsEntity, Entity, EntityEquivalent, EntityLocation}, world::{ - error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, AccessScope, All, + error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell, All, AsAccess, DynamicComponentFetch, Ref, }, }; @@ -16,17 +16,17 @@ use core::{ }; /// Provides read-only access to a single [`Entity`] and the components allowed -/// by the [`AccessScope`] `S`. Plain `EntityRef`s have an [`AccessScope`] of +/// by the [`AsAccess`] `A`. Plain `EntityRef`s have an [`AsAccess`] of /// [`All`], providing access to all components of the entity. /// -/// # [`AccessScope`]s +/// # [`AsAccess`]s /// -/// Access scopes describe what you can access on an `EntityRef`. The default -/// scope is [`All`], which provides access to all components of the entity. -/// Other scopes, such as [`Filtered`] and [`Except`], can restrict access to +/// Access kinds describe what you can access on an `EntityRef`. The default +/// kind is [`All`], which provides access to all components of the entity. +/// Other kinds, such as [`Filtered`] and [`Except`], can restrict access to /// only a subset of components. /// -/// See the documentation of [`AccessScope`] for more details. +/// See the documentation of [`AsAccess`] for more details. /// /// # Examples /// @@ -48,32 +48,26 @@ use core::{ /// [`Filtered`]: crate::world::Filtered /// [`Except`]: crate::world::Except #[derive(Copy, Clone)] -pub struct EntityRef<'w, S: AccessScope = All> { +pub struct EntityRef<'w, A: AsAccess = All> { pub(super) cell: UnsafeEntityCell<'w>, - scope: S, + access: A, } -impl<'w, S: AccessScope> EntityRef<'w, S> { +impl<'w, A: AsAccess> EntityRef<'w, A> { /// # Safety /// - /// Caller must ensure `scope` does not exceed the read permissions of `cell` + /// Caller must ensure `access` does not exceed the read permissions of `cell` /// in a way that would violate Rust's aliasing rules, including simultaneous /// access of `cell` via an `EntityMut` or any other means. #[inline] - pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>, scope: S) -> Self { - Self { cell, scope } + pub(crate) unsafe fn new(cell: UnsafeEntityCell<'w>, access: A) -> Self { + Self { cell, access } } - /// Returns a reference to the current [`AccessScope`]. + /// Returns a copy of the current [`AsAccess`]. #[inline] - pub fn scope(&self) -> &S { - &self.scope - } - - /// Consumes self and returns the current [`AccessScope`]. - #[inline] - pub fn into_scope(self) -> S { - self.scope + pub fn access(&self) -> A { + self.access } /// Returns the [ID](Entity) of the current entity. @@ -136,9 +130,9 @@ impl<'w, S: AccessScope> EntityRef<'w, S> { /// Returns `None` if the entity does not have a component of type `T`. #[inline] pub fn get(&self) -> Option<&'w T> { - // SAFETY: `self` was constructed with a `scope` that doesn't violate + // SAFETY: `self` was constructed with an `access` that doesn't violate // Rust's aliasing rules for `cell`. - unsafe { self.cell.get::(&self.scope) } + unsafe { self.cell.get::(self.access) } } /// Gets access to the component of type `T` for the current entity, @@ -147,18 +141,18 @@ impl<'w, S: AccessScope> EntityRef<'w, S> { /// Returns `None` if the entity does not have a component of type `T`. #[inline] pub fn get_ref(&self) -> Option> { - // SAFETY: `self` was constructed with a `scope` that doesn't violate + // SAFETY: `self` was constructed with an `access` that doesn't violate // Rust's aliasing rules for `cell`. - unsafe { self.cell.get_ref::(&self.scope) } + unsafe { self.cell.get_ref::(self.access) } } /// Retrieves the change ticks for the given component. This can be useful for implementing change /// detection in custom runtimes. #[inline] pub fn get_change_ticks(&self) -> Option { - // SAFETY: `self` was constructed with a `scope` that doesn't violate + // SAFETY: `self` was constructed with an `access` that doesn't violate // Rust's aliasing rules for `cell`. - unsafe { self.cell.get_change_ticks::(&self.scope) } + unsafe { self.cell.get_change_ticks::(self.access) } } /// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change @@ -169,9 +163,9 @@ impl<'w, S: AccessScope> EntityRef<'w, S> { /// compile time.** #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - // SAFETY: `self` was constructed with a `scope` that doesn't violate + // SAFETY: `self` was constructed with an `access` that doesn't violate // Rust's aliasing rules for `cell`. - unsafe { self.cell.get_change_ticks_by_id(&self.scope, component_id) } + unsafe { self.cell.get_change_ticks_by_id(self.access, component_id) } } /// Returns untyped read-only reference(s) to component(s) for the @@ -283,9 +277,9 @@ impl<'w, S: AccessScope> EntityRef<'w, S> { &self, component_ids: F, ) -> Result, EntityComponentError> { - // SAFETY: `self` was constructed with a `scope` that doesn't violate + // SAFETY: `self` was constructed with an `access` that doesn't violate // Rust's aliasing rules for `cell`. - unsafe { component_ids.fetch_ref(self.cell, &self.scope) } + unsafe { component_ids.fetch_ref(self.cell, self.access) } } /// Returns the source code location from which this entity has been spawned. @@ -299,15 +293,15 @@ impl<'w, S: AccessScope> EntityRef<'w, S> { } } -impl PartialEq for EntityRef<'_, S> { +impl PartialEq for EntityRef<'_, A> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() } } -impl Eq for EntityRef<'_, S> {} +impl Eq for EntityRef<'_, A> {} -impl PartialOrd for EntityRef<'_, S> { +impl PartialOrd for EntityRef<'_, A> { /// [`EntityRef`]'s comparison trait implementations match the underlying [`Entity`], /// and cannot discern between different worlds. fn partial_cmp(&self, other: &Self) -> Option { @@ -315,23 +309,23 @@ impl PartialOrd for EntityRef<'_, S> { } } -impl Ord for EntityRef<'_, S> { +impl Ord for EntityRef<'_, A> { fn cmp(&self, other: &Self) -> Ordering { self.entity().cmp(&other.entity()) } } -impl Hash for EntityRef<'_, S> { +impl Hash for EntityRef<'_, A> { fn hash(&self, state: &mut H) { self.entity().hash(state); } } -impl ContainsEntity for EntityRef<'_, S> { +impl ContainsEntity for EntityRef<'_, A> { fn entity(&self) -> Entity { self.id() } } // SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity. -unsafe impl EntityEquivalent for EntityRef<'_, S> {} +unsafe impl EntityEquivalent for EntityRef<'_, A> {} diff --git a/crates/bevy_ecs/src/world/entity_access/except.rs b/crates/bevy_ecs/src/world/entity_access/except.rs index 7a280dcde63f2..be819b36e4bb4 100644 --- a/crates/bevy_ecs/src/world/entity_access/except.rs +++ b/crates/bevy_ecs/src/world/entity_access/except.rs @@ -5,10 +5,10 @@ use crate::{ /// Provides read-only access to a single [`Entity`] and all its components, /// except those mentioned in the [`Bundle`] `B` at compile time. This is an -/// [`EntityRef`] with an [`AccessScope`] of [`Except`]. +/// [`EntityRef`] with an [`AsAccess`] of [`Except`]. /// /// [`Entity`]: crate::world::Entity -/// [`AccessScope`]: crate::world::AccessScope +/// [`AsAccess`]: crate::world::AsAccess pub type EntityRefExcept<'w, 's, B> = EntityRef<'w, Except<'s, B>>; impl<'w, 's, B: Bundle> EntityRefExcept<'w, 's, B> { @@ -16,8 +16,8 @@ impl<'w, 's, B: Bundle> EntityRefExcept<'w, 's, B> { /// permissions. pub fn into_filtered(self) -> FilteredEntityRef<'w, 's> { // SAFETY: - // - Read permissions of the `Except` scope are preserved in the `Filtered` scope. - unsafe { EntityRef::new(self.cell, Filtered(self.scope().0)) } + // - Read permissions of the `Except` access are preserved in the `Filtered` access. + unsafe { EntityRef::new(self.cell, Filtered(self.access().0)) } } } @@ -37,7 +37,7 @@ impl<'w, 's, B: Bundle> From<&EntityRefExcept<'w, 's, B>> for FilteredEntityRef< /// Provides mutable access to a single [`Entity`] and all its components, /// except those mentioned in the [`Bundle`] `B` at compile time. This is an -/// [`EntityMut`] with an [`AccessScope`] of [`Except`]. +/// [`EntityMut`] with an [`AsAccess`] of [`Except`]. /// /// This is a rather niche type that should only be used if you need access to /// *all* components of an entity, while still allowing you to consult other @@ -46,7 +46,7 @@ impl<'w, 's, B: Bundle> From<&EntityRefExcept<'w, 's, B>> for FilteredEntityRef< /// [`Without`](`crate::query::Without`) filter. /// /// [`Entity`]: crate::world::Entity -/// [`AccessScope`]: crate::world::AccessScope +/// [`AsAccess`]: crate::world::AsAccess pub type EntityMutExcept<'w, 's, B> = EntityMut<'w, Except<'s, B>>; impl<'w, 's, B: Bundle> EntityMutExcept<'w, 's, B> { @@ -55,10 +55,10 @@ impl<'w, 's, B: Bundle> EntityMutExcept<'w, 's, B> { #[inline] pub fn into_filtered(self) -> FilteredEntityMut<'w, 's> { // SAFETY: - // - Read and write permissions of the `Except` scope are preserved in - // the `Filtered` scope. + // - Read and write permissions of the `Except` access are preserved in + // the `Filtered` access. // - Consuming `self` ensures there are no other accesses. - unsafe { EntityMut::new(self.cell, Filtered(self.scope().0)) } + unsafe { EntityMut::new(self.cell, Filtered(self.access().0)) } } } diff --git a/crates/bevy_ecs/src/world/entity_access/filtered.rs b/crates/bevy_ecs/src/world/entity_access/filtered.rs index 01880589da98d..7556d418f9250 100644 --- a/crates/bevy_ecs/src/world/entity_access/filtered.rs +++ b/crates/bevy_ecs/src/world/entity_access/filtered.rs @@ -9,7 +9,7 @@ use thiserror::Error; /// Provides read-only access to a single [`Entity`] and some of its components, /// as defined by the contained [`Access`] at runtime. This is an [`EntityRef`] -/// with an [`AccessScope`] of [`Filtered`]. +/// with an [`AsAccess`] of [`Filtered`]. /// /// To define the access when used as a [`QueryData`], use a [`QueryBuilder`] or /// [`QueryParamBuilder`]. The [`FilteredEntityRef`] must be the entire @@ -34,20 +34,13 @@ use thiserror::Error; /// ``` /// /// [`Entity`]: crate::world::Entity -/// [`AccessScope`]: crate::world::AccessScope +/// [`AsAccess`]: crate::world::AsAccess /// [`QueryData`]: crate::query::QueryData /// [`QueryBuilder`]: crate::query::QueryBuilder /// [`QueryParamBuilder`]: crate::system::QueryParamBuilder pub type FilteredEntityRef<'w, 's> = EntityRef<'w, Filtered<'s>>; impl<'w, 's> FilteredEntityRef<'w, 's> { - /// Returns a reference to the underlying [`Access`]. - #[inline] - #[deprecated(since = "0.19.0", note = "Use `EntityRef::scope()` instead.")] - pub fn access(&self) -> &Access { - self.scope().0 - } - /// Consumes `self` and attempts to return an [`EntityRef`] with [`All`] /// access. /// @@ -56,11 +49,11 @@ impl<'w, 's> FilteredEntityRef<'w, 's> { /// Returns [`TryFromFilteredError::MissingReadAllAccess`] if the contained /// [`Access`] does not have read access to all components. pub fn try_into_all(self) -> Result, TryFromFilteredError> { - if !self.scope().has_read_all() { + if !self.access().has_read_all() { Err(TryFromFilteredError::MissingReadAllAccess) } else { - // SAFETY: `Access::has_read_all` check satisfies the `All` scope - // for `EntityRef`. + // SAFETY: `Access::has_read_all` check satisfies the `All` access + // kind for `EntityRef`. Ok(unsafe { EntityRef::new(self.cell, All) }) } } @@ -126,7 +119,7 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { pub fn new_readonly(filtered_entity_mut: &FilteredEntityMut<'w, 's>) -> Self { Self { entity: filtered_entity_mut.cell, - access: filtered_entity_mut.scope().0, + access: filtered_entity_mut.access().0, } } @@ -142,7 +135,7 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { /// Provides mutable access to a single [`Entity`] and some of its components, /// as defined by the contained [`Access`] at runtime. This is an [`EntityMut`] -/// with an [`AccessScope`] of [`Filtered`]. +/// with an [`AsAccess`] of [`Filtered`]. /// /// To define the access when used as a [`QueryData`], use a [`QueryBuilder`] or /// [`QueryParamBuilder`]. The `FilteredEntityMut` must be the entire @@ -169,20 +162,13 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { /// Also see [`UnsafeFilteredEntityMut`] for a way to bypass borrow-checker restrictions. /// /// [`Entity`]: crate::world::Entity -/// [`AccessScope`]: crate::world::AccessScope +/// [`AsAccess`]: crate::world::AsAccess /// [`QueryData`]: crate::query::QueryData /// [`QueryBuilder`]: crate::query::QueryBuilder /// [`QueryParamBuilder`]: crate::system::QueryParamBuilder pub type FilteredEntityMut<'w, 's> = EntityMut<'w, Filtered<'s>>; impl<'w, 's> FilteredEntityMut<'w, 's> { - /// Returns a reference to the underlying [`Access`]. - #[inline] - #[deprecated(since = "0.19.0", note = "Use `EntityMut::scope()` instead.")] - pub fn access(&self) -> &Access { - self.scope().0 - } - /// Consumes `self` and attempts to return an [`EntityMut`] with [`All`] access. /// /// # Errors @@ -192,13 +178,13 @@ impl<'w, 's> FilteredEntityMut<'w, 's> { /// - Returns [`TryFromFilteredError::MissingWriteAllAccess`] if the contained /// [`Access`] does not have write access to all components. pub fn try_into_all(self) -> Result, TryFromFilteredError> { - if !self.scope().has_read_all() { + if !self.access().has_read_all() { Err(TryFromFilteredError::MissingReadAllAccess) - } else if !self.scope().has_write_all() { + } else if !self.access().has_write_all() { Err(TryFromFilteredError::MissingWriteAllAccess) } else { // SAFETY: `Access::has_read_all` and `Access::has_write_all` checks - // satisfy the `All` scope for `EntityMut`. + // satisfy the `All` access for `EntityMut`. Ok(unsafe { EntityMut::new(self.cell, All) }) } } @@ -241,8 +227,7 @@ impl<'w> TryFrom<&'w mut FilteredEntityMut<'_, '_>> for EntityMut<'w> { } /// Error type returned by [`TryFrom`] conversions from [`EntityRef`]/[`EntityMut`] -/// entity reference types with [`Filtered`] access scopes to ones with [`All`] -/// access scopes. +/// entity reference types with [`Filtered`] access to ones with [`All`] access. #[derive(Error, Debug)] pub enum TryFromFilteredError { /// Error indicating that the filtered entity does not have read access to diff --git a/crates/bevy_ecs/src/world/entity_access/mod.rs b/crates/bevy_ecs/src/world/entity_access/mod.rs index 2e7d96991583c..21b8a9ce65f66 100644 --- a/crates/bevy_ecs/src/world/entity_access/mod.rs +++ b/crates/bevy_ecs/src/world/entity_access/mod.rs @@ -1,5 +1,5 @@ -mod access_scope; mod all; +mod as_access; mod component_fetch; mod entity_mut; mod entity_ref; @@ -8,7 +8,7 @@ mod except; mod filtered; mod world_mut; -pub use access_scope::*; +pub use as_access::*; pub use component_fetch::*; pub use entity_mut::*; pub use entity_ref::*; diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index ea2f4303b910b..2882e84d0d3fe 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -25,7 +25,7 @@ pub use crate::{ pub use bevy_ecs_macros::FromWorld; pub use deferred_world::DeferredWorld; pub use entity_access::{ - AccessScope, All, ComponentEntry, DynamicComponentFetch, EntityMut, EntityMutExcept, EntityRef, + All, AsAccess, ComponentEntry, DynamicComponentFetch, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, EntityWorldMut, Except, Filtered, FilteredEntityMut, FilteredEntityRef, OccupiedComponentEntry, TryFromFilteredError, UnsafeFilteredEntityMut, VacantComponentEntry, }; diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 2eb1bddfdaae5..2154147c6ee9c 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -19,7 +19,7 @@ use crate::{ query::{DebugCheckedUnwrap, QueryAccessError, ReleaseStateQueryData}, resource::Resource, storage::{ComponentSparseSet, Storages, Table}, - world::{AccessScope, RawCommandQueue}, + world::{AsAccess, RawCommandQueue}, }; use bevy_platform::sync::atomic::Ordering; use bevy_ptr::Ptr; @@ -807,14 +807,14 @@ impl<'w> UnsafeEntityCell<'w> { /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// Caller must ensure the provided [`AsAccess`] does not exceed the read /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get(self, scope: &impl AccessScope) -> Option<&'w T> { + pub unsafe fn get(self, access: impl AsAccess) -> Option<&'w T> { let component_id = self.world.components().get_valid_id(TypeId::of::())?; - if !scope.can_read(component_id, self.world.components()) { + if !access.has_component_read(component_id) { return None; } @@ -837,14 +837,14 @@ impl<'w> UnsafeEntityCell<'w> { /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// Caller must ensure the provided [`AsAccess`] does not exceed the read /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] - pub unsafe fn get_ref(self, scope: &impl AccessScope) -> Option> { + pub unsafe fn get_ref(self, access: impl AsAccess) -> Option> { let component_id = self.world.components().get_valid_id(TypeId::of::())?; - if !scope.can_read(component_id, self.world.components()) { + if !access.has_component_read(component_id) { return None; } @@ -876,17 +876,17 @@ impl<'w> UnsafeEntityCell<'w> { /// /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// Caller must ensure the provided [`AsAccess`] does not exceed the read /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_change_ticks( self, - scope: &impl AccessScope, + access: impl AsAccess, ) -> Option { let component_id = self.world.components().get_valid_id(TypeId::of::())?; - if !scope.can_read(component_id, self.world.components()) { + if !access.has_component_read(component_id) { return None; } @@ -913,16 +913,16 @@ impl<'w> UnsafeEntityCell<'w> { /// /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// Caller must ensure the provided [`AsAccess`] does not exceed the read /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_change_ticks_by_id( &self, - scope: &impl AccessScope, + access: impl AsAccess, component_id: ComponentId, ) -> Option { - if !scope.can_read(component_id, self.world.components()) { + if !access.has_component_read(component_id) { return None; } @@ -945,47 +945,47 @@ impl<'w> UnsafeEntityCell<'w> { /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the write + /// Caller must ensure the provided [`AsAccess`] does not exceed the write /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_mut>( self, - scope: &impl AccessScope, + access: impl AsAccess, ) -> Option> { // SAFETY: // - trait bound `T: Component` ensures component is mutable // - proper world access is promised by caller - unsafe { self.get_mut_assume_mutable(scope) } + unsafe { self.get_mut_assume_mutable(access) } } /// # Safety /// /// Caller must ensure that: - /// - The provided [`AccessScope`] does not exceed the write permissions of + /// - The provided [`AsAccess`] does not exceed the write permissions of /// `self` in a way that would violate Rust's aliasing rules, including /// via copies of `self` or other indirect means. /// - The component `T` is mutable. #[inline] pub unsafe fn get_mut_assume_mutable( self, - scope: &impl AccessScope, + access: impl AsAccess, ) -> Option> { // SAFETY: proper world access is promised by caller - unsafe { self.get_mut_using_ticks_assume_mutable(scope, self.last_run, self.this_run) } + unsafe { self.get_mut_using_ticks_assume_mutable(access, self.last_run, self.this_run) } } /// # Safety /// /// Caller must ensure that: - /// - The provided [`AccessScope`] does not exceed the write permissions of + /// - The provided [`AsAccess`] does not exceed the write permissions of /// `self` in a way that would violate Rust's aliasing rules, including /// via copies of `self` or other indirect means. /// - The component `T` is mutable. #[inline] pub(crate) unsafe fn get_mut_using_ticks_assume_mutable( &self, - scope: &impl AccessScope, + access: impl AsAccess, last_change_tick: Tick, change_tick: Tick, ) -> Option> { @@ -993,7 +993,7 @@ impl<'w> UnsafeEntityCell<'w> { let component_id = self.world.components().get_valid_id(TypeId::of::())?; - if !scope.can_write(component_id, self.world.components()) { + if !access.has_component_write(component_id) { return None; } @@ -1076,16 +1076,16 @@ impl<'w> UnsafeEntityCell<'w> { /// /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the read + /// Caller must ensure the provided [`AsAccess`] does not exceed the read /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_by_id( self, - scope: &impl AccessScope, + access: impl AsAccess, component_id: ComponentId, ) -> Option> { - if !scope.can_read(component_id, self.world.components()) { + if !access.has_component_read(component_id) { return None; } @@ -1113,18 +1113,18 @@ impl<'w> UnsafeEntityCell<'w> { /// /// # Safety /// - /// Caller must ensure the provided [`AccessScope`] does not exceed the write + /// Caller must ensure the provided [`AsAccess`] does not exceed the write /// permissions of `self` in a way that would violate Rust's aliasing rules, /// including via copies of `self` or other indirect means. #[inline] pub unsafe fn get_mut_by_id( self, - scope: &impl AccessScope, + access: impl AsAccess, component_id: ComponentId, ) -> Result, GetEntityMutByIdError> { self.world.assert_allows_mutable_access(); - if !scope.can_write(component_id, self.world.components()) { + if !access.has_component_write(component_id) { return Err(GetEntityMutByIdError::ComponentNotFound); } @@ -1170,19 +1170,19 @@ impl<'w> UnsafeEntityCell<'w> { /// # Safety /// /// Caller must ensure that: - /// - The provided [`AccessScope`] does not exceed the write permissions of + /// - The provided [`AsAccess`] does not exceed the write permissions of /// `self` in a way that would violate Rust's aliasing rules, including /// via copies of `self` or other indirect means. /// - The component `T` is mutable. #[inline] pub unsafe fn get_mut_assume_mutable_by_id( self, - scope: &impl AccessScope, + access: impl AsAccess, component_id: ComponentId, ) -> Result, GetEntityMutByIdError> { self.world.assert_allows_mutable_access(); - if !scope.can_write(component_id, self.world.components()) { + if !access.has_component_write(component_id) { return Err(GetEntityMutByIdError::ComponentNotFound); } @@ -1409,6 +1409,6 @@ mod tests { let world_cell = world.as_unsafe_world_cell_readonly(); let entity_cell = world_cell.get_entity(entity).unwrap(); // SAFETY: this invalid usage will be caught by a runtime panic. - let _ = unsafe { entity_cell.get_mut::(&All) }; + let _ = unsafe { entity_cell.get_mut::(All) }; } } diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index a2e359293be1b..e5dd3c40b50a2 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -157,7 +157,7 @@ fn main() { let mut query = builder.build(); query.iter_mut(&mut world).for_each(|filtered_entity| { let terms = filtered_entity - .scope() + .access() .try_iter_component_access() .unwrap() .map(|component_access| { diff --git a/release-content/migration-guides/scoped_entities.md b/release-content/migration-guides/entity_deduplication.md similarity index 89% rename from release-content/migration-guides/scoped_entities.md rename to release-content/migration-guides/entity_deduplication.md index b05e0d33a5eeb..ad5c829e26157 100644 --- a/release-content/migration-guides/scoped_entities.md +++ b/release-content/migration-guides/entity_deduplication.md @@ -1,5 +1,5 @@ --- -title: "`UnsafeEntityCell` functions now have an `AccessScope` parameter" +title: "`UnsafeEntityCell` functions now have an `AccessKind` parameter" pull_requests: [22538] --- @@ -14,7 +14,7 @@ The following functions now return a `Result` with a proper error, instead of an - `EntityMutExcept::get_by_id` - `EntityMutExcept::get_mut_by_id` -The following functions now take an `AccessScope` as an additional argument. +The following functions now take an `AccessKind` as an additional argument. You should pass a scope that most closely matches your access patterns, and ensure it abides by Rust aliasing rules. From b1ded199031345380168cb7bbf983050c9dd37cb Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Thu, 22 Jan 2026 20:36:20 -0600 Subject: [PATCH 4/5] elide lifetimes --- .../world/entity_access/component_fetch.rs | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_access/component_fetch.rs b/crates/bevy_ecs/src/world/entity_access/component_fetch.rs index 9dfe06d7cf3da..383ca108d8ed5 100644 --- a/crates/bevy_ecs/src/world/entity_access/component_fetch.rs +++ b/crates/bevy_ecs/src/world/entity_access/component_fetch.rs @@ -51,11 +51,11 @@ pub unsafe trait DynamicComponentFetch { /// # Errors /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. - unsafe fn fetch_ref<'w>( + unsafe fn fetch_ref( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError>; + ) -> Result, EntityComponentError>; /// Returns untyped mutable reference(s) to the component(s) with the /// given [`ComponentId`]s, as determined by `self`. @@ -70,11 +70,11 @@ pub unsafe trait DynamicComponentFetch { /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. /// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times. - unsafe fn fetch_mut<'w>( + unsafe fn fetch_mut( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError>; + ) -> Result, EntityComponentError>; /// Returns untyped mutable reference(s) to the component(s) with the /// given [`ComponentId`]s, as determined by `self`. @@ -92,11 +92,11 @@ pub unsafe trait DynamicComponentFetch { /// /// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity. /// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times. - unsafe fn fetch_mut_assume_mutable<'w>( + unsafe fn fetch_mut_assume_mutable( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError>; + ) -> Result, EntityComponentError>; } // SAFETY: @@ -106,30 +106,30 @@ unsafe impl DynamicComponentFetch for ComponentId { type Ref<'w> = Ptr<'w>; type Mut<'w> = MutUntyped<'w>; - unsafe fn fetch_ref<'w>( + unsafe fn fetch_ref( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has read access to the component. unsafe { cell.get_by_id(access, self) }.ok_or(EntityComponentError::MissingComponent(self)) } - unsafe fn fetch_mut<'w>( + unsafe fn fetch_mut( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has mutable access to the component. unsafe { cell.get_mut_by_id(access, self) } .map_err(|_| EntityComponentError::MissingComponent(self)) } - unsafe fn fetch_mut_assume_mutable<'w>( + unsafe fn fetch_mut_assume_mutable( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // SAFETY: caller ensures that the cell has mutable access to the component. unsafe { cell.get_mut_assume_mutable_by_id(access, self) } .map_err(|_| EntityComponentError::MissingComponent(self)) @@ -143,27 +143,27 @@ unsafe impl DynamicComponentFetch for [ComponentId; N] { type Ref<'w> = [Ptr<'w>; N]; type Mut<'w> = [MutUntyped<'w>; N]; - unsafe fn fetch_ref<'w>( + unsafe fn fetch_ref( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { <&Self>::fetch_ref(&self, cell, access) } - unsafe fn fetch_mut<'w>( + unsafe fn fetch_mut( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { <&Self>::fetch_mut(&self, cell, access) } - unsafe fn fetch_mut_assume_mutable<'w>( + unsafe fn fetch_mut_assume_mutable( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { <&Self>::fetch_mut_assume_mutable(&self, cell, access) } } @@ -175,11 +175,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { type Ref<'w> = [Ptr<'w>; N]; type Mut<'w> = [MutUntyped<'w>; N]; - unsafe fn fetch_ref<'w>( + unsafe fn fetch_ref( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { let mut ptrs = [const { MaybeUninit::uninit() }; N]; for (ptr, &id) in core::iter::zip(&mut ptrs, self) { *ptr = MaybeUninit::new( @@ -195,11 +195,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { Ok(ptrs) } - unsafe fn fetch_mut<'w>( + unsafe fn fetch_mut( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -224,11 +224,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId; N] { Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable<'w>( + unsafe fn fetch_mut_assume_mutable( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -261,11 +261,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { type Ref<'w> = Vec>; type Mut<'w> = Vec>; - unsafe fn fetch_ref<'w>( + unsafe fn fetch_ref( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { let mut ptrs = Vec::with_capacity(self.len()); for &id in self { ptrs.push( @@ -277,11 +277,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { Ok(ptrs) } - unsafe fn fetch_mut<'w>( + unsafe fn fetch_mut( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -302,11 +302,11 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] { Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable<'w>( + unsafe fn fetch_mut_assume_mutable( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { // Check for duplicate component IDs. for i in 0..self.len() { for j in 0..i { @@ -335,11 +335,11 @@ unsafe impl DynamicComponentFetch for &'_ HashSet { type Ref<'w> = HashMap>; type Mut<'w> = HashMap>; - unsafe fn fetch_ref<'w>( + unsafe fn fetch_ref( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( @@ -352,11 +352,11 @@ unsafe impl DynamicComponentFetch for &'_ HashSet { Ok(ptrs) } - unsafe fn fetch_mut<'w>( + unsafe fn fetch_mut( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( @@ -369,11 +369,11 @@ unsafe impl DynamicComponentFetch for &'_ HashSet { Ok(ptrs) } - unsafe fn fetch_mut_assume_mutable<'w>( + unsafe fn fetch_mut_assume_mutable( self, - cell: UnsafeEntityCell<'w>, + cell: UnsafeEntityCell<'_>, access: impl AsAccess, - ) -> Result, EntityComponentError> { + ) -> Result, EntityComponentError> { let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default()); for &id in self { ptrs.insert( From 58bfdf8d6035f9f1db8072949964b365e4ef0afb Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Sat, 24 Jan 2026 17:35:34 -0600 Subject: [PATCH 5/5] address feedback Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> --- .../bevy_ecs/src/world/entity_access/as_access.rs | 3 +-- .../bevy_ecs/src/world/entity_access/filtered.rs | 6 ++---- .../migration-guides/entity_deduplication.md | 14 +++++++------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_access/as_access.rs b/crates/bevy_ecs/src/world/entity_access/as_access.rs index 927d458ba9c8d..1aef14e97c8ea 100644 --- a/crates/bevy_ecs/src/world/entity_access/as_access.rs +++ b/crates/bevy_ecs/src/world/entity_access/as_access.rs @@ -35,8 +35,7 @@ impl Deref for All { type Target = Access; fn deref(&self) -> &Access { - static WRITE_ALL: Access = Access::new_write_all(); - &WRITE_ALL + const { &Access::new_write_all() } } } diff --git a/crates/bevy_ecs/src/world/entity_access/filtered.rs b/crates/bevy_ecs/src/world/entity_access/filtered.rs index 7556d418f9250..57b550926d907 100644 --- a/crates/bevy_ecs/src/world/entity_access/filtered.rs +++ b/crates/bevy_ecs/src/world/entity_access/filtered.rs @@ -12,8 +12,7 @@ use thiserror::Error; /// with an [`AsAccess`] of [`Filtered`]. /// /// To define the access when used as a [`QueryData`], use a [`QueryBuilder`] or -/// [`QueryParamBuilder`]. The [`FilteredEntityRef`] must be the entire -/// [`QueryData`], and not nested inside a tuple with other data. +/// [`QueryParamBuilder`]. /// /// ``` /// # use bevy_ecs::{prelude::*, world::FilteredEntityRef}; @@ -138,8 +137,7 @@ impl<'w, 's> UnsafeFilteredEntityMut<'w, 's> { /// with an [`AsAccess`] of [`Filtered`]. /// /// To define the access when used as a [`QueryData`], use a [`QueryBuilder`] or -/// [`QueryParamBuilder`]. The `FilteredEntityMut` must be the entire -/// `QueryData`, and not nested inside a tuple with other data. +/// [`QueryParamBuilder`]. /// /// ``` /// # use bevy_ecs::{prelude::*, world::FilteredEntityMut}; diff --git a/release-content/migration-guides/entity_deduplication.md b/release-content/migration-guides/entity_deduplication.md index ad5c829e26157..d9dde72bfbbf7 100644 --- a/release-content/migration-guides/entity_deduplication.md +++ b/release-content/migration-guides/entity_deduplication.md @@ -1,5 +1,5 @@ --- -title: "`UnsafeEntityCell` functions now have an `AccessKind` parameter" +title: "`UnsafeEntityCell` functions now have an `AsAccess` parameter" pull_requests: [22538] --- @@ -14,9 +14,9 @@ The following functions now return a `Result` with a proper error, instead of an - `EntityMutExcept::get_by_id` - `EntityMutExcept::get_mut_by_id` -The following functions now take an `AccessKind` as an additional argument. -You should pass a scope that most closely matches your access patterns, and -ensure it abides by Rust aliasing rules. +The following functions now take an `AsAccess` as an additional argument. +You should pass an access type that most closely matches your access patterns, +and ensure it abides by Rust aliasing rules. - `UnsafeEntityCell::get` - `UnsafeEntityCell::get_ref` @@ -29,7 +29,7 @@ ensure it abides by Rust aliasing rules. - `UnsafeEntityCell::get_mut_assume_mutable_by_id` For example, if your cell can access all components without violating aliasing -rules, use the `All` scope. If your cell can only access a specific set of -components without violating aliasing rules, consider using `Filtered` or `Except` -scopes. If you are able to validate externally that you won't violate aliasing +rules, use `All`. If your cell can only access a specific set of +components without violating aliasing rules, consider using `Filtered` or `Except`. +If you are able to validate externally that you won't violate aliasing rules by accessing a particular component, you may use `All`.