chiark / gitweb /
organisation
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 14 Nov 2024 22:27:39 +0000 (22:27 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 14 Nov 2024 22:27:39 +0000 (22:27 +0000)
src/lib.rs

index 7ce6970c75f9cdcee8a15bcb60cf2b9423c4244f..bae5e8261b07561bc128e678ec4dbf3aa1aa9b41 100644 (file)
@@ -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<T> {
+    /// # 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<T>`.
+    ///  * All references given out are compatible with the borrow
+    ///   of the `NoAliasSingleton` (possibly via `IsRefToken`
+    ///   or `IsMutToken`.
+    //
+    // XXXX check variance
+    ptr: NonNull<T>,
+}
+
 /// 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<T: Sealed> Sealed for &    T {}
 impl<T: Sealed> 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<T> {
-    /// # 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<T>`.
-    ///  * All references given out are compatible with the borrow
-    ///   of the `NoAliasSingleton` (possibly via `IsRefToken`
-    ///   or `IsMutToken`.
-    //
-    // XXXX check variance
-    ptr: NonNull<T>,
-}
+//---------- principal API for borrowing etc. ----------
 
-impl<T> Clone for Ptr<T> {
-    fn clone(&self) -> Self { *self }
-}
+impl<T> Clone for Ptr<T> { fn clone(&self) -> Self { *self } }
 impl<T> Copy for Ptr<T> {}
 
-impl<T> Debug for Ptr<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_tuple("Ptr").field(&self.ptr).finish()
-    }
-}
-
 impl<T> Ptr<T> {
     pub fn new_heap(t: T) -> Self {
         let b = Box::new(t);
@@ -145,18 +141,6 @@ impl<T> Ptr<T> {
         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<T> {
-    fn as_ptr(self) -> Option<*mut T>;
-}
-impl<T> OptionPtrExt<T> for Option<Ptr<T>> {
-    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<T> {
+    fn as_ptr(self) -> Option<*mut T>;
+}
+impl<T> OptionPtrExt<T> for Option<Ptr<T>> {
+    fn as_ptr(self) -> Option<*mut T> {
+        self.map(|p| p.ptr.as_ptr())
+    }
+}
+
+impl<T> Debug for Ptr<T> {
+    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<T> = Result<T, BorrowConflict>;
+
+pub struct MultiRuntime<'a> {
+    _tok: MutToken<'a>,
+    ref_given: HashSet<NonNull<()>>,
+    mut_given: HashSet<NonNull<()>>,
+}
+
+impl<'a> MultiRuntime<'a> {
+    #[inline]
+    pub fn borrow<'r, T>(&mut self, p: Ptr<T>) -> 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<T>)
+                             -> 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<L: MultiStaticList> 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<L: MultiStaticList> 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<NonNull<()>>,
-    mut_given: HashSet<NonNull<()>>,
-}
-
-impl<'a> MultiRuntime<'a> {
-    #[inline]
-    pub fn borrow<'r, T>(&mut self, p: Ptr<T>) 
-        -> 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<T>)
-        -> 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(())
-    }
-}