Add safe constructors for untyped pointers Ptr and PtrMut (#6539)

# Objective

Currently, `Ptr` and `PtrMut` can only be constructed via unsafe code. This means that downgrading a reference to an untyped pointer is very cumbersome, despite being a very simple operation.

## Solution

Define conversions for easily and safely constructing untyped pointers. This is the non-owned counterpart to `OwningPtr::make`.

Before:

```rust
let ptr = unsafe { PtrMut::new(NonNull::from(&mut value).cast()) };
```

After:

```rust
let ptr = PtrMut::from(&mut value);
```


Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
JoJoJet 2022-11-14 22:53:50 +00:00
parent 635320f172
commit f2f8f9097f
2 changed files with 31 additions and 15 deletions

View File

@ -3,7 +3,7 @@
#![warn(missing_docs)] #![warn(missing_docs)]
use core::{ use core::{
cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, num::NonZeroUsize, ptr::NonNull, cell::UnsafeCell, marker::PhantomData, mem::ManuallyDrop, num::NonZeroUsize, ptr::NonNull,
}; };
/// Type-erased borrow of some unknown type chosen when constructing this type. /// Type-erased borrow of some unknown type chosen when constructing this type.
@ -98,6 +98,9 @@ macro_rules! impl_ptr {
} }
impl_ptr!(Ptr); impl_ptr!(Ptr);
impl_ptr!(PtrMut);
impl_ptr!(OwningPtr);
impl<'a> Ptr<'a> { impl<'a> Ptr<'a> {
/// Transforms this [`Ptr`] into an [`PtrMut`] /// Transforms this [`Ptr`] into an [`PtrMut`]
/// ///
@ -127,7 +130,16 @@ impl<'a> Ptr<'a> {
self.0.as_ptr() self.0.as_ptr()
} }
} }
impl_ptr!(PtrMut);
impl<'a, T> From<&'a T> for Ptr<'a> {
#[inline]
fn from(val: &'a T) -> Self {
// SAFETY: The returned pointer has the same lifetime as the passed reference.
// Access is immutable.
unsafe { Self::new(NonNull::from(val).cast()) }
}
}
impl<'a> PtrMut<'a> { impl<'a> PtrMut<'a> {
/// Transforms this [`PtrMut`] into an [`OwningPtr`] /// Transforms this [`PtrMut`] into an [`OwningPtr`]
/// ///
@ -157,15 +169,24 @@ impl<'a> PtrMut<'a> {
self.0.as_ptr() self.0.as_ptr()
} }
} }
impl_ptr!(OwningPtr);
impl<'a, T> From<&'a mut T> for PtrMut<'a> {
#[inline]
fn from(val: &'a mut T) -> Self {
// SAFETY: The returned pointer has the same lifetime as the passed reference.
// The reference is mutable, and thus will not alias.
unsafe { Self::new(NonNull::from(val).cast()) }
}
}
impl<'a> OwningPtr<'a> { impl<'a> OwningPtr<'a> {
/// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen. /// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen.
#[inline] #[inline]
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R { pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
let mut temp = MaybeUninit::new(val); let mut temp = ManuallyDrop::new(val);
// SAFETY: `temp.as_mut_ptr()` is a reference to a local value on the stack, so it cannot be null // SAFETY: The value behind the pointer will not get dropped or observed later,
let ptr = unsafe { NonNull::new_unchecked(temp.as_mut_ptr().cast::<u8>()) }; // so it's safe to promote it to an owning pointer.
f(Self(ptr, PhantomData)) f(unsafe { PtrMut::from(&mut *temp).promote() })
} }
/// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`. /// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`.

View File

@ -475,7 +475,7 @@ impl<T: for<'a> Deserialize<'a> + Reflect> FromType<T> for ReflectDeserialize {
/// type_registry.register::<Reflected>(); /// type_registry.register::<Reflected>();
/// ///
/// let mut value = Reflected("Hello world!".to_string()); /// let mut value = Reflected("Hello world!".to_string());
/// let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) }; /// let value = Ptr::from(&value);
/// ///
/// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap(); /// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap();
/// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap(); /// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();
@ -534,8 +534,6 @@ impl<T: Reflect> FromType<T> for ReflectFromPtr {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::ptr::NonNull;
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration}; use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
use bevy_ptr::{Ptr, PtrMut}; use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::HashMap; use bevy_utils::HashMap;
@ -560,8 +558,7 @@ mod test {
let mut value = Foo { a: 1.0 }; let mut value = Foo { a: 1.0 };
{ {
// SAFETY: lifetime doesn't outlive original value, access is unique let value = PtrMut::from(&mut value);
let value = unsafe { PtrMut::new(NonNull::from(&mut value).cast()) };
// SAFETY: reflect_from_ptr was constructed for the correct type // SAFETY: reflect_from_ptr was constructed for the correct type
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr_mut(value) }; let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr_mut(value) };
match dyn_reflect.reflect_mut() { match dyn_reflect.reflect_mut() {
@ -573,10 +570,8 @@ mod test {
} }
{ {
// SAFETY: lifetime doesn't outlive original value
let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
// SAFETY: reflect_from_ptr was constructed for the correct type // SAFETY: reflect_from_ptr was constructed for the correct type
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr(value) }; let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr(Ptr::from(&value)) };
match dyn_reflect.reflect_ref() { match dyn_reflect.reflect_ref() {
bevy_reflect::ReflectRef::Struct(strukt) => { bevy_reflect::ReflectRef::Struct(strukt) => {
let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap(); let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap();