Pointerfication followup: Type safety and cleanup (#4621)
# Objective The `Ptr` types gives free access to the underlying `NonNull<u8>`, which adds more publicly visible pointer wrangling than there needs to be. There are also a few edge cases where Ptr types could be more readily utilized for properly validating the soundness of ECS operations. ## Solution - Replace `*Ptr(Mut)::inner` with `cast` which requires a concrete type to give the pointer. This function could also have a `debug_assert` with an alignment check to ensure that the pointer is aligned properly, but is currently not included. - Use `OwningPtr::read` in ECS macros over casting the inner pointer around.
This commit is contained in:
		
							parent
							
								
									4a9932fa8e
								
							
						
					
					
						commit
						3e24b725af
					
				| @ -134,7 +134,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { | ||||
|                 #ecs_path::ptr::OwningPtr::make(self.#field, &mut func); | ||||
|             }); | ||||
|             field_from_components.push(quote! { | ||||
|                 #field: func(ctx).inner().as_ptr().cast::<#field_type>().read(), | ||||
|                 #field: func(ctx).read::<#field_type>(), | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -112,10 +112,7 @@ macro_rules! tuple_impl { | ||||
|                 F: FnMut(&mut T) -> OwningPtr<'_> | ||||
|             { | ||||
|                 #[allow(non_snake_case)] | ||||
|                 let ($(mut $name,)*) = ( | ||||
|                     $(func(ctx).inner().cast::<$name>(),)* | ||||
|                 ); | ||||
|                 ($($name.as_ptr().read(),)*) | ||||
|                 ($(func(ctx).read::<$name>(),)*) | ||||
|             } | ||||
| 
 | ||||
|             #[allow(unused_variables, unused_mut)] | ||||
|  | ||||
| @ -186,7 +186,7 @@ impl std::fmt::Debug for ComponentDescriptor { | ||||
| impl ComponentDescriptor { | ||||
|     // SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
 | ||||
|     unsafe fn drop_ptr<T>(x: OwningPtr<'_>) { | ||||
|         x.inner().cast::<T>().as_ptr().drop_in_place() | ||||
|         x.drop_as::<T>() | ||||
|     } | ||||
| 
 | ||||
|     pub fn new<T: Component>() -> Self { | ||||
|  | ||||
| @ -43,57 +43,86 @@ pub struct OwningPtr<'a>(NonNull<u8>, PhantomData<&'a mut u8>); | ||||
| macro_rules! impl_ptr { | ||||
|     ($ptr:ident) => { | ||||
|         impl $ptr<'_> { | ||||
|             /// Calculates the offset from a pointer.
 | ||||
|             /// As the pointer is type-erased, there is no size information available. The provided
 | ||||
|             /// `count` parameter is in raw bytes.
 | ||||
|             ///
 | ||||
|             /// *See also: [`ptr::offset`][ptr_offset]*
 | ||||
|             ///
 | ||||
|             /// # Safety
 | ||||
|             /// the offset cannot make the existing ptr null, or take it out of bounds for its allocation.
 | ||||
|             ///
 | ||||
|             /// [ptr_offset]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
 | ||||
|             #[inline] | ||||
|             pub unsafe fn offset(self, count: isize) -> Self { | ||||
|                 Self( | ||||
|                     NonNull::new_unchecked(self.0.as_ptr().offset(count)), | ||||
|                     NonNull::new_unchecked(self.as_ptr().offset(count)), | ||||
|                     PhantomData, | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
 | ||||
|             /// As the pointer is type-erased, there is no size information available. The provided
 | ||||
|             /// `count` parameter is in raw bytes.
 | ||||
|             ///
 | ||||
|             /// *See also: [`ptr::add`][ptr_add]*
 | ||||
|             ///
 | ||||
|             /// # Safety
 | ||||
|             /// the offset cannot make the existing ptr null, or take it out of bounds for its allocation.
 | ||||
|             ///
 | ||||
|             /// [ptr_add]: https://doc.rust-lang.org/std/primitive.pointer.html#method.add
 | ||||
|             #[inline] | ||||
|             pub unsafe fn add(self, count: usize) -> Self { | ||||
|                 Self( | ||||
|                     NonNull::new_unchecked(self.0.as_ptr().add(count)), | ||||
|                     NonNull::new_unchecked(self.as_ptr().add(count)), | ||||
|                     PhantomData, | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             /// # Safety
 | ||||
|             /// Creates a new instance from a raw pointer.
 | ||||
|             ///
 | ||||
|             /// # Safety
 | ||||
|             /// The lifetime for the returned item must not exceed the lifetime `inner` is valid for
 | ||||
|             #[inline] | ||||
|             pub unsafe fn new(inner: NonNull<u8>) -> Self { | ||||
|                 Self(inner, PhantomData) | ||||
|             } | ||||
| 
 | ||||
|             #[inline] | ||||
|             pub fn inner(&self) -> NonNull<u8> { | ||||
|                 self.0 | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl_ptr!(Ptr); | ||||
| impl<'a> Ptr<'a> { | ||||
|     /// # Safety
 | ||||
|     /// Transforms this [`Ptr`] into an [`PtrMut`]
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     /// Another [`PtrMut`] for the same [`Ptr`] must not be created until the first is dropped.
 | ||||
|     #[inline] | ||||
|     pub unsafe fn assert_unique(self) -> PtrMut<'a> { | ||||
|         PtrMut(self.0, PhantomData) | ||||
|     } | ||||
| 
 | ||||
|     /// Transforms this [`Ptr<T>`] into a `&T` with the same lifetime
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     /// Must point to a valid `T`
 | ||||
|     #[inline] | ||||
|     pub unsafe fn deref<T>(self) -> &'a T { | ||||
|         &*self.0.as_ptr().cast() | ||||
|         &*self.as_ptr().cast() | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the underlying pointer, erasing the associated lifetime.
 | ||||
|     ///
 | ||||
|     /// If possible, it is strongly encouraged to use [`deref`](Self::deref) over this function,
 | ||||
|     /// as it retains the lifetime.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     /// All subsequent operations to the returned pointer must be valid inside the
 | ||||
|     /// associated lifetime.
 | ||||
|     #[inline] | ||||
|     #[allow(clippy::wrong_self_convention)] | ||||
|     pub unsafe fn as_ptr(self) -> *mut u8 { | ||||
|         self.0.as_ptr() | ||||
|     } | ||||
| } | ||||
| impl_ptr!(PtrMut); | ||||
| @ -113,7 +142,21 @@ impl<'a> PtrMut<'a> { | ||||
|     /// Must point to a valid `T`
 | ||||
|     #[inline] | ||||
|     pub unsafe fn deref_mut<T>(self) -> &'a mut T { | ||||
|         &mut *self.inner().as_ptr().cast() | ||||
|         &mut *self.as_ptr().cast() | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the underlying pointer, erasing the associated lifetime.
 | ||||
|     ///
 | ||||
|     /// If possible, it is strongly encouraged to use [`deref_mut`](Self::deref_mut) over
 | ||||
|     /// this function, as it retains the lifetime.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     /// All subsequent operations to the returned pointer must be valid inside the
 | ||||
|     /// associated lifetime.
 | ||||
|     #[inline] | ||||
|     #[allow(clippy::wrong_self_convention)] | ||||
|     pub unsafe fn as_ptr(self) -> *mut u8 { | ||||
|         self.0.as_ptr() | ||||
|     } | ||||
| } | ||||
| impl_ptr!(OwningPtr); | ||||
| @ -132,7 +175,30 @@ impl<'a> OwningPtr<'a> { | ||||
|     /// Must point to a valid `T`.
 | ||||
|     #[inline] | ||||
|     pub unsafe fn read<T>(self) -> T { | ||||
|         self.inner().as_ptr().cast::<T>().read() | ||||
|         self.as_ptr().cast::<T>().read() | ||||
|     } | ||||
| 
 | ||||
|     //// Consumes the [`OwningPtr`] to drop the underlying data of type `T`.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     /// Must point to a valid `T`.
 | ||||
|     #[inline] | ||||
|     pub unsafe fn drop_as<T>(self) { | ||||
|         self.as_ptr().cast::<T>().drop_in_place() | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the underlying pointer, erasing the associated lifetime.
 | ||||
|     ///
 | ||||
|     /// If possible, it is strongly encouraged to use the other more type-safe functions
 | ||||
|     /// over this function.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     /// All subsequent operations to the returned pointer must be valid inside the
 | ||||
|     /// associated lifetime.
 | ||||
|     #[inline] | ||||
|     #[allow(clippy::wrong_self_convention)] | ||||
|     pub unsafe fn as_ptr(self) -> *mut u8 { | ||||
|         self.0.as_ptr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -101,7 +101,7 @@ impl BlobVec { | ||||
|                 std::alloc::alloc(new_layout) | ||||
|             } else { | ||||
|                 std::alloc::realloc( | ||||
|                     self.get_ptr_mut().inner().as_ptr(), | ||||
|                     self.get_ptr_mut().as_ptr(), | ||||
|                     array_layout(&self.item_layout, self.capacity) | ||||
|                         .expect("array layout should be valid"), | ||||
|                     new_layout.size(), | ||||
| @ -121,11 +121,7 @@ impl BlobVec { | ||||
|     pub unsafe fn initialize_unchecked(&mut self, index: usize, value: OwningPtr<'_>) { | ||||
|         debug_assert!(index < self.len()); | ||||
|         let ptr = self.get_unchecked_mut(index); | ||||
|         std::ptr::copy_nonoverlapping::<u8>( | ||||
|             value.inner().as_ptr(), | ||||
|             ptr.inner().as_ptr(), | ||||
|             self.item_layout.size(), | ||||
|         ); | ||||
|         std::ptr::copy_nonoverlapping::<u8>(value.as_ptr(), ptr.as_ptr(), self.item_layout.size()); | ||||
|     } | ||||
| 
 | ||||
|     /// # Safety
 | ||||
| @ -142,15 +138,11 @@ impl BlobVec { | ||||
|         // in the collection), so we get a double drop. To prevent that, we set len to 0 until we're
 | ||||
|         // done.
 | ||||
|         let old_len = self.len; | ||||
|         let ptr = self.get_unchecked_mut(index).promote().inner(); | ||||
|         let ptr = self.get_unchecked_mut(index).promote().as_ptr(); | ||||
|         self.len = 0; | ||||
|         // Drop the old value, then write back, justifying the promotion
 | ||||
|         (self.drop)(OwningPtr::new(ptr)); | ||||
|         std::ptr::copy_nonoverlapping::<u8>( | ||||
|             value.inner().as_ptr(), | ||||
|             ptr.as_ptr(), | ||||
|             self.item_layout.size(), | ||||
|         ); | ||||
|         (self.drop)(OwningPtr::new(NonNull::new_unchecked(ptr))); | ||||
|         std::ptr::copy_nonoverlapping::<u8>(value.as_ptr(), ptr, self.item_layout.size()); | ||||
|         self.len = old_len; | ||||
|     } | ||||
| 
 | ||||
| @ -192,13 +184,13 @@ impl BlobVec { | ||||
|         let last = self.len - 1; | ||||
|         let swap_scratch = self.swap_scratch.as_ptr(); | ||||
|         std::ptr::copy_nonoverlapping::<u8>( | ||||
|             self.get_unchecked_mut(index).inner().as_ptr(), | ||||
|             self.get_unchecked_mut(index).as_ptr(), | ||||
|             swap_scratch, | ||||
|             self.item_layout.size(), | ||||
|         ); | ||||
|         std::ptr::copy::<u8>( | ||||
|             self.get_unchecked_mut(last).inner().as_ptr(), | ||||
|             self.get_unchecked_mut(index).inner().as_ptr(), | ||||
|             self.get_unchecked_mut(last).as_ptr(), | ||||
|             self.get_unchecked_mut(index).as_ptr(), | ||||
|             self.item_layout.size(), | ||||
|         ); | ||||
|         self.len -= 1; | ||||
| @ -280,7 +272,7 @@ impl Drop for BlobVec { | ||||
|             array_layout(&self.item_layout, self.capacity).expect("array layout should be valid"); | ||||
|         if array_layout.size() > 0 { | ||||
|             unsafe { | ||||
|                 std::alloc::dealloc(self.get_ptr_mut().inner().as_ptr(), array_layout); | ||||
|                 std::alloc::dealloc(self.get_ptr_mut().as_ptr(), array_layout); | ||||
|                 std::alloc::dealloc(self.swap_scratch.as_ptr(), self.item_layout); | ||||
|             } | ||||
|         } | ||||
| @ -350,7 +342,7 @@ mod tests { | ||||
| 
 | ||||
|     // SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
 | ||||
|     unsafe fn drop_ptr<T>(x: OwningPtr<'_>) { | ||||
|         x.inner().cast::<T>().as_ptr().drop_in_place() | ||||
|         x.drop_as::<T>() | ||||
|     } | ||||
| 
 | ||||
|     /// # Safety
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 James Liu
						James Liu