#![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,
{
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>,
}
}
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
#[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
/// (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())
}
}
}*/
impl NoAliasSingleton {
+ /// Initialise by creating the singleton for alias checking
+ ///
/// # SAFETY
///
/// There must be only one `NoAliasSingleton` used with each `Ptr`.
//
// 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,
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> {
}
}
- 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(),
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);
}
}