diff --git a/crates/bevy_animation/src/animation_curves.rs b/crates/bevy_animation/src/animation_curves.rs index 45fa393e05..782daf0f55 100644 --- a/crates/bevy_animation/src/animation_curves.rs +++ b/crates/bevy_animation/src/animation_curves.rs @@ -408,10 +408,7 @@ impl AnimationCurveEvaluator for AnimatableCurveEvaluator { self.evaluator.push_blend_register(weight, graph_node) } - fn commit<'a>( - &mut self, - mut entity: AnimationEntityMut<'a>, - ) -> Result<(), AnimationEvaluationError> { + fn commit(&mut self, mut entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError> { let property = self.property.get_mut(&mut entity)?; *property = self .evaluator @@ -596,10 +593,7 @@ impl AnimationCurveEvaluator for WeightsCurveEvaluator { Ok(()) } - fn commit<'a>( - &mut self, - mut entity: AnimationEntityMut<'a>, - ) -> Result<(), AnimationEvaluationError> { + fn commit(&mut self, mut entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError> { if self.stack_morph_target_weights.is_empty() { return Ok(()); } @@ -905,10 +899,7 @@ pub trait AnimationCurveEvaluator: Downcast + Send + Sync + 'static { /// /// The property on the component must be overwritten with the value from /// the stack, not blended with it. - fn commit<'a>( - &mut self, - entity: AnimationEntityMut<'a>, - ) -> Result<(), AnimationEvaluationError>; + fn commit(&mut self, entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError>; } impl_downcast!(AnimationCurveEvaluator); diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index ae7ce42ed6..585f5d1229 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -1021,8 +1021,8 @@ pub fn advance_animations( } /// A type alias for [`EntityMutExcept`] as used in animation. -pub type AnimationEntityMut<'w> = - EntityMutExcept<'w, (AnimationTarget, AnimationPlayer, AnimationGraphHandle)>; +pub type AnimationEntityMut<'w, 's> = + EntityMutExcept<'w, 's, (AnimationTarget, AnimationPlayer, AnimationGraphHandle)>; /// A system that modifies animation targets (e.g. bones in a skinned mesh) /// according to the currently-playing animations. diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index c75d52d356..e66606d11c 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -14,7 +14,6 @@ use crate::{ use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, marker::PhantomData, panic::Location}; -use smallvec::SmallVec; use variadics_please::all_tuples; /// Types that can be fetched from a [`World`] using a [`Query`]. @@ -1160,12 +1159,12 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityMut<'a, 'b> { /// SAFETY: `EntityRefExcept` guards access to all components in the bundle `B` /// and populates `Access` values so that queries that conflict with this access /// are rejected. -unsafe impl<'a, B> WorldQuery for EntityRefExcept<'a, B> +unsafe impl<'a, 'b, B> WorldQuery for EntityRefExcept<'a, 'b, B> where B: Bundle, { type Fetch<'w> = EntityFetch<'w>; - type State = SmallVec<[ComponentId; 4]>; + type State = Access; fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { fetch @@ -1197,15 +1196,9 @@ where unsafe fn set_table<'w, 's>(_: &mut Self::Fetch<'w>, _: &'s Self::State, _: &'w Table) {} fn update_component_access( - state: &Self::State, + my_access: &Self::State, filtered_access: &mut FilteredAccess, ) { - let mut my_access = Access::new(); - my_access.read_all_components(); - for id in state { - my_access.remove_component_read(*id); - } - let access = filtered_access.access_mut(); assert!( access.is_compatible(&my_access), @@ -1216,17 +1209,29 @@ where } fn init_state(world: &mut World) -> Self::State { - Self::get_state(world.components()).unwrap() + let mut access = Access::new(); + access.read_all_components(); + B::component_ids(&mut world.components_registrator(), &mut |id| { + access.remove_component_read(id); + }); + access } fn get_state(components: &Components) -> Option { - let mut ids = SmallVec::new(); + let mut access = Access::new(); + access.read_all_components(); B::get_component_ids(components, &mut |maybe_id| { + // If the component isn't registered, we don't have a `ComponentId` + // to use to exclude its access. + // Rather than fail, just try to take additional access. + // This is sound because access checks will run on the resulting access. + // Since the component isn't registered, there are no entities with that + // component, and the extra access will usually have no effect. if let Some(id) = maybe_id { - ids.push(id); + access.remove_component_read(id); } }); - Some(ids) + Some(access) } fn matches_component_set(_: &Self::State, _: &impl Fn(ComponentId) -> bool) -> bool { @@ -1235,13 +1240,13 @@ where } /// SAFETY: `Self` is the same as `Self::ReadOnly`. -unsafe impl<'a, B> QueryData for EntityRefExcept<'a, B> +unsafe impl<'a, 'b, B> QueryData for EntityRefExcept<'a, 'b, B> where B: Bundle, { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w, 's> = EntityRefExcept<'w, B>; + type Item<'w, 's> = EntityRefExcept<'w, 's, B>; fn shrink<'wlong: 'wshort, 'wshort, 's>( item: Self::Item<'wlong, 's>, @@ -1250,7 +1255,7 @@ where } unsafe fn fetch<'w, 's>( - _state: &'s Self::State, + access: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _: TableRow, @@ -1259,23 +1264,23 @@ where .world .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) .unwrap(); - EntityRefExcept::new(cell) + EntityRefExcept::new(cell, access) } } /// SAFETY: `EntityRefExcept` enforces read-only access to its contained /// components. -unsafe impl<'a, B> ReadOnlyQueryData for EntityRefExcept<'a, B> where B: Bundle {} +unsafe impl ReadOnlyQueryData for EntityRefExcept<'_, '_, B> where B: Bundle {} /// SAFETY: `EntityMutExcept` guards access to all components in the bundle `B` /// and populates `Access` values so that queries that conflict with this access /// are rejected. -unsafe impl<'a, B> WorldQuery for EntityMutExcept<'a, B> +unsafe impl<'a, 'b, B> WorldQuery for EntityMutExcept<'a, 'b, B> where B: Bundle, { type Fetch<'w> = EntityFetch<'w>; - type State = SmallVec<[ComponentId; 4]>; + type State = Access; fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { fetch @@ -1307,15 +1312,9 @@ where unsafe fn set_table<'w, 's>(_: &mut Self::Fetch<'w>, _: &'s Self::State, _: &'w Table) {} fn update_component_access( - state: &Self::State, + my_access: &Self::State, filtered_access: &mut FilteredAccess, ) { - let mut my_access = Access::new(); - my_access.write_all_components(); - for id in state { - my_access.remove_component_read(*id); - } - let access = filtered_access.access_mut(); assert!( access.is_compatible(&my_access), @@ -1326,17 +1325,29 @@ where } fn init_state(world: &mut World) -> Self::State { - Self::get_state(world.components()).unwrap() + let mut access = Access::new(); + access.write_all_components(); + B::component_ids(&mut world.components_registrator(), &mut |id| { + access.remove_component_read(id); + }); + access } fn get_state(components: &Components) -> Option { - let mut ids = SmallVec::new(); + let mut access = Access::new(); + access.write_all_components(); B::get_component_ids(components, &mut |maybe_id| { + // If the component isn't registered, we don't have a `ComponentId` + // to use to exclude its access. + // Rather than fail, just try to take additional access. + // This is sound because access checks will run on the resulting access. + // Since the component isn't registered, there are no entities with that + // component, and the extra access will usually have no effect. if let Some(id) = maybe_id { - ids.push(id); + access.remove_component_read(id); } }); - Some(ids) + Some(access) } fn matches_component_set(_: &Self::State, _: &impl Fn(ComponentId) -> bool) -> bool { @@ -1346,13 +1357,13 @@ where /// SAFETY: All accesses that `EntityRefExcept` provides are also accesses that /// `EntityMutExcept` provides. -unsafe impl<'a, B> QueryData for EntityMutExcept<'a, B> +unsafe impl<'a, 'b, B> QueryData for EntityMutExcept<'a, 'b, B> where B: Bundle, { const IS_READ_ONLY: bool = false; - type ReadOnly = EntityRefExcept<'a, B>; - type Item<'w, 's> = EntityMutExcept<'w, B>; + type ReadOnly = EntityRefExcept<'a, 'b, B>; + type Item<'w, 's> = EntityMutExcept<'w, 's, B>; fn shrink<'wlong: 'wshort, 'wshort, 's>( item: Self::Item<'wlong, 's>, @@ -1361,7 +1372,7 @@ where } unsafe fn fetch<'w, 's>( - _state: &'s Self::State, + access: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _: TableRow, @@ -1370,7 +1381,7 @@ where .world .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) .unwrap(); - EntityMutExcept::new(cell) + EntityMutExcept::new(cell, access) } } diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 606990f6b3..cab2ee9c93 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -970,13 +970,13 @@ unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator - for QueryIter<'w, 's, EntityRefExcept<'_, B>, F> + for QueryIter<'w, 's, EntityRefExcept<'_, '_, B>, F> { } // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator - for QueryIter<'w, 's, EntityMutExcept<'_, B>, F> + for QueryIter<'w, 's, EntityMutExcept<'_, '_, B>, F> { } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 3f57d77c10..0b850f816b 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -3561,6 +3561,14 @@ impl<'a> From<&'a EntityWorldMut<'_>> for FilteredEntityRef<'a, 'static> { } } +impl<'w, 's, B: Bundle> From<&'w EntityRefExcept<'_, 's, B>> for FilteredEntityRef<'w, 's> { + fn from(value: &'w EntityRefExcept<'_, 's, B>) -> Self { + // SAFETY: + // - The FilteredEntityRef has the same component access as the given EntityRefExcept. + unsafe { FilteredEntityRef::new(value.entity, value.access) } + } +} + impl PartialEq for FilteredEntityRef<'_, '_> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() @@ -3884,6 +3892,14 @@ impl<'a> From<&'a mut EntityWorldMut<'_>> for FilteredEntityMut<'a, 'static> { } } +impl<'w, 's, B: Bundle> From<&'w EntityMutExcept<'_, 's, B>> for FilteredEntityMut<'w, 's> { + fn from(value: &'w EntityMutExcept<'_, 's, B>) -> Self { + // SAFETY: + // - The FilteredEntityMut has the same component access as the given EntityMutExcept. + unsafe { FilteredEntityMut::new(value.entity, value.access) } + } +} + impl PartialEq for FilteredEntityMut<'_, '_> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() @@ -3938,23 +3954,28 @@ pub enum TryFromFilteredError { /// Provides read-only access to a single entity and all its components, save /// for an explicitly-enumerated set. -pub struct EntityRefExcept<'w, B> +pub struct EntityRefExcept<'w, 's, B> where B: Bundle, { entity: UnsafeEntityCell<'w>, + access: &'s Access, phantom: PhantomData, } -impl<'w, B> EntityRefExcept<'w, B> +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>) -> Self { + pub(crate) unsafe fn new( + entity: UnsafeEntityCell<'w>, + access: &'s Access, + ) -> Self { Self { entity, + access, phantom: PhantomData, } } @@ -4107,34 +4128,34 @@ where } } -impl<'a, B> From<&'a EntityMutExcept<'_, B>> for EntityRefExcept<'a, B> +impl<'w, 's, B> From<&'w EntityMutExcept<'_, 's, B>> for EntityRefExcept<'w, 's, B> where B: Bundle, { - fn from(entity: &'a EntityMutExcept<'_, B>) -> Self { + fn from(entity: &'w EntityMutExcept<'_, 's, B>) -> Self { // SAFETY: All accesses that `EntityRefExcept` provides are also // accesses that `EntityMutExcept` provides. - unsafe { EntityRefExcept::new(entity.entity) } + unsafe { EntityRefExcept::new(entity.entity, entity.access) } } } -impl Clone for EntityRefExcept<'_, B> { +impl Clone for EntityRefExcept<'_, '_, B> { fn clone(&self) -> Self { *self } } -impl Copy for EntityRefExcept<'_, B> {} +impl Copy for EntityRefExcept<'_, '_, B> {} -impl PartialEq for EntityRefExcept<'_, B> { +impl PartialEq for EntityRefExcept<'_, '_, B> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() } } -impl Eq for EntityRefExcept<'_, B> {} +impl Eq for EntityRefExcept<'_, '_, B> {} -impl PartialOrd 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 { @@ -4142,26 +4163,26 @@ impl PartialOrd for EntityRefExcept<'_, B> { } } -impl Ord for EntityRefExcept<'_, B> { +impl Ord for EntityRefExcept<'_, '_, B> { fn cmp(&self, other: &Self) -> Ordering { self.entity().cmp(&other.entity()) } } -impl Hash for EntityRefExcept<'_, B> { +impl Hash for EntityRefExcept<'_, '_, B> { fn hash(&self, state: &mut H) { self.entity().hash(state); } } -impl ContainsEntity for EntityRefExcept<'_, B> { +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> {} +unsafe impl EntityEquivalent for EntityRefExcept<'_, '_, B> {} /// Provides mutable access to all components of an entity, with the exception /// of an explicit set. @@ -4171,23 +4192,28 @@ unsafe impl EntityEquivalent for EntityRefExcept<'_, B> {} /// 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 /// [`crate::query::Without`] filter. -pub struct EntityMutExcept<'w, B> +pub struct EntityMutExcept<'w, 's, B> where B: Bundle, { entity: UnsafeEntityCell<'w>, + access: &'s Access, phantom: PhantomData, } -impl<'w, B> EntityMutExcept<'w, B> +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>) -> Self { + pub(crate) unsafe fn new( + entity: UnsafeEntityCell<'w>, + access: &'s Access, + ) -> Self { Self { entity, + access, phantom: PhantomData, } } @@ -4203,16 +4229,16 @@ where /// /// This is useful if you have `&mut EntityMutExcept`, but you need /// `EntityMutExcept`. - pub fn reborrow(&mut self) -> EntityMutExcept<'_, B> { + pub fn reborrow(&mut self) -> EntityMutExcept<'_, 's, B> { // SAFETY: We have exclusive access to the entire entity and the // applicable components. - unsafe { Self::new(self.entity) } + unsafe { Self::new(self.entity, self.access) } } /// Gets read-only access to all of the entity's components, except for the /// ones in `CL`. #[inline] - pub fn as_readonly(&self) -> EntityRefExcept<'_, B> { + pub fn as_readonly(&self) -> EntityRefExcept<'_, 's, B> { EntityRefExcept::from(self) } @@ -4341,15 +4367,15 @@ where } } -impl PartialEq for EntityMutExcept<'_, B> { +impl PartialEq for EntityMutExcept<'_, '_, B> { fn eq(&self, other: &Self) -> bool { self.entity() == other.entity() } } -impl Eq for EntityMutExcept<'_, B> {} +impl Eq for EntityMutExcept<'_, '_, B> {} -impl PartialOrd 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 { @@ -4357,26 +4383,26 @@ impl PartialOrd for EntityMutExcept<'_, B> { } } -impl Ord for EntityMutExcept<'_, B> { +impl Ord for EntityMutExcept<'_, '_, B> { fn cmp(&self, other: &Self) -> Ordering { self.entity().cmp(&other.entity()) } } -impl Hash for EntityMutExcept<'_, B> { +impl Hash for EntityMutExcept<'_, '_, B> { fn hash(&self, state: &mut H) { self.entity().hash(state); } } -impl ContainsEntity for EntityMutExcept<'_, B> { +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> {} +unsafe impl EntityEquivalent for EntityMutExcept<'_, '_, B> {} fn bundle_contains_component(components: &Components, query_id: ComponentId) -> bool where