summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2022-10-08 10:26:56 -0300
committerSoniEx2 <endermoneymod@gmail.com>2022-10-08 10:26:56 -0300
commit26d91dee181d6b95b2f864407bcde00c04b7b81d (patch)
tree0d2c7e799488be7ddda0d0e0b03d3522f2be089e
parentec276d652c4296a8de3790ea0dc8043e4547841c (diff)
Fix support for ?Sized
-rw-r--r--Cargo.toml10
-rw-r--r--README.md4
-rw-r--r--src/lib.rs120
3 files changed, 124 insertions, 10 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c4f45ae..61b930a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,19 @@
 [package]
 name = "selfref"
-version = "0.2.2"
+version = "0.2.3"
 authors = ["SoniEx2 <endermoneymod@gmail.com>"]
 edition = "2021"
-description = "Pain-free self-referential pinned types"
+description = "Semi-pain-free self-referential pinned types"
 license = "MIT OR Apache-2.0"
 repository = "https://soniex2.autistic.space/git-repos/selfref.git"
 homepage = "https://ganarchy.autistic.space/project/cc8ab1441fd398d23454c149c74bd5a2304eb566"
+categories = ["no-std", "rust-patterns"]
+readme = "README.md"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
+[features]
+nightly = []
+alloc = []
+
 [dependencies]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6a88df5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+selfref
+=======
+
+Please refer to the top-level documentation for this crate instead.
diff --git a/src/lib.rs b/src/lib.rs
index c321880..b6e28fd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,7 @@
 // SPDX-License-Identifier: MIT OR Apache-2.0
 
 #![no_std]
+#![cfg_attr(feature="nightly", feature(dropck_eyepatch))]
 
 //! Pain-free self-referential/recursively-referential pinned types.
 //!
@@ -178,11 +179,67 @@
 //!     holder.as_ref().operate_in(SetFooRef);
 //! }
 //! ```
+//!
+//! # Features
+//!
+//! Due to [PhantomData is unsound](https://github.com/rust-lang/rust/issues/102810)
+//! we currently require the following features for `T: ?Sized` support in
+//! `selfref::opaque!`:
+//!
+//! - `alloc` - `selfref::opaque!` for `T: ?Sized` is provided by `Box`.
+//! - `nightly` - `selfref::opaque!` for `T: ?Sized` is provided by a *wrapper*
+//!     around `PhantomData`, which works around the above issue. we call this
+//!     "PhantomDrop".
+//!
+//! When enabling both features, `nightly` takes over and we use the wrapper
+//! always. This doesn't make a practical difference since the generated UB
+//! check is dead code anyway, but `PhantomDrop` should be lighter on compile
+//! times.
+//!
+//! If not using either feature, `T: ?Sized` support requires `unsafe`ly
+//! implementing `Opaque`.
+//!
+//! Note that we do **not** enable any features by default! We assume most
+//! folks aren't coming to this crate for its `T: ?Sized` support, so these are
+//! the best defaults for crates to depend on. If they do need the `?Sized`
+//! support they can just enable one of these (probably `alloc`).
 
 use core::marker::PhantomPinned;
 use core::pin::Pin;
 use core::mem;
 
+// there's no sound way to dropck T: ?Sized without either alloc or nightly.
+//
+// so we just have the user opt-in to alloc or nightly as desired.
+//
+// when using alloc, we use Box<T> for UBCheck.
+//
+// when using nightly, we use our custom PhantomDrop<T> for UBCheck.
+//
+// when using neither, we just error on T: ?Sized and require a manual unsafe
+// impl of Opaque.
+
+#[cfg(all(feature="alloc", not(feature="nightly")))]
+extern crate alloc;
+
+#[cfg(all(feature="alloc", not(feature="nightly")))]
+#[doc(hidden)]
+pub use alloc::boxed::Box as UBCheck;
+
+#[cfg(feature="nightly")]
+#[doc(hidden)]
+pub struct UBCheck<T: ?Sized>(core::marker::PhantomData<T>);
+
+#[cfg(all(not(feature="alloc"), not(feature="nightly")))]
+#[doc(hidden)]
+pub struct UBCheck<T>(T);
+
+#[cfg(feature="nightly")]
+// SAFETY: dropck's like a Box<T>, but is no-alloc friendly.
+unsafe impl<#[may_dangle] T: ?Sized> Drop for UBCheck<T> {
+    fn drop(&mut self) {}
+}
+
 /// An opaqueified self-referential struct "key".
 ///
 /// # Safety
@@ -197,6 +254,53 @@ use core::mem;
 ///     time of writing this, and relies on Rust not having lifetime
 ///     specialization.
 ///
+/// It's recommended to use the `selfref::opaque!` macro instead, which
+/// enforces these invariants. For example, this doesn't compile:
+///
+/// ```rust compile_fail
+/// use std::cell::Cell;
+/// use selfref::opaque;
+///
+/// struct Foo<'a> {
+///     foo: Cell<Option<&'a Foo<'a>>>,
+/// }
+///
+/// impl<'a> Drop for Foo<'a> {
+///     fn drop(&mut self) {
+///     }
+/// }
+///
+/// struct FooKey;
+/// opaque! {
+///     impl Opaque for FooKey {
+///         type Kind<'a> = Foo<'a>;
+///     }
+/// }
+/// ```
+///
+/// But by removing the `Drop` impl, it compiles:
+///
+/// ```rust
+/// use std::cell::Cell;
+/// use selfref::opaque;
+///
+/// struct Foo<'a> {
+///     foo: Cell<Option<&'a Foo<'a>>>,
+/// }
+///
+/// //impl<'a> Drop for Foo<'a> {
+/// //    fn drop(&mut self) {
+/// //    }
+/// //}
+///
+/// struct FooKey;
+/// opaque! {
+///     impl Opaque for FooKey {
+///         type Kind<'a> = Foo<'a>;
+///     }
+/// }
+/// ```
+///
 /// # Examples
 ///
 /// ```rust
@@ -285,14 +389,14 @@ macro_rules! opaque {
             fn ub_check() {
                 fn ub_detect_helper(
                     _f: impl ::core::ops::Fn(
-                        for<$l> fn([&$l (); 0]) -> $kind,
-                        for<$l> fn(&$l $kind)
+                        for<$l> fn([&$l (); 0]) -> $crate::UBCheck<$kind>,
+                        for<$l> fn(&$l $crate::UBCheck<$kind>)
                     )
                 ) $(where $($bounds)*)? {
                 }
                 ub_detect_helper(|f, g| {
-                    let _foo: Self::Kind<'_> = f([]);
-                    g(&_foo);
+                    let x: $crate::UBCheck<Self::Kind<'_>> = f([]);
+                    g(&x);
                 });
             }
         }
@@ -308,14 +412,14 @@ macro_rules! opaque {
             fn ub_check() {
                 fn ub_detect_helper<$($params)+>(
                     _f: impl ::core::ops::Fn(
-                        for<$l> fn([&$l (); 0]) -> $kind,
-                        for<$l> fn(&$l $kind)
+                        for<$l> fn([&$l (); 0]) -> $crate::UBCheck<$kind>,
+                        for<$l> fn(&$l $crate::UBCheck<$kind>)
                     )
                 ) $(where $($bounds)*)? {
                 }
                 ub_detect_helper(|f, g| {
-                    let _foo: Self::Kind<'_> = f([]);
-                    g(&_foo);
+                    let x: $crate::UBCheck<Self::Kind<'_>> = f([]);
+                    g(&x);
                 });
             }
         }