diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2022-10-08 10:26:56 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2022-10-08 10:26:56 -0300 |
commit | 26d91dee181d6b95b2f864407bcde00c04b7b81d (patch) | |
tree | 0d2c7e799488be7ddda0d0e0b03d3522f2be089e | |
parent | ec276d652c4296a8de3790ea0dc8043e4547841c (diff) |
Fix support for ?Sized
-rw-r--r-- | Cargo.toml | 10 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | src/lib.rs | 120 |
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); }); } } |