Make EntityRef::new unsafe (#7222)
				
					
				
			# Objective - We rely on the construction of `EntityRef` to be valid elsewhere in unsafe code. This construction is not checked (for performance reasons), and thus this private method must be unsafe. - Fixes #7218. ## Solution - Make the method unsafe. - Add safety docs. - Improve safety docs slightly for the sibling `EntityMut::new`. - Add debug asserts to start to verify these assumptions in debug mode. ## Context for reviewers I attempted to verify the `EntityLocation` more thoroughly, but this turned out to be more work than expected. I've spun that off into #7221 as a result.
This commit is contained in:
		
							parent
							
								
									e44990a48d
								
							
						
					
					
						commit
						39e14a4a40
					
				| @ -747,7 +747,7 @@ impl EntityMeta { | |||||||
| // SAFETY:
 | // SAFETY:
 | ||||||
| // This type must not contain any pointers at any level, and be safe to fully fill with u8::MAX.
 | // This type must not contain any pointers at any level, and be safe to fully fill with u8::MAX.
 | ||||||
| /// A location of an entity in an archetype.
 | /// A location of an entity in an archetype.
 | ||||||
| #[derive(Copy, Clone, Debug)] | #[derive(Copy, Clone, Debug, PartialEq)] | ||||||
| #[repr(C)] | #[repr(C)] | ||||||
| pub struct EntityLocation { | pub struct EntityLocation { | ||||||
|     /// The ID of the [`Archetype`] the [`Entity`] belongs to.
 |     /// The ID of the [`Archetype`] the [`Entity`] belongs to.
 | ||||||
|  | |||||||
| @ -22,8 +22,17 @@ pub struct EntityRef<'w> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'w> EntityRef<'w> { | impl<'w> EntityRef<'w> { | ||||||
|  |     /// # Safety
 | ||||||
|  |     ///
 | ||||||
|  |     ///  - `entity` must be valid for `world`: the generation should match that of the entity at the same index.
 | ||||||
|  |     ///  - `location` must be sourced from `world`'s `Entities` and must exactly match the location for `entity`
 | ||||||
|  |     ///
 | ||||||
|  |     ///  The above is trivially satisfied if `location` was sourced from `world.entities().get(entity)`.
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub(crate) fn new(world: &'w World, entity: Entity, location: EntityLocation) -> Self { |     pub(crate) unsafe fn new(world: &'w World, entity: Entity, location: EntityLocation) -> Self { | ||||||
|  |         debug_assert!(world.entities().contains(entity)); | ||||||
|  |         debug_assert_eq!(world.entities().get(entity), Some(location)); | ||||||
|  | 
 | ||||||
|         Self { |         Self { | ||||||
|             world, |             world, | ||||||
|             entity, |             entity, | ||||||
| @ -193,7 +202,9 @@ impl<'w> EntityRef<'w> { | |||||||
| 
 | 
 | ||||||
| impl<'w> From<EntityMut<'w>> for EntityRef<'w> { | impl<'w> From<EntityMut<'w>> for EntityRef<'w> { | ||||||
|     fn from(entity_mut: EntityMut<'w>) -> EntityRef<'w> { |     fn from(entity_mut: EntityMut<'w>) -> EntityRef<'w> { | ||||||
|         EntityRef::new(entity_mut.world, entity_mut.entity, entity_mut.location) |         // SAFETY: the safety invariants on EntityMut and EntityRef are identical
 | ||||||
|  |         // and EntityMut is promised to be valid by construction.
 | ||||||
|  |         unsafe { EntityRef::new(entity_mut.world, entity_mut.entity, entity_mut.location) } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -206,13 +217,20 @@ pub struct EntityMut<'w> { | |||||||
| 
 | 
 | ||||||
| impl<'w> EntityMut<'w> { | impl<'w> EntityMut<'w> { | ||||||
|     /// # Safety
 |     /// # Safety
 | ||||||
|     /// entity and location _must_ be valid
 |     ///
 | ||||||
|  |     ///  - `entity` must be valid for `world`: the generation should match that of the entity at the same index.
 | ||||||
|  |     ///  - `location` must be sourced from `world`'s `Entities` and must exactly match the location for `entity`
 | ||||||
|  |     ///
 | ||||||
|  |     ///  The above is trivially satisfied if `location` was sourced from `world.entities().get(entity)`.
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub(crate) unsafe fn new( |     pub(crate) unsafe fn new( | ||||||
|         world: &'w mut World, |         world: &'w mut World, | ||||||
|         entity: Entity, |         entity: Entity, | ||||||
|         location: EntityLocation, |         location: EntityLocation, | ||||||
|     ) -> Self { |     ) -> Self { | ||||||
|  |         debug_assert!(world.entities().contains(entity)); | ||||||
|  |         debug_assert_eq!(world.entities().get(entity), Some(location)); | ||||||
|  | 
 | ||||||
|         EntityMut { |         EntityMut { | ||||||
|             world, |             world, | ||||||
|             entity, |             entity, | ||||||
|  | |||||||
| @ -317,7 +317,10 @@ impl World { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn get_entity(&self, entity: Entity) -> Option<EntityRef> { |     pub fn get_entity(&self, entity: Entity) -> Option<EntityRef> { | ||||||
|         let location = self.entities.get(entity)?; |         let location = self.entities.get(entity)?; | ||||||
|         Some(EntityRef::new(self, entity, location)) |         // SAFETY: if the Entity is invalid, the function returns early.
 | ||||||
|  |         // Additionally, Entities::get(entity) returns the correct EntityLocation if the entity exists.
 | ||||||
|  |         let entity_ref = unsafe { EntityRef::new(self, entity, location) }; | ||||||
|  |         Some(entity_ref) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns an [`Entity`] iterator of current entities.
 |     /// Returns an [`Entity`] iterator of current entities.
 | ||||||
| @ -331,13 +334,16 @@ impl World { | |||||||
|                 .iter() |                 .iter() | ||||||
|                 .enumerate() |                 .enumerate() | ||||||
|                 .map(|(archetype_row, archetype_entity)| { |                 .map(|(archetype_row, archetype_entity)| { | ||||||
|  |                     let entity = archetype_entity.entity(); | ||||||
|                     let location = EntityLocation { |                     let location = EntityLocation { | ||||||
|                         archetype_id: archetype.id(), |                         archetype_id: archetype.id(), | ||||||
|                         archetype_row: ArchetypeRow::new(archetype_row), |                         archetype_row: ArchetypeRow::new(archetype_row), | ||||||
|                         table_id: archetype.table_id(), |                         table_id: archetype.table_id(), | ||||||
|                         table_row: archetype_entity.table_row(), |                         table_row: archetype_entity.table_row(), | ||||||
|                     }; |                     }; | ||||||
|                     EntityRef::new(self, archetype_entity.entity(), location) | 
 | ||||||
|  |                     // SAFETY: entity exists and location accurately specifies the archetype where the entity is stored
 | ||||||
|  |                     unsafe { EntityRef::new(self, entity, location) } | ||||||
|                 }) |                 }) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Alice Cecile
						Alice Cecile