From: Ian Jackson Date: Thu, 14 Nov 2024 22:27:39 +0000 (+0000) Subject: organisation X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=5cfb18db1398d5aaadaffe382521e0b688bfd833;p=manually-boxed organisation --- diff --git a/src/lib.rs b/src/lib.rs index 7ce6970..bae5e82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,61 @@ use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ptr::NonNull; +//---------- public types ---------- + +/// Pointer to manually-memory-managed `T` +/// +/// Ensuring that `Ptr`s are freed at the right time is up to the caller. +/// But aliasing, drop, etc., is (largely) handled by the library. +pub struct Ptr { + /// # SAFETY + /// + /// Invariants (only while valid, but we can rely on them + /// since our caller is not allowed to call any of our methods + /// on an invalid Ptr): + /// + /// * Always points to a valid T on the heap: was `Box`. + /// * All references given out are compatible with the borrow + /// of the `NoAliasSingleton` (possibly via `IsRefToken` + /// or `IsMutToken`. + // + // XXXX check variance + ptr: NonNull, +} + /// Singleton, used for compile-time alias checking #[derive(Debug)] // Not Clone or Copy #[non_exhaustive] // unsafe to construct pub struct NoAliasSingleton { } +/// `&NoAliasSingleton`, but a ZST +/// +/// We don't actually ever need to look at the data for the singleton. +/// So the actual pointer is redundant, and, here, absent. +#[derive(Debug, Clone, Copy)] +pub struct RefToken<'a>( + PhantomData<&'a NoAliasSingleton>, +); + +/// `&mut NoAliasSingleton`, but a ZST +/// +/// See [`RefToken`] +#[derive(Debug)] +pub struct MutToken<'a>( + PhantomData<&'a mut NoAliasSingleton>, +); + +impl<'a> MutToken<'a> { + /// + /// + /// SAFETY + /// + pub unsafe fn new_unchecked() -> Self { MutToken(PhantomData) } +} + +//---------- IsRefToken and IsMutToken traits ---------- + /// Token-like: `&self` implies `&NoAliasSingleton` /// /// Implemented for `NoAliasSingleton` and the ZST reference tokens, @@ -25,31 +74,25 @@ pub unsafe trait IsRefToken<'a>: Sized + Sealed { } } -/// Token-like `&mut self` implies `&mut NoAliasSingleton` +/// Token-like: `&mut self` implies `&mut NoAliasSingleton` /// /// Implemented for `NoAliasSingleton` and `MutToken`, /// and `&mut`-references to them. pub unsafe trait IsMutToken<'a>: IsRefToken<'a> + Sized + Sealed { /// Obtain a new ZST token from something that might be a real object. #[inline] - fn mut_token<'r>(self) -> MutToken<'r> - where 'a: 'r, - { + fn mut_token<'r>(self) -> MutToken<'r> where 'a: 'r { MutToken(PhantomData) } - fn multi_static<'r>(self) -> MultiStatic<'r, ()> - where 'a: 'r, - { + fn multi_static<'r>(self) -> MultiStatic<'r, ()> where 'a: 'r { MultiStatic { tok: self.mut_token(), l: (), } } - fn multi_runtime<'r>(self) -> MultiRuntime<'r> - where 'a: 'r, - { + fn multi_runtime<'r>(self) -> MultiRuntime<'r> where 'a: 'r { MultiRuntime { _tok: self.mut_token(), ref_given: HashSet::new(), @@ -81,58 +124,11 @@ impl Sealed for NoAliasSingleton {} impl Sealed for & T {} impl Sealed for &mut T {} -/// `&NoAliasSingleton`, but a ZST -/// -/// We don't actually ever need to look at the data for the singleton. -/// So the actual pointer is redundant, and, here, absent. -#[derive(Debug, Clone, Copy)] -pub struct RefToken<'a>( - PhantomData<&'a NoAliasSingleton>, -); - -/// `&mut NoAliasSingleton`, but a ZST -/// -/// See [`RefToken`] -#[derive(Debug)] -pub struct MutToken<'a>( - PhantomData<&'a mut NoAliasSingleton>, -); - -impl<'a> MutToken<'a> { - /// - /// - /// SAFETY - /// - pub unsafe fn new_unchecked() -> Self { MutToken(PhantomData) } -} - -pub struct Ptr { - /// # SAFETY - /// - /// Invariants (only while valid, but we can rely on them - /// since our caller is not allowed to call any of our methods - /// on an invalid Ptr): - /// - /// * Always points to a valid T on the heap: was `Box`. - /// * All references given out are compatible with the borrow - /// of the `NoAliasSingleton` (possibly via `IsRefToken` - /// or `IsMutToken`. - // - // XXXX check variance - ptr: NonNull, -} +//---------- principal API for borrowing etc. ---------- -impl Clone for Ptr { - fn clone(&self) -> Self { *self } -} +impl Clone for Ptr { fn clone(&self) -> Self { *self } } impl Copy for Ptr {} -impl Debug for Ptr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Ptr").field(&self.ptr).finish() - } -} - impl Ptr { pub fn new_heap(t: T) -> Self { let b = Box::new(t); @@ -145,18 +141,6 @@ impl Ptr { Ptr { ptr } } -/* - /// SAFETY - /// - /// All copies of the returned `Ptr` become invalid - /// when the borrow of `t` ends. - /// No-one must use them after that. - /// - /// You must not call `deallocate` on the return value. -TODO lifetime is hard - pub unsafe fn new_borrowed<'t>(t: Pin<&'t mut T>) -> Self { todo!() } -*/ - #[inline] pub fn borrow<'a>(self, tok: impl IsRefToken<'a>) -> &'a T { let _ = tok; @@ -219,15 +203,18 @@ TODO lifetime is hard }; *t } -} -pub trait OptionPtrExt { - fn as_ptr(self) -> Option<*mut T>; -} -impl OptionPtrExt for Option> { - fn as_ptr(self) -> Option<*mut T> { - self.map(|p| p.ptr.as_ptr()) - } +/* + /// SAFETY + /// + /// All copies of the returned `Ptr` become invalid + /// when the borrow of `t` ends. + /// No-one must use them after that. + /// + /// You must not call `deallocate` on the return value. +TODO lifetime is hard + pub unsafe fn new_borrowed<'t>(t: Pin<&'t mut T>) -> Self { todo!() } +*/ } impl NoAliasSingleton { @@ -250,48 +237,112 @@ impl NoAliasSingleton { pub unsafe fn init() -> Self { NoAliasSingleton {} } } +//---------- helpful impls ---------- + +pub trait OptionPtrExt { + fn as_ptr(self) -> Option<*mut T>; +} +impl OptionPtrExt for Option> { + fn as_ptr(self) -> Option<*mut T> { + self.map(|p| p.ptr.as_ptr()) + } +} + +impl Debug for Ptr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Ptr").field(&self.ptr).finish() + } +} + +//---------- multi-borrowing, runtime-checked ---------- + #[derive(Debug)] pub struct BorrowConflict; +type BorrowResult = Result; + +pub struct MultiRuntime<'a> { + _tok: MutToken<'a>, + ref_given: HashSet>, + mut_given: HashSet>, +} + +impl<'a> MultiRuntime<'a> { + #[inline] + pub fn borrow<'r, T>(&mut self, p: Ptr) -> BorrowResult<&'r T> + where 'a: 'r + { + self.borrow_inner_check(p.ptr.cast())?; + Ok(unsafe { p.ptr.as_ref() }) + } + + fn borrow_inner_check(&mut self, p: NonNull<()>) -> BorrowResult<()> { + if self.mut_given.contains(&p) { + return Err(BorrowConflict) + } + self.ref_given.insert(p); + Ok(()) + } + + #[inline] + pub fn borrow_mut<'r, T>(&mut self, mut p: Ptr) + -> BorrowResult<&'r mut T> + where 'a: 'r + { + self.borrow_mut_inner_check(p.ptr.cast())?; + Ok(unsafe { p.ptr.as_mut() }) + } + + fn borrow_mut_inner_check(&mut self, p: NonNull<()>) -> BorrowResult<()> { + if self.ref_given.contains(&p) { + return Err(BorrowConflict) + } + if !self.mut_given.insert(p) { + return Err(BorrowConflict) + } + Ok(()) + } +} + +//---------- multi-borrowing ---------- + pub struct MultiStatic<'a, L> { tok: MutToken<'a>, l: L, } -fn forbid_alias(this: *const (), new: NonNull<()>) - -> Result<(), BorrowConflict> -{ +fn forbid_alias(this: *const (), new: NonNull<()>) -> BorrowResult<()> { if this == new.as_ptr() { return Err(BorrowConflict) } Ok(()) } pub unsafe trait MultiStaticList { - fn alias_check_ref(&self, p: NonNull<()>) -> Result<(), BorrowConflict>; - fn alias_check_mut(&self, p: NonNull<()>) -> Result<(), BorrowConflict>; + fn alias_check_ref(&self, p: NonNull<()>) -> BorrowResult<()>; + fn alias_check_mut(&self, p: NonNull<()>) -> BorrowResult<()>; } unsafe impl MultiStaticList for () { - fn alias_check_ref(&self, _: NonNull<()>) -> Result<(), BorrowConflict> { + fn alias_check_ref(&self, _: NonNull<()>) -> BorrowResult<()> { Ok(()) } - fn alias_check_mut(&self, _: NonNull<()>) -> Result<(), BorrowConflict> { + fn alias_check_mut(&self, _: NonNull<()>) -> BorrowResult<()> { Ok(()) } } unsafe impl MultiStaticList for (L, *const ()) { - fn alias_check_ref(&self, p: NonNull<()>) -> Result<(), BorrowConflict> { + fn alias_check_ref(&self, p: NonNull<()>) -> BorrowResult<()> { self.0.alias_check_ref(p) } - fn alias_check_mut(&self, p: NonNull<()>) -> Result<(), BorrowConflict> { + fn alias_check_mut(&self, p: NonNull<()>) -> BorrowResult<()> { forbid_alias(self.1, p)?; self.0.alias_check_mut(p) } } unsafe impl MultiStaticList for (L, NonNull<()>) { - fn alias_check_ref(&self, p: NonNull<()>) -> Result<(), BorrowConflict> { + fn alias_check_ref(&self, p: NonNull<()>) -> BorrowResult<()> { forbid_alias(self.1.as_ptr(), p)?; self.0.alias_check_ref(p) } - fn alias_check_mut(&self, p: NonNull<()>) -> Result<(), BorrowConflict> { + fn alias_check_mut(&self, p: NonNull<()>) -> BorrowResult<()> { forbid_alias(self.1.as_ptr(), p)?; self.0.alias_check_mut(p) } @@ -334,51 +385,3 @@ impl<'a, L: MultiStaticList> MultiStatic<'a, L> { } } } - -pub struct MultiRuntime<'a> { - _tok: MutToken<'a>, - ref_given: HashSet>, - mut_given: HashSet>, -} - -impl<'a> MultiRuntime<'a> { - #[inline] - pub fn borrow<'r, T>(&mut self, p: Ptr) - -> Result<&'r T, BorrowConflict> - where 'a: 'r - { - self.borrow_inner_check(p.ptr.cast())?; - Ok(unsafe { p.ptr.as_ref() }) - } - - fn borrow_inner_check(&mut self, p: NonNull<()>) - -> Result<(), BorrowConflict> - { - if self.mut_given.contains(&p) { - return Err(BorrowConflict) - } - self.ref_given.insert(p); - Ok(()) - } - - #[inline] - pub fn borrow_mut<'r, T>(&mut self, mut p: Ptr) - -> Result<&'r mut T, BorrowConflict> - where 'a: 'r - { - self.borrow_mut_inner_check(p.ptr.cast())?; - Ok(unsafe { p.ptr.as_mut() }) - } - - fn borrow_mut_inner_check(&mut self, p: NonNull<()>) - -> Result<(), BorrowConflict> -{ - if self.ref_given.contains(&p) { - return Err(BorrowConflict) - } - if !self.mut_given.insert(p) { - return Err(BorrowConflict) - } - Ok(()) - } -}