Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Use lifetimed, type erased pointers in bevy_ecs #3001

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
feea9e0
Codedump!
TheRawMeatball Oct 19, 2021
40b1366
Ptrify everything
TheRawMeatball Oct 23, 2021
33d1844
appease clippy
TheRawMeatball Oct 23, 2021
6c887ad
Move FetchInit to State
TheRawMeatball Oct 23, 2021
3d936f4
Remove ThinSlicePtr
TheRawMeatball Oct 23, 2021
feb49a3
fix pr
TheRawMeatball Jan 10, 2022
9d1cfe8
fix the pr, properly
TheRawMeatball Jan 10, 2022
ea83de9
moar fixing
TheRawMeatball Jan 10, 2022
0181c36
fix mistake
TheRawMeatball Jan 10, 2022
68b0903
docs
TheRawMeatball Jan 10, 2022
ed70596
hopefully final fix
TheRawMeatball Jan 10, 2022
632c130
Merge remote-tracking branch 'upstream/main' into smallish-ptrification
TheRawMeatball Apr 12, 2022
9b71e0b
fix missing impls
TheRawMeatball Apr 12, 2022
c954cf7
change test expectations
TheRawMeatball Apr 12, 2022
7034c14
silence warning
TheRawMeatball Apr 12, 2022
d13ad78
touch up iter_combinations using newer api s
TheRawMeatball Apr 12, 2022
f3a1a89
partial derive WorldQuery fixes
TheRawMeatball Apr 14, 2022
0ea41a7
Make QueryCombinationIter not generic over fetches so it can use Worl…
TheRawMeatball Apr 14, 2022
3e96ad9
rip out 's
TheRawMeatball Apr 15, 2022
379c614
Update `derive(WorldQuery)` (#40)
BoxyUwU Apr 16, 2022
77a6e15
Merge remote-tracking branch 'upstream/main' into smallish-ptrification
TheRawMeatball Apr 16, 2022
c8801b2
Address review
TheRawMeatball Apr 16, 2022
163dbf4
make test use less ridiculous numbers
TheRawMeatball Apr 16, 2022
36ec230
shrink more tests
TheRawMeatball Apr 16, 2022
9a844f5
revert array map
TheRawMeatball Apr 16, 2022
87dd80f
moar fixing
TheRawMeatball Apr 16, 2022
0908c90
revert changes to replace_unchecked
TheRawMeatball Apr 16, 2022
344bbef
fix caught ub
TheRawMeatball Apr 16, 2022
d72bd23
fix silly mistake
TheRawMeatball Apr 16, 2022
148d598
denoise error
TheRawMeatball Apr 16, 2022
ef248cd
fix incorrect deref
TheRawMeatball Apr 16, 2022
7adc7dd
rip out debug code
TheRawMeatball Apr 16, 2022
75e1fa6
Ptrification changes v2 (#41)
BoxyUwU Apr 17, 2022
c03f937
Use UnsafeCellDeref more
TheRawMeatball Apr 17, 2022
cdb442a
Adress some FIXME s
TheRawMeatball Apr 17, 2022
d78c5aa
fix redundant clone
TheRawMeatball Apr 17, 2022
e7258ea
fix mistake
TheRawMeatball Apr 17, 2022
828e0c6
adress CI errors
TheRawMeatball Apr 17, 2022
40c5ef2
fix docs
TheRawMeatball Apr 17, 2022
6d21f3a
Ptrification fetch filter review (#43)
BoxyUwU Apr 18, 2022
134e4ae
Apply suggestions from code review
TheRawMeatball Apr 19, 2022
b76d47a
ptrfication reviews round... 4(?) (#44)
BoxyUwU Apr 19, 2022
1a9d2b4
Apply suggestions from code review
TheRawMeatball Apr 19, 2022
fa466a0
Merge remote-tracking branch 'upstream/main' into smallish-ptrification
TheRawMeatball Apr 19, 2022
7df4761
Merge branch 'smallish-ptrification' of https://github.com/TheRawMeat…
TheRawMeatball Apr 19, 2022
e857b85
use unwrap_unchecked for sparse fetches
TheRawMeatball Apr 19, 2022
26d7c29
Add and use ThinSlicePtr + inlined OwningPtr::make
TheRawMeatball Apr 19, 2022
fcd4f9a
inline all the things
TheRawMeatball Apr 19, 2022
96c7a92
Fix CI (#45)
BoxyUwU Apr 19, 2022
47d8fa6
Apply suggestions from code review
TheRawMeatball Apr 19, 2022
5bedc37
Add safety docs
TheRawMeatball Apr 19, 2022
53518a4
Merge branch 'smallish-ptrification' of https://github.com/TheRawMeat…
TheRawMeatball Apr 19, 2022
5eec29d
even more docs
TheRawMeatball Apr 19, 2022
156f6bf
simplify doc
TheRawMeatball Apr 19, 2022
691874d
Fix docs
TheRawMeatball Apr 20, 2022
3b7baec
Remove #[repr(transparent)]
TheRawMeatball Apr 20, 2022
2704a34
Merge branch 'main' into smallish-ptrification
TheRawMeatball Apr 22, 2022
57fbb7b
boxy docs
BoxyUwU Apr 22, 2022
0ea1718
Merge branch 'main' into smallish-ptrification
TheRawMeatball Apr 23, 2022
c72047d
Remove lifetime and supertrait on `ReadOnlyFetch` (#47)
BoxyUwU Apr 25, 2022
4b0cf4f
Fix type inference regressions (#48)
BoxyUwU Apr 27, 2022
47024d0
Merge remote-tracking branch 'upstream/main' into smallish-ptrification
TheRawMeatball Apr 27, 2022
e3cc656
couple extra fixes
TheRawMeatball Apr 27, 2022
34167f2
cart hates match bool (#49)
BoxyUwU Apr 27, 2022
18add0c
fun is banned
cart Apr 27, 2022
8bc292e
Remove unnecessary instances of `for<'x>`
cart Apr 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,17 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
self.#field.get_components(&mut func);
});
field_from_components.push(quote! {
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func),
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut func),
});
} else {
field_component_ids.push(quote! {
component_ids.push(components.init_component::<#field_type>(storages));
});
field_get_components.push(quote! {
func((&mut self.#field as *mut #field_type).cast::<u8>());
std::mem::forget(self.#field);
#ecs_path::ptr::OwningPtr::make(self.#field, &mut func);
});
field_from_components.push(quote! {
#field: func().cast::<#field_type>().read(),
#field: func(ctx).inner().cast::<#field_type>().read(),
});
}
}
Expand All @@ -159,14 +158,17 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
}

#[allow(unused_variables, unused_mut, non_snake_case)]
unsafe fn from_components(mut func: impl FnMut() -> *mut u8) -> Self {
unsafe fn from_components<T, F>(ctx: &mut T, mut func: F) -> Self
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
where
F: FnMut(&mut T) -> #ecs_path::ptr::OwningPtr<'_>
{
Self {
#(#field_from_components)*
}
}

#[allow(unused_variables, unused_mut, forget_copy, forget_ref)]
fn get_components(mut self, mut func: impl FnMut(*mut u8)) {
fn get_components(self, mut func: impl FnMut(#ecs_path::ptr::OwningPtr<'_>)) {
#(#field_get_components)*
}
}
Expand Down Expand Up @@ -209,20 +211,20 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! {
impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a latent problem, but this macro assumes it's being run inside of bevy_ecs (for good reason)

Ideally, we'd have a way to document that, but I think the macro should at least be #[doc(hidden)]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but is this relevant in this PR?

where #(#filter::Fetch: FilterFetch,)*
where #(for<'x, 'y> QueryFetch<'x, 'y, #filter>: FilterFetch<'x, 'y>,)*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, the two lifetimes, x and y.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'w and 's are taken, sometimes 'a is also taken, I just wanted something I knew wouldn't cause any conflicts.

{
type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>;
}

// SAFE: All Queries are constrained to ReadOnlyFetch, so World is only read
// SAFE: All Queries are constrained to ReadOnlyQuery, so World is only read
unsafe impl<#(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> ReadOnlySystemParamFetch for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#query::Fetch: ReadOnlyFetch,)* #(#filter::Fetch: FilterFetch,)*
where #(#query: ReadOnlyQuery,)* #(for<'x, 'y> QueryFetch<'x, 'y, #filter>: FilterFetch<'x, 'y>,)*
{ }

// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any QueryState conflicts
// with any prior access, a panic will occur.
unsafe impl<#(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamState for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
where #(for<'x, 'y> QueryFetch<'x, 'y, #filter>: FilterFetch<'x, 'y>,)*
{
type Config = ();
fn init(world: &mut World, system_meta: &mut SystemMeta, config: Self::Config) -> Self {
Expand Down Expand Up @@ -262,7 +264,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
}

impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'w, 's> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
where #(for<'x, 'y> QueryFetch<'x, 'y, #filter>: FilterFetch<'x, 'y>,)*
{
type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>;

Expand All @@ -283,7 +285,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
}

impl<'w, 's, #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
where #(for<'x, 'y> QueryFetch<'x, 'y, #filter>: FilterFetch<'x, 'y>,)*
{
#(#query_fn_mut)*
}
Expand Down
18 changes: 11 additions & 7 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus},
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation},
ptr::OwningPtr,
storage::{SparseSetIndex, SparseSets, Storages, Table},
};
use bevy_ecs_macros::all_tuples;
Expand Down Expand Up @@ -85,14 +86,15 @@ pub unsafe trait Bundle: Send + Sync + 'static {
/// # Safety
/// Caller must return data for each component in the bundle, in the order of this bundle's
/// Components
unsafe fn from_components(func: impl FnMut() -> *mut u8) -> Self
unsafe fn from_components<T, F>(ctx: &mut T, func: F) -> Self
where
F: FnMut(&mut T) -> OwningPtr<'_>,
Self: Sized;

/// Calls `func` on each value, in the order of this bundle's Components. This will
/// "mem::forget" the bundle fields, so callers are responsible for dropping the fields if
/// that is desirable.
fn get_components(self, func: impl FnMut(*mut u8));
fn get_components(self, func: impl FnMut(OwningPtr<'_>));
}

macro_rules! tuple_impl {
Expand All @@ -106,21 +108,23 @@ macro_rules! tuple_impl {

#[allow(unused_variables, unused_mut)]
#[allow(clippy::unused_unit)]
unsafe fn from_components(mut func: impl FnMut() -> *mut u8) -> Self {
unsafe fn from_components<T, F>(ctx: &mut T, mut func: F) -> Self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that the bundle can only live as long as ctx? If we destroy the world, will this cause UB?

Copy link
Member

@alice-i-cecile alice-i-cecile Nov 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just give the Bundle trait a 'world lifetime?

Edit: I don't think so, because we want tuple bundles.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect ctx to exclusively borrow the world?

How would we destroy the world in that case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it doesn't mean bundle can only live as long as ctx and Bundle doesnt need a 'world lifetime. This function is used when removing a bundle from world so we are taking data out of ctx hence the OwningPtr<'_> and moving it into Self to construct the bundle. The only lifetimes involved are for the temporary borrow on world while we are removing the components to construct our bundle from.

where
F: FnMut(&mut T) -> OwningPtr<'_>
{
#[allow(non_snake_case)]
let ($(mut $name,)*) = (
$(func().cast::<$name>(),)*
$(func(ctx).inner().cast::<$name>(),)*
);
($($name.read(),)*)
}

#[allow(unused_variables, unused_mut)]
fn get_components(self, mut func: impl FnMut(*mut u8)) {
fn get_components(self, mut func: impl FnMut(OwningPtr<'_>)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lifetime is unbounded. I suspect we need an HRTB to properly constrain this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected this an anonymous lifetime within the function, i.e. the only assumption the function can make is that it outlives the call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the same as impl for<'a> FnMut(OwningPtr<'a>) its not unbounded. If you look at the error message in this playground link https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1b3f01da9fb82ca83e808a056801a79e you can see it saying it expects the type for<'a> FnOnce(&'a u8)

#[allow(non_snake_case)]
let ($(mut $name,)*) = self;
$(
func((&mut $name as *mut $name).cast::<u8>());
std::mem::forget($name);
OwningPtr::make($name, &mut func);
)*
}
}
Expand Down
22 changes: 17 additions & 5 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Types for declaring and storing [`Component`]s.

use crate::{
ptr::OwningPtr,
storage::{SparseSetIndex, Storages},
system::Resource,
};
Expand Down Expand Up @@ -118,7 +119,7 @@ impl ComponentInfo {
}

#[inline]
pub fn drop(&self) -> unsafe fn(*mut u8) {
pub fn drop(&self) -> unsafe fn(OwningPtr<'_>) {
self.descriptor.drop
}

Expand Down Expand Up @@ -163,7 +164,6 @@ impl SparseSetIndex for ComponentId {
}
}

#[derive(Debug)]
pub struct ComponentDescriptor {
name: String,
// SAFETY: This must remain private. It must match the statically known StorageType of the
Expand All @@ -174,13 +174,25 @@ pub struct ComponentDescriptor {
is_send_and_sync: bool,
type_id: Option<TypeId>,
layout: Layout,
drop: unsafe fn(*mut u8),
drop: for<'a> unsafe fn(OwningPtr<'a>),
}

impl std::fmt::Debug for ComponentDescriptor {
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComponentDescriptor")
.field("name", &self.name)
.field("storage_type", &self.storage_type)
.field("is_send_and_sync", &self.is_send_and_sync)
.field("type_id", &self.type_id)
.field("layout", &self.layout)
.finish()
}
}

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: *mut u8) {
x.cast::<T>().drop_in_place()
unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {
x.inner().cast::<T>().drop_in_place()
}

pub fn new<T: Component>() -> Self {
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod change_detection;
pub mod component;
pub mod entity;
pub mod event;
pub mod ptr;
pub mod query;
#[cfg(feature = "bevy_reflect")]
pub mod reflect;
Expand Down Expand Up @@ -41,6 +42,7 @@ pub mod prelude {
#[cfg(test)]
mod tests {
use crate as bevy_ecs;
use crate::query::QueryFetch;
use crate::{
bundle::Bundle,
component::{Component, ComponentId},
Expand Down Expand Up @@ -886,7 +888,7 @@ mod tests {

fn get_filtered<F: WorldQuery>(world: &mut World) -> Vec<Entity>
where
F::Fetch: FilterFetch,
for<'x, 'y> QueryFetch<'x, 'y, F>: FilterFetch<'x, 'y>,
{
world
.query_filtered::<Entity, F>()
Expand Down
149 changes: 149 additions & 0 deletions crates/bevy_ecs/src/ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use std::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};

/// Type-erased pointer into memory. Guaranteed to be correctly aligned, non-null and safe to read for a particular type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a particular type" could be read as universal here, which is clearly not what is meant.

Not sure how to deal with that though.

Additionally, this does kind of run into the 'ambiguity' of 'read', as ptr::read is consuming, but this means only 'reading', i.e. converting to a shared reference.
We should probably just use that term here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Guaranteed" feels too strong to me, since this must be carefully constructed to be safe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point of these types is that they are supposed to be carefully constructed to uphold these guarantees.

#[derive(Copy, Clone)]
pub struct Ptr<'a>(NonNull<u8>, PhantomData<&'a u8>);
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved

/// Type-erased pointer into memory. Guaranteed to be correctly aligned, non-null and safe to modify for a particular type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly "convert to a unique reference" could be clearer.

pub struct PtrMut<'a>(NonNull<u8>, PhantomData<&'a mut u8>);
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved

/// Type-erased pointer into memory. Guaranteed to be correctly aligned, non-null and safe to move out of for a particular type.
pub struct OwningPtr<'a>(NonNull<u8>, PhantomData<&'a mut u8>);
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved

pub struct ThinSlicePtr<'a, T> {
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
ptr: NonNull<T>,
#[cfg(debug_assertions)]
len: usize,
_marker: PhantomData<&'a [T]>,
}

impl<T> Clone for ThinSlicePtr<'_, T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
#[cfg(debug_assertions)]
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
len: self.len,
_marker: PhantomData,
}
}
}
impl<T> Copy for ThinSlicePtr<'_, T> {}

impl<'a, T> ThinSlicePtr<'a, T> {
pub fn new(slice: &'a [T]) -> Self {
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
unsafe {
Self {
ptr: NonNull::new_unchecked(slice.as_ptr() as *mut _),
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(debug_assertions)]
len: slice.len(),
_marker: PhantomData,
}
}
}

/// # Safety
/// ptr must be valid for the returned lifetime.
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
pub unsafe fn new_raw(ptr: NonNull<T>, #[cfg(debug_assertions)] len: usize) -> Self {
Self {
ptr,
#[cfg(debug_assertions)]
len,
_marker: PhantomData,
}
}

/// # Safety
/// index must not be out of bounds
pub unsafe fn index(self, index: usize) -> &'a T {
debug_assert!(index < self.len);
&*self.ptr.as_ptr().add(index)
}

/// # Safety
/// index must not be out of bounds, and the same element must not be mutably accessed twice.
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
pub unsafe fn index_mut(self, index: usize) -> &'a mut T {
debug_assert!(index < self.len);
&mut *self.ptr.as_ptr().add(index)
}
}

macro_rules! impl_ptr {
($ptr:ident) => {
impl $ptr<'_> {
/// # Safety
/// the offset cannot make the existing ptr null, or take it out of bounds for it's allocation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should be its here (and below)

pub unsafe fn offset(self, count: isize) -> Self {
Self(
NonNull::new_unchecked(self.0.as_ptr().offset(count)),
PhantomData,
)
}

/// # Safety
/// the offset cannot make the existing ptr null, or take it out of bounds for it's allocation.
pub unsafe fn add(self, count: usize) -> Self {
Self(
NonNull::new_unchecked(self.0.as_ptr().add(count)),
PhantomData,
)
}

/// # Safety
/// the lifetime for the returned item must not exceed the lifetime `inner` is valid for
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
pub unsafe fn new(inner: NonNull<u8>) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems extremely dangerous to use due to inferred lifetimes: this is very easy to mess up and will result in UB lifetime extensions if you do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This problem is repeated elsewhere, e.g. on OwningPointer::make.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing that can be done about this, its the same problem as dereferencing a raw pointer creating an unbounded lifetime which has to happen at some point anyway to turn it into a reference.

Self(inner, PhantomData)
}
}
};
}

impl_ptr!(Ptr);
impl<'a> Ptr<'a> {
/// # Safety
/// another PtrMut for the same Ptr shouldn't be created until the first is dropped.
pub unsafe fn assert_unique(self) -> PtrMut<'a> {
PtrMut(self.0, PhantomData)
}

/// # Safety
/// Must point to a valid T
pub unsafe fn deref<T>(self) -> &'a T {
&*self.0.as_ptr().cast()
}
}
impl_ptr!(PtrMut);
impl<'a> PtrMut<'a> {
pub fn inner(self) -> NonNull<u8> {
self.0
}

/// # Safety
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
/// must have right to drop or move out of PtrMut, and current PtrMut should not be accessed again unless it's written to again.
pub unsafe fn promote(self) -> OwningPtr<'a> {
OwningPtr(self.0, PhantomData)
}

/// # Safety
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
/// Must point to a valid T
pub unsafe fn deref_mut<T>(self) -> &'a mut T {
&mut *self.inner().as_ptr().cast()
}
}
impl_ptr!(OwningPtr);
impl<'a> OwningPtr<'a> {
pub fn inner(self) -> *mut u8 {
self.0.as_ptr()
}

pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
let mut temp = MaybeUninit::new(val);
let ptr = unsafe { NonNull::new_unchecked(temp.as_mut_ptr().cast::<u8>()) };
f(Self(ptr, PhantomData))
}

/// # Safety
TheRawMeatball marked this conversation as resolved.
Show resolved Hide resolved
/// must point to a valid T.
pub unsafe fn read<T>(self) -> T {
self.inner().cast::<T>().read()
}
}
Loading