chiark / gitweb /
wip
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 14 Nov 2024 17:59:22 +0000 (17:59 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 14 Nov 2024 21:49:40 +0000 (21:49 +0000)
src/lib.rs

index 436871deb63a1dfba44af0f901116f9c6e807786..d7549d3c6297ec333abb1a57f2e8160d012a91b9 100644 (file)
@@ -1,18 +1,33 @@
 
 #![allow(unused)]
 
+use std::fmt::{self, Debug};
 use std::marker::PhantomData;
 use std::ptr::NonNull;
 
-// Not Clone or Copy
+/// Singleton, used for compile-time alias checking
+#[derive(Debug)] // Not Clone or Copy
+#[non_exhaustive] // unsafe to construct
 pub struct NoAliasSingleton {
-    _unsafe_to_construct: (),
 }
 
+/// Something where `&self` implies `&NoAliasSingleton`
+///
+/// Implemented for `NoAliasSingleton` and the ZST reference tokens,
+/// and `&`-references to them.
 pub unsafe trait IsRefToken<'a> {
-    fn ref_token(&'a self) -> RefToken<'a> { RefToken(PhantomData) }
+    /// Obtain a new ZST token from something that might be a real object.
+    fn ref_token(&'a self) -> RefToken<'a> {
+        RefToken(PhantomData)
+    }
 }
+
+/// Something where `&mut self` implies `&mut NoAliasSingleton`
+///
+/// Implemented for `NoAliasSingleton` and `MutToken`,
+/// and `&mut`-references to them.
 pub unsafe trait IsMutToken<'a>: IsRefToken<'a> {
+    /// Obtain a new ZST token from something that might be a real object.
     fn mut_token<'r, 's>(&'s mut self) -> MutToken<'r>
     where 's: 'r, 'a: 'r,
     {
@@ -32,28 +47,35 @@ where A: IsMutToken<'a>, 'a: 'r, 'aa: 'r {}
 unsafe impl<'r, 'a, 'aa, A> IsMutToken<'r> for &'aa mut A
 where A: IsMutToken<'a>, 'a: 'r, 'aa: 'r {}
 
+/// `&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>{
-    pub fn ref_token(&'a self) -> RefToken<'a> {
-        RefToken(PhantomData)
-    }
-}*/
-
-/*impl<'a> MutToken<'a> {
-    pub fn reborrow<'s: 'a>(&'s mut self) -> MutToken<'a> {
-        todo!()
-    }
-}*/
 
 pub struct Ptr<T> {
-    // invariant over 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>,
 }
@@ -63,8 +85,23 @@ impl<T> Clone for Ptr<T> {
 }
 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 { todo!() }
+    pub fn new_heap(t: T) -> Self {
+        let b = Box::new(t);
+        let ptr = Box::into_raw(b);
+        let ptr = unsafe {
+            // SAFETY
+            // Box.into_raw() guarantees it's not null
+            NonNull::new_unchecked(ptr)
+        };
+        Ptr { ptr }
+    }
 
 /*
     /// SAFETY
@@ -80,12 +117,26 @@ TODO lifetime is hard
 
     #[inline]
     pub fn borrow<'a>(self, tok: impl IsRefToken<'a>) -> &'a T {
-        todo!()
+        unsafe {
+            // SAFETY
+            //
+            // Points to a valid T by our invariant.
+            // There can be no concurrent &mut T
+            // because that would imply a concurrent `&mut NoAliasSingleton`.
+            self.ptr.as_ref()
+        }
     }
 
     #[inline]
-    pub fn borrow_mut<'a>(self, tok: impl IsMutToken<'a>) -> &'a mut T {
-        todo!()
+    pub fn borrow_mut<'a>(mut self, tok: impl IsMutToken<'a>) -> &'a mut T {
+        unsafe {
+            // SAFETY
+            //
+            // Points to a valid T by our invariant.
+            // There can be no other concurrent &T or &mut T
+            // because that would imply a concurrent `&NoAliasSingleton`.
+            self.ptr.as_mut()
+        }
     }
 
     /// Useful if you want to compare pointers, or something
@@ -108,7 +159,27 @@ TODO lifetime is hard
     /// (The compiler will check that no borrows are live.)
     #[inline]
     pub unsafe fn free_heap<'a>(self, tok: impl IsMutToken<'a>) -> T {
-        todo!()
+        let t: Box<T> = unsafe {
+            // SAFETY
+            //
+            // Points to a valid T that came from Box by our invariant.
+            // There can be no other concurrent borrows
+            // (see safety comment for borrow_mut).
+            //
+            // We rely on the caller promising that no-one else
+            // is going to do this.
+            Box::from_raw(self.ptr.as_ptr())
+        };
+        *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())
     }
 }
 
@@ -116,6 +187,8 @@ TODO lifetime is hard
 }*/
 
 impl NoAliasSingleton {
+    /// Initialise by creating the singleton for alias checking
+    ///
     /// # SAFETY
     ///
     /// There must be only one `NoAliasSingleton` used with each `Ptr`.
@@ -130,48 +203,53 @@ impl NoAliasSingleton {
     //
     // I haven't been able to think of a practical safe API,
     // but one only needs about 1 call to init_unchecked.
-    pub unsafe fn init() -> Self { todo!() }
+    pub unsafe fn init() -> Self { NoAliasSingleton {} }
 }
 
-impl<'a> MutToken<'a> {
-/*
-    pub fn multi(&mut self) -> MultiAccessor<(), 0> { todo!() }
-*/
-}
-
-/*
-impl MultiAccessor<'a, R, const N: usize> {
-    pub fn finish(self) -> R { todo!() }
-
-    pub fn borrow<T>(self, p: &mut MutToken
-*/
-
 #[cfg(test)]
 mod tests {
     use super::*;
-    use std::fmt::Write as _;
+    use fmt::Write as _;
 
     mod list {
         use super::*;
 
+        #[derive(Debug)]
         pub struct Node<T> {
             back: Option<Ptr<Node<T>>>,
             next: Option<Ptr<Node<T>>>,
             pub data: T,
         }
 
-        pub struct List<T> {
+        #[derive(Debug)]
+        pub struct List<T: Debug> {
             head: Option<Ptr<Node<T>>>,
             tail: Option<Ptr<Node<T>>>,
             noalias: NoAliasSingleton,
         }
 
-        impl<T> List<T> {
+        impl<T: Debug> List<T> {
             pub fn new() -> Self {
                 let noalias = unsafe { NoAliasSingleton::init() };
                 List { head: None, tail: None, noalias }
             }
 
+            pub fn check_consistency(&self) {
+                dbg!(self);
+                let tok = self.noalias.ref_token();
+                let mut last: Option<Ptr<Node<T>>> = None;
+                let mut node = self.head;
+                while let Some(node_) = node {
+                    dbg!(node_);
+                    let node__ = node_.borrow(&tok);
+                    dbg!(node__);
+                    assert_eq!(last.as_ptr(), node__.back.as_ptr());
+                    last = Some(node_);
+                    node = node__.next;
+                }
+                assert_eq!(self.tail.as_ptr(), last.as_ptr());
+            }
+
             pub fn append(&mut self, data: T) {
                 let node = Node {
                     back: self.head,
@@ -179,8 +257,12 @@ mod tests {
                     data,
                 };
                 let node = Ptr::new_heap(node);
+                if let Some(old_tail) = self.tail {
+                    old_tail.borrow_mut(&mut self.noalias).next = Some(node);
+                } else {
+                    self.head = Some(node);
+                };
                 self.tail = Some(node);
-                if self.head.is_none() { self.head = Some(node) };
             }
 
             pub fn front(&self) -> Option<&T> {
@@ -224,7 +306,7 @@ mod tests {
             }
         }
 
-        impl<T> Drop for List<T> {
+        impl<T: Debug> Drop for List<T> {
             fn drop(&mut self) {
                 while let Some(_) = Self::pop_front_inner(
                     self.noalias.mut_token(),
@@ -239,15 +321,21 @@ mod tests {
     fn demo() {
         use list::List;
 
+        let s = |s: &str| format!("{s}");
+
         let l = List::<String>::new();
+        l.check_consistency();
         assert_eq!(l.front(), None);
         drop(l);
 
-        let mut l = List::new();
-        l.append(format!("hi"));
-        l.append(format!("ho"));
+        let mut l = List::new();     l.check_consistency();
+        l.append(s("hi"));           l.check_consistency();
+        l.append(s("ho"));           l.check_consistency();
+
         write!(l.front_mut().unwrap(), "!").unwrap();
-        assert_eq!(l.front(), Some(&format!("hi!")));
+        assert_eq!(l.pop_front(), Some(s("hi!")));  l.check_consistency();
+        assert_eq!(l.front(), Some(&s("ho")));      l.check_consistency();
+
         drop(l);
     }
 }