summary refs log tree commit diff stats
path: root/src/srce.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/srce.rs')
-rw-r--r--src/srce.rs293
1 files changed, 293 insertions, 0 deletions
diff --git a/src/srce.rs b/src/srce.rs
new file mode 100644
index 0000000..e263ddc
--- /dev/null
+++ b/src/srce.rs
@@ -0,0 +1,293 @@
+// SRCE - Self-referential cell environments
+// Copyright (C) 2022-2023 Soni L.
+// This software is made with love by a queer trans person.
+//
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+//! SRCE is a way to open and close an `LCellOwner`. This provides the full
+//! expressive power of `selfref`, with the ability to use zeroish-cost
+//! `LCell`s in the struct.
+//!
+//! This module requires crate feature `qcell`.
+//!
+//! # Examples
+//!
+//! Funny runtime specialization thing:
+//!
+//! ```rust
+//! #![feature(pin_macro)]
+//! use core::cell::Cell;
+//! use core::marker::PhantomData;
+//! use core::pin::Pin;
+//! use core::pin::pin;
+//!
+//! use qcell::LCell;
+//!
+//! use selfref::srce::LCellEnvironment;
+//!
+//! // Some traits we wanna have
+//! trait Foo {
+//!     fn update(&mut self);
+//! }
+//! trait Bar {
+//!     fn get(&self) -> String;
+//! }
+//!
+//! // Thing that impls multiple of them
+//! struct MyThing;
+//! impl Foo for MyThing {
+//!     fn update(&mut self) {
+//!         println!("hello world!");
+//!     }
+//! }
+//! impl Bar for MyThing {
+//!     fn get(&self) -> String {
+//!         return String::from("hello");
+//!     }
+//! }
+//!
+//! // The container for the above.
+//! // T is gonna become dyn Foo later
+//! struct IntrusiveLCell<'a, T: ?Sized> {
+//!     y: Cell<Option<&'a LCell<'a, dyn Bar>>>,
+//!     x: LCell<'a, T>,
+//! }
+//! // The "key" or "kind" struct for `selfref` to do the magic.
+//! struct IntrusiveLCellKey<T: ?Sized>(PhantomData<T>);
+//! selfref::opaque! {
+//!     impl[T: ?Sized] Opaque for IntrusiveLCellKey<T> {
+//!         type Kind<'this> = IntrusiveLCell<'this, T>;
+//!     }
+//! }
+//!
+//! fn main() {
+//!     // Create the holder
+//!     let mut holder = pin!(
+//!         LCellEnvironment::<IntrusiveLCellKey<MyThing>>::new_with(
+//!             |thing| thing.build(IntrusiveLCell {
+//!                 y: Cell::new(None),
+//!                 x: LCell::new(MyThing),
+//!             })
+//!         )
+//!     );
+//!
+//!     // Create the `&LCell<dyn Bar>`. doesn't actually require rw because of
+//!     // the `Cell`!
+//!     holder.as_ref().open_ro(|_, thing| {
+//!         thing.y.set(Some(&thing.get_ref().x));
+//!     });
+//!
+//!     // Coerce to dyn Foo.
+//!     let mut holder: Pin<&mut LCellEnvironment<IntrusiveLCellKey<dyn Foo>>> = holder;
+//!
+//!     // Run some rw/mut operations on it.
+//!     holder.as_mut().open_rw(|owner, thing| {
+//!         owner.rw(&thing.x).update();
+//!     });
+//!
+//!     // Run some ro/ref operations on it
+//!     holder.as_ref().open_ro(|owner, thing| {
+//!         if let Some(y) = thing.y.get() {
+//!             owner.ro(y).get();
+//!         }
+//!     });
+//! }
+//! ```
+
+use qcell::LCellOwner;
+use crate::{Builder, Holder, Opaque, OperateIn};
+
+use core::pin::Pin;
+
+/// A self-referential environment of `LCell`s, with zero runtime cost.
+///
+/// Wrapper around [`Holder`]. Like `Holder`, the lifetime may be anything.
+///
+/// # Examples
+///
+/// ```rust
+/// use qcell::LCell;
+/// 
+/// use selfref::srce::LCellEnvironment;
+/// use selfref::opaque;
+///
+/// struct Foo<'a> {
+///     foo: LCell<'a, Option<&'a Foo<'a>>>,
+/// }
+///
+/// struct FooKey;
+/// opaque! {
+///     impl Opaque for FooKey {
+///         type Kind<'a> = Foo<'a>;
+///     }
+/// }
+///
+/// fn main() {
+///     // We can use a closure here, but we need to give the compiler a hint.
+///     let holder = LCellEnvironment::<'_, FooKey>::new_with(
+///         |foo| foo.build(Foo { foo: LCell::new(Default::default()) })
+///     );
+/// }
+/// ```
+pub struct LCellEnvironment<'k, T: Opaque + 'k> {
+    inner: Holder<'k, T>,
+}
+
+impl<'k, T> LCellEnvironment<'k, T> where T: Opaque {
+    /// Creates a new `LCellEnvironment`.
+    ///
+    /// Works exactly like [`Holder::new_with`].
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use qcell::LCell;
+    /// 
+    /// use selfref::srce::LCellEnvironment;
+    /// use selfref::opaque;
+    ///
+    /// struct Foo<'a> {
+    ///     foo: LCell<'a, Option<&'a Foo<'a>>>,
+    /// }
+    ///
+    /// struct FooKey;
+    /// opaque! {
+    ///     impl Opaque for FooKey {
+    ///         type Kind<'a> = Foo<'a>;
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     // We can use a closure here, but we need to help the compiler.
+    ///     let holder = LCellEnvironment::<'_, FooKey>::new_with(
+    ///         |foo| foo.build(Foo { foo: LCell::new(Default::default()) })
+    ///     );
+    /// }
+    /// ```
+    pub fn new_with<F>(f: F) -> Self
+    where
+        F: for<'a> FnOnce(&mut Builder<'a, T>),
+        T::Kind<'k>: Sized,
+    {
+        Self {
+            inner: Holder::new_with(f)
+        }
+    }
+}
+
+impl<'k, T> LCellEnvironment<'k, T> where T: Opaque {
+    /// Opens this LCellEnvironment for reading and writing (mutable).
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(pin_macro)]
+    ///
+    /// use qcell::LCell;
+    /// use core::pin::pin;
+    /// 
+    /// use selfref::srce::LCellEnvironment;
+    /// use selfref::opaque;
+    ///
+    /// struct Foo<'a> {
+    ///     foo: LCell<'a, Option<&'a Foo<'a>>>,
+    /// }
+    ///
+    /// struct FooKey;
+    /// opaque! {
+    ///     impl Opaque for FooKey {
+    ///         type Kind<'a> = Foo<'a>;
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     let mut holder = pin!(LCellEnvironment::<'_, FooKey>::new_with(
+    ///         |foo| foo.build(Foo { foo: LCell::new(Default::default()) })
+    ///     ));
+    ///     // Actually making our Foo refer to itself.
+    ///     holder.as_mut().open_rw(
+    ///         |owner, foo| {
+    ///             *owner.rw(&foo.foo) = Some(foo.get_ref());
+    ///         }
+    ///     );
+    /// }
+    /// ```
+    pub fn open_rw<'i, F, R>(self: Pin<&'i mut Self>, f: F) -> R
+    where
+        F: for<'a, 'id> FnOnce(&'a mut LCellOwner<'id>, OperateIn<'id, T>) -> R,
+    {
+        unsafe {
+            self.map_unchecked_mut(|this| &mut this.inner)
+        }.into_ref().operate_in(|env| {
+            // SAFETY? we're still unsure if this is entirely sound!
+            // the LCellOwner is owned by us, and the borrow given to the
+            // closure is mutable if we're mutable, and shared if we're shared.
+            // while this is mixing concerns ('id is being used for 2 different
+            // things), it is also invariant as far as the owner is concerned,
+            // and (for rw access), it is also unique.
+            // thus, we tentatively consider this sound.
+            // see https://users.rust-lang.org/t/soundness-review-request-srce-0-1/87587
+            let guard = unsafe {
+                generativity::Guard::new(generativity::Id::new())
+            };
+            f(&mut LCellOwner::new(guard), env)
+        })
+    }
+
+    /// Opens this LCellEnvironment for reading only (shared).
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(pin_macro)]
+    ///
+    /// use qcell::LCell;
+    /// use core::pin::pin;
+    /// 
+    /// use selfref::srce::LCellEnvironment;
+    /// use selfref::opaque;
+    ///
+    /// struct Foo<'a> {
+    ///     foo: LCell<'a, Option<&'a Foo<'a>>>,
+    /// }
+    ///
+    /// struct FooKey;
+    /// opaque! {
+    ///     impl Opaque for FooKey {
+    ///         type Kind<'a> = Foo<'a>;
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     let holder = pin!(LCellEnvironment::<'_, FooKey>::new_with(
+    ///         |foo| foo.build(Foo { foo: LCell::new(Default::default()) })
+    ///     ));
+    ///     holder.as_ref().open_ro(
+    ///         |owner, foo| {
+    ///             // can't write to it
+    ///             assert!(owner.ro(&foo.foo).is_none());
+    ///         }
+    ///     );
+    /// }
+    /// ```
+    pub fn open_ro<'i, F, R>(self: Pin<&'i Self>, f: F) -> R
+    where
+        F: for<'a, 'id> FnOnce(&'a LCellOwner<'id>, OperateIn<'id, T>) -> R,
+    {
+        unsafe {
+            self.map_unchecked(|this| &this.inner)
+        }.operate_in(|env| {
+            // SAFETY? we're still unsure if this is entirely sound!
+            // the LCellOwner is owned by us, and the borrow given to the
+            // closure is mutable if we're mutable, and shared if we're shared.
+            // while this is mixing concerns ('id is being used for 2 different
+            // things), it is also invariant as far as the owner is concerned.
+            // thus, we tentatively consider this sound.
+            // see https://users.rust-lang.org/t/soundness-review-request-srce-0-1/87587
+            let guard = unsafe {
+                generativity::Guard::new(generativity::Id::new())
+            };
+            f(&LCellOwner::new(guard), env)
+        })
+    }
+}