From 566ceddef8fedeb05fa83a0659425c07c352f9ef Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Fri, 10 Nov 2023 18:26:26 +0100 Subject: [PATCH] Create version 0.10.3 re-exporting version 1 --- Cargo.toml | 9 +- README.md | 7 + src/lib.rs | 517 +----------- src/unsafe_self_cell.rs | 230 ------ tests-extra/Cargo.toml | 12 - tests-extra/README.md | 1 - tests-extra/invalid/contravariant_owner.rs | 32 - .../invalid/contravariant_owner.stderr | 14 - ...covariant_owner_non_covariant_dependent.rs | 23 - ...riant_owner_non_covariant_dependent.stderr | 28 - tests-extra/invalid/escape_dependent.rs | 28 - tests-extra/invalid/escape_dependent.stderr | 24 - tests-extra/invalid/leak_dependent.rs | 19 - tests-extra/invalid/leak_dependent.stderr | 9 - tests-extra/invalid/leak_outside_ref.rs | 24 - tests-extra/invalid/leak_outside_ref.stderr | 14 - .../invalid/reborrow_dependent_cyclic.rs | 34 - .../invalid/reborrow_dependent_cyclic.stderr | 33 - tests-extra/invalid/swap_cell_member.rs | 37 - tests-extra/invalid/swap_cell_member.stderr | 8 - tests-extra/invalid/with_mut_stack_use.rs | 22 - tests-extra/invalid/with_mut_stack_use.stderr | 13 - tests-extra/invalid_manual/Cargo.toml | 4 - tests-extra/invalid_manual/README.md | 6 - .../wrong_covariance/Cargo.toml | 9 - .../wrong_covariance/expected.stderr | 35 - .../wrong_covariance/src/main.rs | 18 - tests-extra/src/lib.rs | 148 ---- tests/self_cell.rs | 751 ------------------ 29 files changed, 20 insertions(+), 2089 deletions(-) delete mode 100644 src/unsafe_self_cell.rs delete mode 100644 tests-extra/Cargo.toml delete mode 100644 tests-extra/README.md delete mode 100644 tests-extra/invalid/contravariant_owner.rs delete mode 100644 tests-extra/invalid/contravariant_owner.stderr delete mode 100644 tests-extra/invalid/covariant_owner_non_covariant_dependent.rs delete mode 100644 tests-extra/invalid/covariant_owner_non_covariant_dependent.stderr delete mode 100644 tests-extra/invalid/escape_dependent.rs delete mode 100644 tests-extra/invalid/escape_dependent.stderr delete mode 100644 tests-extra/invalid/leak_dependent.rs delete mode 100644 tests-extra/invalid/leak_dependent.stderr delete mode 100644 tests-extra/invalid/leak_outside_ref.rs delete mode 100644 tests-extra/invalid/leak_outside_ref.stderr delete mode 100644 tests-extra/invalid/reborrow_dependent_cyclic.rs delete mode 100644 tests-extra/invalid/reborrow_dependent_cyclic.stderr delete mode 100644 tests-extra/invalid/swap_cell_member.rs delete mode 100644 tests-extra/invalid/swap_cell_member.stderr delete mode 100644 tests-extra/invalid/with_mut_stack_use.rs delete mode 100644 tests-extra/invalid/with_mut_stack_use.stderr delete mode 100644 tests-extra/invalid_manual/Cargo.toml delete mode 100644 tests-extra/invalid_manual/README.md delete mode 100644 tests-extra/invalid_manual/wrong_covariance/Cargo.toml delete mode 100644 tests-extra/invalid_manual/wrong_covariance/expected.stderr delete mode 100644 tests-extra/invalid_manual/wrong_covariance/src/main.rs delete mode 100644 tests-extra/src/lib.rs delete mode 100644 tests/self_cell.rs diff --git a/Cargo.toml b/Cargo.toml index d1bf5e3..1bef33f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "self_cell" -version = "0.10.2" +version = "0.10.3" authors = ["Lukas Bergdoll "] edition = "2018" license = "Apache-2.0" @@ -21,14 +21,11 @@ include = [ ] [dependencies] -rustversion = { version=">=1", optional=true } - -[dev-dependencies] -once_cell = ">=1" +self_cell = "1" [features] # This optional feature lowers the minimum rustc version from 1.51 to 1.36. # However this requires polyfilling std library functionality for older rustc # with technically UB versions. Testing does not show older rustc versions # (ab)using this. Use at -old_rust = ["rustversion"] \ No newline at end of file +old_rust = ["self_cell/old_rust"] diff --git a/README.md b/README.md index 819bdc8..eeee9b1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ [crates.io](https://crates.io/crates/self_cell) [docs.rs](https://docs.rs/self_cell) +# Note on version 0.10.3 + +This version of `self_cell` is a re-export of the `self_cell` version `1` +meant for backwards-compatibly allowing `0.10` users to get current and +future soundness fixes. If you are adding `self_cell` as a dependency, +please use an up-to-date version directly. + # `self_cell!` Use the macro-rules macro: `self_cell!` to create safe-to-use self-referential diff --git a/src/lib.rs b/src/lib.rs index e2d8f2e..e3e44b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,13 @@ +//! # Note on version 0.10.3 +//! +//! This version of `self_cell` is a re-export of the `self_cell` version `1` +//! meant for backwards-compatibly allowing `0.10` users to get current and +//! future soundness fixes. If you are adding `self_cell` as a dependency, +//! please use an up-to-date version directly. +//! //! # Overview //! -//! `self_cell` provides one macro-rules macro: [`self_cell`]. With this macro +//! `self_cell` provides one macro-rules macro: [`self_cell`][`self_cell!`]. With this macro //! you can create self-referential structs that are safe-to-use in stable Rust, //! without leaking the struct internal lifetime. //! @@ -126,7 +133,7 @@ //! makes it safe to move the generated SelfCell but you have to pay for the //! heap allocation. //! -//! See the documentation for [`self_cell`] to dive further into the details. +//! See the documentation for [`self_cell`][`self_cell!`] to dive further into the details. //! //! Or take a look at the advanced examples: //! - [Example how to handle dependent construction that can @@ -152,508 +159,4 @@ #![no_std] -#[doc(hidden)] -pub extern crate alloc; - -#[doc(hidden)] -pub mod unsafe_self_cell; - -/// This macro declares a new struct of `$StructName` and implements traits -/// based on `$AutomaticDerive`. -/// -/// ### Example: -/// -/// ```rust -/// use self_cell::self_cell; -/// -/// #[derive(Debug, Eq, PartialEq)] -/// struct Ast<'a>(Vec<&'a str>); -/// -/// self_cell!( -/// #[doc(hidden)] -/// struct PackedAstCell { -/// owner: String, -/// -/// #[covariant] -/// dependent: Ast, -/// } -/// -/// impl {Debug, PartialEq, Eq, Hash} -/// ); -/// ``` -/// -/// See the crate overview to get a get an overview and a motivating example. -/// -/// ### Generated API: -/// -/// The macro implements these constructors: -/// -/// ```ignore -/// fn new( -/// owner: $Owner, -/// dependent_builder: impl for<'a> FnOnce(&'a $Owner) -> $Dependent<'a> -/// ) -> Self -/// ``` -/// -/// ```ignore -/// fn try_new( -/// owner: $Owner, -/// dependent_builder: impl for<'a> FnOnce(&'a $Owner) -> Result<$Dependent<'a>, Err> -/// ) -> Result -/// ``` -/// -/// ```ignore -/// fn try_new_or_recover( -/// owner: $Owner, -/// dependent_builder: impl for<'a> FnOnce(&'a $Owner) -> Result<$Dependent<'a>, Err> -/// ) -> Result -/// ``` -/// -/// The macro implements these methods: -/// -/// ```ignore -/// fn borrow_owner<'a>(&'a self) -> &'a $Owner -/// ``` -/// -/// ```ignore -/// // Only available if dependent is covariant. -/// fn borrow_dependent<'a>(&'a self) -> &'a $Dependent<'a> -/// ``` -/// -/// ```ignore -/// fn with_dependent<'outer_fn, Ret>( -/// &'outer_fn self, -/// func: impl for<'a> FnOnce(&'a $Owner, &'outer_fn $Dependent<'a> -/// ) -> Ret) -> Ret -/// ``` -/// -/// ```ignore -/// fn with_dependent_mut<'outer_fn, Ret>( -/// &'outer_fn mut self, -/// func: impl for<'a> FnOnce(&'a $Owner, &'outer_fn mut $Dependent<'a>) -> Ret -/// ) -> Ret -/// ``` -/// -/// ```ignore -/// fn into_owner(self) -> $Owner -/// ``` -/// -/// -/// ### Parameters: -/// -/// - `$Vis:vis struct $StructName:ident` Name of the struct that will be -/// declared, this needs to be unique for the relevant scope. Example: `struct -/// AstCell` or `pub struct AstCell`. `$Vis` can be used to mark the struct -/// and all functions implemented by the macro as public. -/// -/// `$(#[$StructMeta:meta])*` allows you specify further meta items for this -/// struct, eg. `#[doc(hidden)] struct AstCell`. -/// -/// - `$Owner:ty` Type of owner. This has to have a `'static` lifetime. Example: -/// `String`. -/// -/// - `$Dependent:ident` Name of the dependent type without specified lifetime. -/// This can't be a nested type name. As workaround either create a type alias -/// `type Dep<'a> = Option>;` or create a new-type `struct -/// Dep<'a>(Option>);`. Example: `Ast`. -/// -/// `$Covariance:ident` Marker declaring if `$Dependent` is -/// [covariant](https://doc.rust-lang.org/nightly/nomicon/subtyping.html). -/// Possible Values: -/// -/// * **covariant**: This generates the direct reference accessor function -/// `borrow_dependent`. This is only safe to do if this compiles `fn -/// _assert_covariance<'x: 'y, 'y>(x: $Dependent<'x>) -> $Dependent<'y> -/// {x}`. Otherwise you could choose a lifetime that is too short for types -/// with interior mutability like `Cell`, which can lead to UB in safe code. -/// Which would violate the promise of this library that it is safe-to-use. -/// If you accidentally mark a type that is not covariant as covariant, you -/// will get a compile time error. -/// -/// * **not_covariant**: This generates no additional code but you can use the -/// `with_dependent` function. See [How to build a lazy AST with -/// self_cell](https://github.com/Voultapher/self_cell/tree/main/examples/lazy_ast) -/// for a usage example. -/// -/// In both cases you can use the `with_dependent_mut` function to mutate the -/// dependent value. This is safe to do because notionally you are replacing -/// pointers to a value not the other way around. -/// -/// - `impl {$($AutomaticDerive:ident),*},` Optional comma separated list of -/// optional automatic trait implementations. Possible Values: -/// -/// * **Debug**: Prints the debug representation of owner and dependent. -/// Example: `AstCell { owner: "fox = cat + dog", dependent: Ast(["fox", -/// "cat", "dog"]) }` -/// -/// * **PartialEq**: Logic `*self.borrow_owner() == *other.borrow_owner()`, -/// this assumes that `Dependent<'a>::From<&'a Owner>` is deterministic, so -/// that only comparing owner is enough. -/// -/// * **Eq**: Will implement the trait marker `Eq` for `$StructName`. Beware -/// if you select this `Eq` will be implemented regardless if `$Owner` -/// implements `Eq`, that's an unfortunate technical limitation. -/// -/// * **Hash**: Logic `self.borrow_owner().hash(state);`, this assumes that -/// `Dependent<'a>::From<&'a Owner>` is deterministic, so that only hashing -/// owner is enough. -/// -/// All `AutomaticDerive` are optional and you can implement you own version -/// of these traits. The declared struct is part of your module and you are -/// free to implement any trait in any way you want. Access to the unsafe -/// internals is only possible via unsafe functions, so you can't accidentally -/// use them in safe code. -/// -/// There is limited nested cell support. Eg, having an owner with non static -/// references. Eg `struct ChildCell<'a> { owner: &'a String, ...`. You can -/// use any lifetime name you want, except `_q` and only a single lifetime is -/// supported, and can only be used in the owner. Due to macro_rules -/// limitations, no `AutomaticDerive` are supported if an owner lifetime is -/// provided. -/// -#[macro_export] -macro_rules! self_cell { -( - $(#[$StructMeta:meta])* - $Vis:vis struct $StructName:ident $(<$OwnerLifetime:lifetime>)? { - owner: $Owner:ty, - - #[$Covariance:ident] - dependent: $Dependent:ident, - } - - $(impl {$($AutomaticDerive:ident),*})? -) => { - #[repr(transparent)] - $(#[$StructMeta])* - $Vis struct $StructName $(<$OwnerLifetime>)? { - unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell< - $StructName$(<$OwnerLifetime>)?, - $Owner, - $Dependent<'static> - >, - - $(owner_marker: $crate::_covariant_owner_marker!($Covariance, $OwnerLifetime) ,)? - } - - impl $(<$OwnerLifetime>)? $StructName $(<$OwnerLifetime>)? { - $Vis fn new( - owner: $Owner, - dependent_builder: impl for<'_q> FnOnce(&'_q $Owner) -> $Dependent<'_q> - ) -> Self { - use core::ptr::NonNull; - - unsafe { - // All this has to happen here, because there is not good way - // of passing the appropriate logic into UnsafeSelfCell::new - // short of assuming Dependent<'static> is the same as - // Dependent<'_q>, which I'm not confident is safe. - - // For this API to be safe there has to be no safe way to - // capture additional references in `dependent_builder` and then - // return them as part of Dependent. Eg. it should be impossible - // to express: '_q should outlive 'x here `fn - // bad<'_q>(outside_ref: &'_q String) -> impl for<'x> FnOnce(&'x - // Owner) -> Dependent<'x>`. - - type JoinedCell<'_q $(, $OwnerLifetime)?> = - $crate::unsafe_self_cell::JoinedCell<$Owner, $Dependent<'_q>>; - - let layout = $crate::alloc::alloc::Layout::new::(); - assert!(layout.size() != 0); - - let joined_void_ptr = NonNull::new($crate::alloc::alloc::alloc(layout)).unwrap(); - - let mut joined_ptr = core::mem::transmute::, NonNull>( - joined_void_ptr - ); - - let (owner_ptr, dependent_ptr) = JoinedCell::_field_pointers(joined_ptr.as_ptr()); - - // Move owner into newly allocated space. - owner_ptr.write(owner); - - // Drop guard that cleans up should building the dependent panic. - let drop_guard = - $crate::unsafe_self_cell::OwnerAndCellDropGuard::new(joined_ptr); - - // Initialize dependent with owner reference in final place. - dependent_ptr.write(dependent_builder(&*owner_ptr)); - core::mem::forget(drop_guard); - - Self { - unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new( - joined_void_ptr, - ), - $(owner_marker: $crate::_covariant_owner_marker_ctor!($OwnerLifetime) ,)? - } - } - } - - $Vis fn try_new( - owner: $Owner, - dependent_builder: - impl for<'_q> FnOnce(&'_q $Owner) -> core::result::Result<$Dependent<'_q>, Err> - ) -> core::result::Result { - use core::ptr::NonNull; - - unsafe { - // See fn new for more explanation. - - type JoinedCell<'_q $(, $OwnerLifetime)?> = - $crate::unsafe_self_cell::JoinedCell<$Owner, $Dependent<'_q>>; - - let layout = $crate::alloc::alloc::Layout::new::(); - assert!(layout.size() != 0); - - let joined_void_ptr = NonNull::new($crate::alloc::alloc::alloc(layout)).unwrap(); - - let mut joined_ptr = core::mem::transmute::, NonNull>( - joined_void_ptr - ); - - let (owner_ptr, dependent_ptr) = JoinedCell::_field_pointers(joined_ptr.as_ptr()); - - // Move owner into newly allocated space. - owner_ptr.write(owner); - - // Drop guard that cleans up should building the dependent panic. - let mut drop_guard = - $crate::unsafe_self_cell::OwnerAndCellDropGuard::new(joined_ptr); - - match dependent_builder(&*owner_ptr) { - Ok(dependent) => { - dependent_ptr.write(dependent); - core::mem::forget(drop_guard); - - Ok(Self { - unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new( - joined_void_ptr, - ), - $(owner_marker: $crate::_covariant_owner_marker_ctor!($OwnerLifetime) ,)? - }) - } - Err(err) => Err(err) - } - } - } - - $Vis fn try_new_or_recover( - owner: $Owner, - dependent_builder: - impl for<'_q> FnOnce(&'_q $Owner) -> core::result::Result<$Dependent<'_q>, Err> - ) -> core::result::Result { - use core::ptr::NonNull; - - unsafe { - // See fn new for more explanation. - - type JoinedCell<'_q $(, $OwnerLifetime)?> = - $crate::unsafe_self_cell::JoinedCell<$Owner, $Dependent<'_q>>; - - let layout = $crate::alloc::alloc::Layout::new::(); - assert!(layout.size() != 0); - - let joined_void_ptr = NonNull::new($crate::alloc::alloc::alloc(layout)).unwrap(); - - let mut joined_ptr = core::mem::transmute::, NonNull>( - joined_void_ptr - ); - - let (owner_ptr, dependent_ptr) = JoinedCell::_field_pointers(joined_ptr.as_ptr()); - - // Move owner into newly allocated space. - owner_ptr.write(owner); - - // Drop guard that cleans up should building the dependent panic. - let mut drop_guard = - $crate::unsafe_self_cell::OwnerAndCellDropGuard::new(joined_ptr); - - match dependent_builder(&*owner_ptr) { - Ok(dependent) => { - dependent_ptr.write(dependent); - core::mem::forget(drop_guard); - - Ok(Self { - unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new( - joined_void_ptr, - ), - $(owner_marker: $crate::_covariant_owner_marker_ctor!($OwnerLifetime) ,)? - }) - } - Err(err) => { - // In contrast to into_owner ptr::read, here no dependent - // ever existed in this function and so we are sure its - // drop impl can't access owner after the read. - // And err can't return a reference to owner. - let owner_on_err = core::ptr::read(owner_ptr); - - // Allowing drop_guard to finish would let it double free owner. - // So we dealloc the JoinedCell here manually. - core::mem::forget(drop_guard); - $crate::alloc::alloc::dealloc(joined_void_ptr.as_ptr(), layout); - - Err((owner_on_err, err)) - } - } - } - } - - $Vis fn borrow_owner<'_q>(&'_q self) -> &'_q $Owner { - unsafe { self.unsafe_self_cell.borrow_owner::<$Dependent<'_q>>() } - } - - $Vis fn with_dependent<'outer_fn, Ret>( - &'outer_fn self, - func: impl for<'_q> FnOnce(&'_q $Owner, &'outer_fn $Dependent<'_q> - ) -> Ret) -> Ret { - unsafe { - func( - self.unsafe_self_cell.borrow_owner::<$Dependent>(), - self.unsafe_self_cell.borrow_dependent() - ) - } - } - - $Vis fn with_dependent_mut<'outer_fn, Ret>( - &'outer_fn mut self, - func: impl for<'_q> FnOnce(&'_q $Owner, &'outer_fn mut $Dependent<'_q>) -> Ret - ) -> Ret { - let (owner, dependent) = unsafe { - self.unsafe_self_cell.borrow_mut() - }; - - func(owner, dependent) - } - - $crate::_covariant_access!($Covariance, $Vis, $Dependent); - - $Vis fn into_owner(self) -> $Owner { - // This is only safe to do with repr(transparent). - let unsafe_self_cell = unsafe { core::mem::transmute::< - Self, - $crate::unsafe_self_cell::UnsafeSelfCell< - $StructName$(<$OwnerLifetime>)?, - $Owner, - $Dependent<'static> - > - >(self) }; - - let owner = unsafe { unsafe_self_cell.into_owner::<$Dependent>() }; - - owner - } - } - - impl $(<$OwnerLifetime>)? Drop for $StructName $(<$OwnerLifetime>)? { - fn drop(&mut self) { - unsafe { - self.unsafe_self_cell.drop_joined::<$Dependent>(); - } - } - } - - // The user has to choose which traits can and should be automatically - // implemented for the cell. - $($( - $crate::_impl_automatic_derive!($AutomaticDerive, $StructName); - )*)* -}; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _covariant_access { - (covariant, $Vis:vis, $Dependent:ident) => { - $Vis fn borrow_dependent<'_q>(&'_q self) -> &'_q $Dependent<'_q> { - fn _assert_covariance<'x: 'y, 'y>(x: $Dependent<'x>) -> $Dependent<'y> { - // This function only compiles for covariant types. - x // Change the macro invocation to not_covariant. - } - - unsafe { self.unsafe_self_cell.borrow_dependent() } - } - }; - (not_covariant, $Vis:vis, $Dependent:ident) => { - // For types that are not covariant it's unsafe to allow - // returning direct references. - // For example a lifetime that is too short could be chosen: - // See https://github.com/Voultapher/self_cell/issues/5 - }; - ($x:ident, $Vis:vis, $Dependent:ident) => { - compile_error!("This macro only accepts `covariant` or `not_covariant`"); - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _covariant_owner_marker { - (covariant, $OwnerLifetime:lifetime) => { - // Ensure that contravariant owners don't imply covariance - // over the dependent. See issue https://github.com/Voultapher/self_cell/issues/18 - core::marker::PhantomData<&$OwnerLifetime ()> - }; - (not_covariant, $OwnerLifetime:lifetime) => { - // See the discussion in https://github.com/Voultapher/self_cell/pull/29 - // - // If the dependent is non_covariant, mark the owner as invariant over its - // lifetime. Otherwise unsound use is possible. - core::marker::PhantomData &$OwnerLifetime ()> - }; - ($x:ident, $OwnerLifetime:lifetime) => { - compile_error!("This macro only accepts `covariant` or `not_covariant`"); - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _covariant_owner_marker_ctor { - ($OwnerLifetime:lifetime) => { - // Helper to optionally expand into PhantomData for construction. - core::marker::PhantomData - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! _impl_automatic_derive { - (Debug, $StructName:ident) => { - impl core::fmt::Debug for $StructName { - fn fmt( - &self, - fmt: &mut core::fmt::Formatter, - ) -> core::result::Result<(), core::fmt::Error> { - self.with_dependent(|owner, dependent| { - fmt.debug_struct(stringify!($StructName)) - .field("owner", owner) - .field("dependent", dependent) - .finish() - }) - } - } - }; - (PartialEq, $StructName:ident) => { - impl core::cmp::PartialEq for $StructName { - fn eq(&self, other: &Self) -> bool { - *self.borrow_owner() == *other.borrow_owner() - } - } - }; - (Eq, $StructName:ident) => { - // TODO this should only be allowed if owner is Eq. - impl core::cmp::Eq for $StructName {} - }; - (Hash, $StructName:ident) => { - impl core::hash::Hash for $StructName { - fn hash(&self, state: &mut H) { - self.borrow_owner().hash(state); - } - } - }; - ($x:ident, $StructName:ident) => { - compile_error!(concat!( - "No automatic trait impl for trait: ", - stringify!($x) - )); - }; -} +pub use self_cell::self_cell; \ No newline at end of file diff --git a/src/unsafe_self_cell.rs b/src/unsafe_self_cell.rs deleted file mode 100644 index b0b934d..0000000 --- a/src/unsafe_self_cell.rs +++ /dev/null @@ -1,230 +0,0 @@ -use core::marker::PhantomData; -use core::mem::{self, transmute}; -use core::ptr::{drop_in_place, read, NonNull}; - -extern crate alloc; - -use alloc::alloc::{dealloc, Layout}; - -// Self referential structs are currently not supported with safe vanilla Rust. -// The only reasonable safe alternative is to expect the user to juggle 2 separate -// data structures which is a mess. The library solution rental is both no longer -// maintained and really heavy to compile. So begrudgingly I rolled my own version. -// These are some of the core invariants we require for this to be safe to use. -// -// 1. owner is initialized when UnsafeSelfCell is constructed. -// 2. owner is NEVER changed again. -// 3. The pointer to owner and dependent never changes, even when moved. -// 4. The only access to owner and dependent is as immutable reference. -// 5. owner lives longer than dependent. - -#[doc(hidden)] -pub struct JoinedCell { - pub owner: Owner, - pub dependent: Dependent, -} - -// Library controlled struct that marks all accesses as unsafe. -// Because the macro generated struct impl can be extended, could be unsafe. -#[doc(hidden)] -pub struct UnsafeSelfCell { - joined_void_ptr: NonNull, - - // ContainedIn is necessary for type safety since we don't fully - // prohibit access to the UnsafeSelfCell; swapping between different - // structs can be unsafe otherwise, see Issue #17. - contained_in_marker: PhantomData, - - owner_marker: PhantomData, - // DependentStatic is only used to correctly derive Send and Sync. - dependent_marker: PhantomData, -} - -impl UnsafeSelfCell { - pub unsafe fn new(joined_void_ptr: NonNull) -> Self { - Self { - joined_void_ptr, - contained_in_marker: PhantomData, - owner_marker: PhantomData, - dependent_marker: PhantomData, - } - } - - // Calling any of these *unsafe* functions with the wrong Dependent type is UB. - - pub unsafe fn borrow_owner<'a, Dependent>(&'a self) -> &'a Owner { - let joined_ptr = - transmute::, NonNull>>(self.joined_void_ptr); - - &(*joined_ptr.as_ptr()).owner - } - - pub unsafe fn borrow_dependent<'a, Dependent>(&'a self) -> &'a Dependent { - let joined_ptr = - transmute::, NonNull>>(self.joined_void_ptr); - - &(*joined_ptr.as_ptr()).dependent - } - - pub unsafe fn borrow_mut<'a, Dependent>(&'a mut self) -> (&'a Owner, &'a mut Dependent) { - let joined_ptr = - transmute::, NonNull>>(self.joined_void_ptr); - - // This function used to return `&'a mut JoinedCell`. - // It now creates two references to the fields instead to avoid claiming mutable access - // to the whole `JoinedCell` (including the owner!) here. - ( - &(*joined_ptr.as_ptr()).owner, - &mut (*joined_ptr.as_ptr()).dependent, - ) - } - - // Any subsequent use of this struct other than dropping it is UB. - pub unsafe fn drop_joined(&mut self) { - let joined_ptr = - transmute::, NonNull>>(self.joined_void_ptr); - - // Also used in case drop_in_place(...dependent) fails - let _guard = OwnerAndCellDropGuard { joined_ptr }; - - // IMPORTANT dependent must be dropped before owner. - // We don't want to rely on an implicit order of struct fields. - // So we drop the struct, field by field manually. - drop_in_place(&mut (*joined_ptr.as_ptr()).dependent); - - // Dropping owner - // and deallocating - // due to _guard at end of scope. - } - - pub unsafe fn into_owner(self) -> Owner { - let joined_ptr = - transmute::, NonNull>>(self.joined_void_ptr); - - // In case drop_in_place(...dependent) fails - let drop_guard = OwnerAndCellDropGuard::new(joined_ptr); - - // Drop dependent - drop_in_place(&mut (*joined_ptr.as_ptr()).dependent); - - mem::forget(drop_guard); - - let owner_ptr: *const Owner = &(*joined_ptr.as_ptr()).owner; - - // Move owner out so it can be returned. - // Must not read before dropping dependent!! (Which happened above.) - let owner = read(owner_ptr); - - // Deallocate JoinedCell - let layout = Layout::new::>(); - dealloc(self.joined_void_ptr.as_ptr(), layout); - - owner - } -} - -unsafe impl Send - for UnsafeSelfCell -where - // Only derive Send if Owner and DependentStatic is also Send - Owner: Send, - DependentStatic: Send, -{ -} - -unsafe impl Sync - for UnsafeSelfCell -where - // Only derive Sync if Owner and DependentStatic is also Sync - Owner: Sync, - DependentStatic: Sync, -{ -} - -// This struct is used to safely deallocate only the owner if dependent -// construction fails. -// -// mem::forget it once it's no longer needed or dtor will be UB. -#[doc(hidden)] -pub struct OwnerAndCellDropGuard { - joined_ptr: NonNull>, -} - -impl OwnerAndCellDropGuard { - pub unsafe fn new(joined_ptr: NonNull>) -> Self { - Self { joined_ptr } - } -} - -impl Drop for OwnerAndCellDropGuard { - fn drop(&mut self) { - struct DeallocGuard { - ptr: *mut u8, - layout: Layout, - } - impl Drop for DeallocGuard { - fn drop(&mut self) { - unsafe { dealloc(self.ptr, self.layout) } - } - } - - // Deallocate even when the drop_in_place(...owner) panics - let _guard = DeallocGuard { - ptr: unsafe { - transmute::<*mut JoinedCell, *mut u8>(self.joined_ptr.as_ptr()) - }, - layout: Layout::new::>(), - }; - - unsafe { - // We must only drop owner and the struct itself, - // The whole point of this drop guard is to clean up the partially - // initialized struct should building the dependent fail. - drop_in_place(&mut (*self.joined_ptr.as_ptr()).owner); - } - - // Deallocation happens at end of scope - } -} - -// Older versions of rust do not support addr_of_mut!. What we want to do here -// is to emulate the behavior of that macro by going (incorrectly) via a -// reference cast. Technically this is UB, but testing does not show the older -// compiler versions (ab)using this. For discussions about this behavior see -// https://github.com/Voultapher/self_cell/pull/31 and -// https://github.com/Voultapher/self_cell/issues/30 and -// https://github.com/Voultapher/self_cell/pull/33 -// -// Because of 'procedural macros cannot expand to macro definitions' -// we have wrap this in functions. -impl JoinedCell { - #[doc(hidden)] - #[cfg(not(feature = "old_rust"))] - pub unsafe fn _field_pointers(this: *mut Self) -> (*mut Owner, *mut Dependent) { - let owner_ptr = core::ptr::addr_of_mut!((*this).owner); - let dependent_ptr = core::ptr::addr_of_mut!((*this).dependent); - - (owner_ptr, dependent_ptr) - } - - #[doc(hidden)] - #[cfg(feature = "old_rust")] - #[rustversion::since(1.51)] - pub unsafe fn _field_pointers(this: *mut Self) -> (*mut Owner, *mut Dependent) { - let owner_ptr = core::ptr::addr_of_mut!((*this).owner); - let dependent_ptr = core::ptr::addr_of_mut!((*this).dependent); - - (owner_ptr, dependent_ptr) - } - - #[doc(hidden)] - #[cfg(feature = "old_rust")] - #[rustversion::before(1.51)] - pub unsafe fn _field_pointers(this: *mut Self) -> (*mut Owner, *mut Dependent) { - // See comment above, technically this is UB. - let owner_ptr = &mut (*this).owner as *mut Owner; - let dependent_ptr = &mut (*this).dependent as *mut Dependent; - - (owner_ptr, dependent_ptr) - } -} diff --git a/tests-extra/Cargo.toml b/tests-extra/Cargo.toml deleted file mode 100644 index b2ccb74..0000000 --- a/tests-extra/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "tests-extra" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -self_cell = { path=".." } -crossbeam-utils = "0.8.0" -impls = "1.0.3" -trybuild = "1.0.37" diff --git a/tests-extra/README.md b/tests-extra/README.md deleted file mode 100644 index fbec808..0000000 --- a/tests-extra/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory contains a project with further tests that can't be part of the main tests as they require dependencies that can't be build with the min old_rust feature version. And dev-dependencies can't be optional. \ No newline at end of file diff --git a/tests-extra/invalid/contravariant_owner.rs b/tests-extra/invalid/contravariant_owner.rs deleted file mode 100644 index 58ab897..0000000 --- a/tests-extra/invalid/contravariant_owner.rs +++ /dev/null @@ -1,32 +0,0 @@ -use self_cell::self_cell; - -type X<'a> = &'a str; - -self_cell! { - pub struct Foo<'a> { - owner: fn(&'a ()), - - #[covariant] - dependent: X, - } -} - -fn transmute_lifetime<'a, 'b>(x: &'a str) -> &'b str { - fn helper<'x>(s: &'x str) -> impl for<'z> FnOnce(&'z fn(&'x ())) -> &'z str { - move |_| s - } - let x: Foo<'a> = Foo::new(|_| (), helper(x)); - let x: Foo<'static> = x; // coerce using variance - let y = Box::leak(Box::new(x)); - y.borrow_dependent() -} - -fn main() { - let r; - { - let s = "Hello World".to_owned(); - r = transmute_lifetime(s.as_str()); - dbg!(r); // "Hello World" - } - dbg!(r); // prints garbage :-) -} diff --git a/tests-extra/invalid/contravariant_owner.stderr b/tests-extra/invalid/contravariant_owner.stderr deleted file mode 100644 index 43435b1..0000000 --- a/tests-extra/invalid/contravariant_owner.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/contravariant_owner.rs:19:27 - | -19 | let x: Foo<'static> = x; // coerce using variance - | ^ lifetime mismatch - | - = note: expected struct `Foo<'static>` - found struct `Foo<'a>` -note: the lifetime `'a` as defined on the function body at 14:23... - --> $DIR/contravariant_owner.rs:14:23 - | -14 | fn transmute_lifetime<'a, 'b>(x: &'a str) -> &'b str { - | ^^ - = note: ...does not necessarily outlive the static lifetime diff --git a/tests-extra/invalid/covariant_owner_non_covariant_dependent.rs b/tests-extra/invalid/covariant_owner_non_covariant_dependent.rs deleted file mode 100644 index d0889c7..0000000 --- a/tests-extra/invalid/covariant_owner_non_covariant_dependent.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::{cell::Cell, marker::PhantomData}; - -use self_cell::self_cell; - -self_cell! { - struct Foo<'a> { - owner: PhantomData<&'a ()>, - #[not_covariant] - dependent: Dependent, - } -} - -type Dependent<'q> = Cell<&'q str>; - -fn main() { - let foo = Foo::new(PhantomData, |_| Cell::new("")); - let s: String = "Hello World".into(); - foo.with_dependent(|_, d| { - d.set(&s); - }); - drop(s); - foo.with_dependent(|_, d| println!("{}", d.get())); -} diff --git a/tests-extra/invalid/covariant_owner_non_covariant_dependent.stderr b/tests-extra/invalid/covariant_owner_non_covariant_dependent.stderr deleted file mode 100644 index e839ddf..0000000 --- a/tests-extra/invalid/covariant_owner_non_covariant_dependent.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error[E0597]: `s` does not live long enough - --> $DIR/covariant_owner_non_covariant_dependent.rs:19:16 - | -18 | foo.with_dependent(|_, d| { - | ------ value captured here -19 | d.set(&s); - | ^ borrowed value does not live long enough -... -23 | } - | - - | | - | `s` dropped here while still borrowed - | borrow might be used here, when `foo` is dropped and runs the `Drop` code for type `Foo` - | - = note: values in a scope are dropped in the opposite order they are defined - -error[E0505]: cannot move out of `s` because it is borrowed - --> $DIR/covariant_owner_non_covariant_dependent.rs:21:10 - | -18 | foo.with_dependent(|_, d| { - | ------ borrow of `s` occurs here -19 | d.set(&s); - | - borrow occurs due to use in closure -20 | }); -21 | drop(s); - | ^ move out of `s` occurs here -22 | foo.with_dependent(|_, d| println!("{}", d.get())); - | --- borrow later used here diff --git a/tests-extra/invalid/escape_dependent.rs b/tests-extra/invalid/escape_dependent.rs deleted file mode 100644 index 613a5cf..0000000 --- a/tests-extra/invalid/escape_dependent.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::{cell::Cell, marker::PhantomData}; - -use self_cell::self_cell; - -type NotCovariant<'a> = Cell<&'a str>; - -struct Owner<'a>(String, PhantomData<&'a ()>); - -self_cell!( - struct NoCov<'a> { - owner: Owner<'a>, - - #[not_covariant] - dependent: NotCovariant, - } -); - -fn main() { - let cell = NoCov::new(Owner("hi this is no good".into(), PhantomData), |owner| { - Cell::new(&owner.0) - }); - let leaked_ref = cell.with_dependent(|_, dependent| dependent); - leaked_ref.set(&String::from("temporary")); - - cell.with_dependent(|_, dep| { - println!("{}", dep.replace("")); - }); -} diff --git a/tests-extra/invalid/escape_dependent.stderr b/tests-extra/invalid/escape_dependent.stderr deleted file mode 100644 index d9ef868..0000000 --- a/tests-extra/invalid/escape_dependent.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0597]: `cell` does not live long enough - --> $DIR/escape_dependent.rs:22:22 - | -22 | let leaked_ref = cell.with_dependent(|_, dependent| dependent); - | ^^^^ borrowed value does not live long enough -... -28 | } - | - - | | - | `cell` dropped here while still borrowed - | borrow might be used here, when `cell` is dropped and runs the `Drop` code for type `NoCov` - -error[E0716]: temporary value dropped while borrowed - --> $DIR/escape_dependent.rs:23:21 - | -23 | leaked_ref.set(&String::from("temporary")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement - | | - | creates a temporary which is freed while still in use -... -28 | } - | - borrow might be used here, when `cell` is dropped and runs the `Drop` code for type `NoCov` - | - = note: consider using a `let` binding to create a longer lived value diff --git a/tests-extra/invalid/leak_dependent.rs b/tests-extra/invalid/leak_dependent.rs deleted file mode 100644 index 67fda57..0000000 --- a/tests-extra/invalid/leak_dependent.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::cell::Cell; - -use self_cell::self_cell; - -type NotCovariant<'a> = Cell<&'a String>; - -self_cell!( - struct NoCov { - owner: String, - - #[not_covariant] - dependent: NotCovariant, - } -); - -fn main() { - let cell = NoCov::new("hi this is no good".into(), |owner| Cell::new(owner)); - let _leaked_ref = cell.with_dependent(|_, dependent| dependent); -} diff --git a/tests-extra/invalid/leak_dependent.stderr b/tests-extra/invalid/leak_dependent.stderr deleted file mode 100644 index 3d4e75f..0000000 --- a/tests-extra/invalid/leak_dependent.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0597]: `cell` does not live long enough - --> $DIR/leak_dependent.rs:18:23 - | -18 | let _leaked_ref = cell.with_dependent(|_, dependent| dependent); - | ^^^^ --------- returning this value requires that `cell` is borrowed for `'static` - | | - | borrowed value does not live long enough -19 | } - | - `cell` dropped here while still borrowed diff --git a/tests-extra/invalid/leak_outside_ref.rs b/tests-extra/invalid/leak_outside_ref.rs deleted file mode 100644 index 10f48d8..0000000 --- a/tests-extra/invalid/leak_outside_ref.rs +++ /dev/null @@ -1,24 +0,0 @@ -use self_cell::self_cell; - -struct Dependent<'a> { - good: &'a String, - bad: &'a String, -} - -self_cell!( - struct NoCov { - owner: String, - - #[covariant] - dependent: Dependent, - } -); - -fn main() { - let outside_string = String::from("outside string"); - - let _cell = NoCov::new("hi this is no good".into(), |owner| Dependent { - good: owner, - bad: &outside_string, - }); -} diff --git a/tests-extra/invalid/leak_outside_ref.stderr b/tests-extra/invalid/leak_outside_ref.stderr deleted file mode 100644 index 06ea703..0000000 --- a/tests-extra/invalid/leak_outside_ref.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0597]: `outside_string` does not live long enough - --> $DIR/leak_outside_ref.rs:22:15 - | -20 | let _cell = NoCov::new("hi this is no good".into(), |owner| Dependent { - | _________________________________________________________-------_- - | | | - | | value captured here -21 | | good: owner, -22 | | bad: &outside_string, - | | ^^^^^^^^^^^^^^ borrowed value does not live long enough -23 | | }); - | |_____- returning this value requires that `outside_string` is borrowed for `'static` -24 | } - | - `outside_string` dropped here while still borrowed diff --git a/tests-extra/invalid/reborrow_dependent_cyclic.rs b/tests-extra/invalid/reborrow_dependent_cyclic.rs deleted file mode 100644 index 4bed017..0000000 --- a/tests-extra/invalid/reborrow_dependent_cyclic.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::cell::RefCell; - -use self_cell::self_cell; - -struct Bar<'a>(RefCell<(Option<&'a Bar<'a>>, String)>); - -self_cell! { - struct Foo { - owner: (), - - #[not_covariant] - dependent: Bar, - } -} - -fn main() { - let mut x = Foo::new((), |_| Bar(RefCell::new((None, "Hello".to_owned())))); - - x.with_dependent(|_, dep| { - dep.0.borrow_mut().0 = Some(dep); - }); - - x.with_dependent_mut(|_, dep| { - let r1 = dep.0.get_mut(); - let string_ref_1 = &mut r1.1; - let mut r2 = r1.0.unwrap().0.borrow_mut(); - let string_ref_2 = &mut r2.1; - - let s = &string_ref_1[..]; - string_ref_2.clear(); - string_ref_2.shrink_to_fit(); - println!("{}", s); // prints garbage - }); -} diff --git a/tests-extra/invalid/reborrow_dependent_cyclic.stderr b/tests-extra/invalid/reborrow_dependent_cyclic.stderr deleted file mode 100644 index 32fcd0d..0000000 --- a/tests-extra/invalid/reborrow_dependent_cyclic.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0597]: `x` does not live long enough - --> $DIR/reborrow_dependent_cyclic.rs:19:5 - | -19 | x.with_dependent(|_, dep| { - | ^ borrowed value does not live long enough - | _____| - | | -20 | | dep.0.borrow_mut().0 = Some(dep); -21 | | }); - | |______- argument requires that `x` is borrowed for `'static` -... -34 | } - | - `x` dropped here while still borrowed - -error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/reborrow_dependent_cyclic.rs:23:5 - | -19 | x.with_dependent(|_, dep| { - | - immutable borrow occurs here - | _____| - | | -20 | | dep.0.borrow_mut().0 = Some(dep); -21 | | }); - | |______- argument requires that `x` is borrowed for `'static` -22 | -23 | / x.with_dependent_mut(|_, dep| { -24 | | let r1 = dep.0.get_mut(); -25 | | let string_ref_1 = &mut r1.1; -26 | | let mut r2 = r1.0.unwrap().0.borrow_mut(); -... | -32 | | println!("{}", s); // prints garbage -33 | | }); - | |______^ mutable borrow occurs here diff --git a/tests-extra/invalid/swap_cell_member.rs b/tests-extra/invalid/swap_cell_member.rs deleted file mode 100644 index 2d88f1a..0000000 --- a/tests-extra/invalid/swap_cell_member.rs +++ /dev/null @@ -1,37 +0,0 @@ -use self_cell::self_cell; - -type Dep1<'a> = (&'a str, &'static str); - -self_cell! { - pub struct Struct1 { - owner: String, - #[covariant] - dependent: Dep1, - } -} - -type Dep2<'a> = (&'static str, &'a str); - -self_cell! { - pub struct Struct2 { - owner: String, - #[covariant] - dependent: Dep2, - } -} - -fn main() { - let hello: &'static str; - { - let mut x1 = Struct1::new(String::from("Hello World"), |s| (s, "")); - let mut x2 = Struct2::new(String::new(), |_| ("", "")); - std::mem::swap(&mut x1.unsafe_self_cell, &mut x2.unsafe_self_cell); - hello = x2.borrow_dependent().0; - - dbg!(hello); // "Hello World" - // hello is now a static reference in to the "Hello World" string - } - // the String is dropped at the end of the block above - - dbg!(hello); // prints garbage, use-after-free -} diff --git a/tests-extra/invalid/swap_cell_member.stderr b/tests-extra/invalid/swap_cell_member.stderr deleted file mode 100644 index 1223012..0000000 --- a/tests-extra/invalid/swap_cell_member.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/swap_cell_member.rs:28:50 - | -28 | std::mem::swap(&mut x1.unsafe_self_cell, &mut x2.unsafe_self_cell); - | ^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Struct1`, found struct `Struct2` - | - = note: expected mutable reference `&mut UnsafeSelfCell` - found mutable reference `&mut UnsafeSelfCell` diff --git a/tests-extra/invalid/with_mut_stack_use.rs b/tests-extra/invalid/with_mut_stack_use.rs deleted file mode 100644 index a7a7c65..0000000 --- a/tests-extra/invalid/with_mut_stack_use.rs +++ /dev/null @@ -1,22 +0,0 @@ -use self_cell::self_cell; - -type Dependent<'a> = &'a String; - -self_cell!( - struct MutStackUse { - owner: String, - - #[covariant] - dependent: Dependent, - } -); - -fn main() { - let outside_string = String::from("outside string"); - - let mut cell = MutStackUse::new("Crackle that thunder".into(), |owner| owner); - - cell.with_dependent_mut(|_, dependent| { - *dependent = &outside_string; - }); -} diff --git a/tests-extra/invalid/with_mut_stack_use.stderr b/tests-extra/invalid/with_mut_stack_use.stderr deleted file mode 100644 index d4a91f1..0000000 --- a/tests-extra/invalid/with_mut_stack_use.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0597]: `outside_string` does not live long enough - --> $DIR/with_mut_stack_use.rs:20:23 - | -19 | cell.with_dependent_mut(|_, dependent| { - | -------------- value captured here -20 | *dependent = &outside_string; - | --------------^^^^^^^^^^^^^^ - | | | - | | borrowed value does not live long enough - | assignment requires that `outside_string` is borrowed for `'static` -21 | }); -22 | } - | - `outside_string` dropped here while still borrowed diff --git a/tests-extra/invalid_manual/Cargo.toml b/tests-extra/invalid_manual/Cargo.toml deleted file mode 100644 index c97f184..0000000 --- a/tests-extra/invalid_manual/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "wrong_covariance", -] \ No newline at end of file diff --git a/tests-extra/invalid_manual/README.md b/tests-extra/invalid_manual/README.md deleted file mode 100644 index 6e5c9c0..0000000 --- a/tests-extra/invalid_manual/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Invalid compile testing - -Due to rustc version specific errors and line numbers changing all the time, -these tests of wrongly invoking the compiler are done in a manual fashion -instead of using trybuild. - diff --git a/tests-extra/invalid_manual/wrong_covariance/Cargo.toml b/tests-extra/invalid_manual/wrong_covariance/Cargo.toml deleted file mode 100644 index 6030a06..0000000 --- a/tests-extra/invalid_manual/wrong_covariance/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "wrong_covariance" -version = "0.1.0" -authors = ["Lukas Bergdoll "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -self_cell = { path="../../../" } \ No newline at end of file diff --git a/tests-extra/invalid_manual/wrong_covariance/expected.stderr b/tests-extra/invalid_manual/wrong_covariance/expected.stderr deleted file mode 100644 index c3ecc2e..0000000 --- a/tests-extra/invalid_manual/wrong_covariance/expected.stderr +++ /dev/null @@ -1,35 +0,0 @@ - --> IGNORE - | -7 | / self_cell!( -8 | | struct NoCov { -9 | | owner: String, -10 | | -... | -13 | | } -14 | | ); - | |__^ lifetime mismatch - | - = note: expected struct `Cell<&'y String>` - found struct `Cell<&'x String>` -note: the lifetime `'y` as defined on the function body at IGNORE - --> IGNORE - | -7 | / self_cell!( -8 | | struct NoCov { -9 | | owner: String, -10 | | -... | -13 | | } -14 | | ); - | |__^ -note: ...does not necessarily outlive the lifetime `'x` as defined on the function body at IGNORE - --> IGNORE - | -7 | / self_cell!( -8 | | struct NoCov { -9 | | owner: String, -10 | | -... | -13 | | } -14 | | ); - | |__^ diff --git a/tests-extra/invalid_manual/wrong_covariance/src/main.rs b/tests-extra/invalid_manual/wrong_covariance/src/main.rs deleted file mode 100644 index 48032f1..0000000 --- a/tests-extra/invalid_manual/wrong_covariance/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::cell::Cell; - -use self_cell::self_cell; - -type NotCovariant<'a> = Cell<&'a String>; - -self_cell!( - struct NoCov { - owner: String, - - #[covariant] - dependent: NotCovariant, - } -); - -fn main() { - let _cell = NoCov::new("hi this is no good".into(), |owner| Cell::new(owner)); -} diff --git a/tests-extra/src/lib.rs b/tests-extra/src/lib.rs deleted file mode 100644 index ae26dd9..0000000 --- a/tests-extra/src/lib.rs +++ /dev/null @@ -1,148 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] - -use std::fs; -use std::process::Command; -use std::str; - -use crossbeam_utils::thread; - -use impls::impls; - -use self_cell::self_cell; - -#[allow(dead_code)] -struct NotSend<'a> { - ptr: *mut i32, - derived: &'a String, -} - -impl<'a> From<&'a String> for NotSend<'a> { - fn from(s: &'a String) -> Self { - Self { - ptr: std::ptr::null_mut(), - derived: s, - } - } -} - -self_cell!( - struct NotSendCell { - owner: String, - - #[covariant] - dependent: NotSend, - } -); // no impl - -#[test] -fn not_send() { - // If owner and or dependent are not Send then the created cell - // should not be Send either. - - assert!(impls!(String: Send)); - assert!(!impls!(NotSend: Send)); - assert!(!impls!(NotSendCell: Send)); -} - -#[test] -fn not_sync() { - // If owner and or dependent are not Sync then the created cell - // should not be Sync either. - - assert!(impls!(String: Sync)); - assert!(!impls!(NotSend: Sync)); - assert!(!impls!(NotSendCell: Sync)); -} - -#[test] -// Not supported by miri isolation. -#[cfg_attr(miri, ignore)] -// Closure paths slashes show up as diff error on Windows. -#[cfg(not(target_os = "windows"))] -fn invalid_compile() { - let t = trybuild::TestCases::new(); - t.compile_fail("invalid/*.rs"); -} - -// Hacky custom version of try_build to support partial diffing. -fn try_build_manual(path: &str) { - let output = Command::new("cargo") - .arg("check") - .arg("--color=never") - .current_dir(path) - .output() - .unwrap(); - - let compile_err = str::from_utf8(&output.stderr).unwrap(); - - let expected_err = fs::read_to_string(format!("{}/expected.stderr", path)) - .unwrap() - .replace("IGNORE", ""); - - // Very naive approach. - for expected_line in expected_err.split("\n").map(str::trim) { - if !compile_err.contains(expected_line) { - eprintln!("Expected: '{}'\n\nIN\n\n{}", expected_line, compile_err); - panic!(); - } - } -} - -#[test] -// Not supported by miri isolation. -#[cfg_attr(miri, ignore)] -fn invalid_compile_manual() { - try_build_manual("invalid_manual/wrong_covariance"); -} - -#[derive(Clone, Debug, PartialEq)] -struct Ast<'a>(Vec<&'a str>); - -impl<'x> From<&'x String> for Ast<'x> { - fn from<'a>(body: &'a String) -> Ast<'a> { - Ast(vec![&body[0..1], &body[2..10]]) - } -} - -self_cell!( - struct AstCell { - owner: String, - - #[covariant] - dependent: Ast, - } - - impl {Debug} -); - -#[test] -fn share_across_threads() { - // drop_joined takes &mut self, so that's not a thread concern anyway. - // And get_or_init_dependent should be as thread compatible as OnceCell. - // Owner never gets changed after init. - - let body = String::from("hy hyperspeed"); - - // expected_ast is on the stack and lifetime dependent on body. - let expected_ast = Ast::from(&body); - - // But PackedAst is struct and can be freely moved and copied. - let packed_ast = AstCell::new(body.clone(), |o| o.into()); - - thread::scope(|s| { - s.spawn(|_| { - assert_eq!(packed_ast.borrow_owner(), &body); - assert_eq!(packed_ast.borrow_dependent(), &expected_ast); - }); - - s.spawn(|_| { - assert_eq!(packed_ast.borrow_owner(), &body); - assert_eq!(packed_ast.borrow_dependent(), &expected_ast); - }); - - assert_eq!(packed_ast.borrow_owner(), &body); - assert_eq!(packed_ast.borrow_dependent(), &expected_ast); - }) - .unwrap(); -} diff --git a/tests/self_cell.rs b/tests/self_cell.rs deleted file mode 100644 index 442eda8..0000000 --- a/tests/self_cell.rs +++ /dev/null @@ -1,751 +0,0 @@ -// The unsafe being used gets tested with miri in the CI. - -#![deny(private_in_public)] - -use std::cell::Cell; -use std::cell::RefCell; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::panic::catch_unwind; -use std::rc::Rc; -use std::str; - -use once_cell::unsync::OnceCell; - -use self_cell::self_cell; - -#[derive(Debug, Eq, PartialEq)] -pub struct Ast<'input>(pub Vec<&'input str>); - -impl<'x> From<&'x String> for Ast<'x> { - fn from<'a>(body: &'a String) -> Ast<'a> { - Ast(vec![&body[2..5], &body[1..3]]) - } -} - -self_cell!( - #[doc(hidden)] - struct PackedAstCell { - owner: String, - - #[covariant] - dependent: Ast, - } - - impl {Debug, PartialEq, Eq, Hash} -); - -impl Clone for PackedAstCell { - fn clone(&self) -> Self { - PackedAstCell::new(self.borrow_owner().clone(), |owner| owner.into()) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -struct PackedAst { - ast_cell: PackedAstCell, -} - -impl PackedAst { - fn new(body: String) -> Self { - Self { - ast_cell: PackedAstCell::new(body, |owner| owner.into()), - } - } - - fn get_body<'a>(&'a self) -> &'a String { - self.ast_cell.borrow_owner() - } - - fn with_ast<'o>(&'o self, func: impl for<'a> FnOnce(&'a String, &'o Ast<'a>)) { - self.ast_cell.with_dependent(func) - } - - fn get_ast<'a>(&'a self) -> &'a Ast<'a> { - self.ast_cell.borrow_dependent() - } -} - -fn assert_with_ast(packed_ast: &PackedAst, expected_ast: &Ast) { - let mut visited = false; - packed_ast.with_ast(|_, ast| { - assert_eq!(ast, expected_ast); - visited = true; - }); - assert!(visited); -} - -#[test] -fn parse_ast() { - let body = String::from("some longer string that ends now"); - - // expected_ast is on the stack and lifetime dependent on body. - let expected_ast = Ast::from(&body); - - // But PackedAst is struct and can be freely moved and copied. - let packed_ast = PackedAst::new(body.clone()); - assert_eq!(packed_ast.get_body(), &body); - assert_with_ast(&packed_ast, &expected_ast); - assert_eq!(packed_ast.get_ast(), &expected_ast); - - assert_eq!( - format!("{:?}", &packed_ast), - "PackedAst { ast_cell: PackedAstCell { owner: \"some longer string that ends now\", dependent: Ast([\"me \", \"om\"]) } }" - ); - - let cloned_packed_ast = packed_ast.clone(); - assert_eq!(cloned_packed_ast.get_body(), &body); - assert_with_ast(&cloned_packed_ast, &expected_ast); - assert_eq!(cloned_packed_ast.get_ast(), &expected_ast); - - let moved_packed_ast = packed_ast; - assert_eq!(moved_packed_ast.get_body(), &body); - assert_with_ast(&moved_packed_ast, &expected_ast); - assert_eq!(moved_packed_ast.get_ast(), &expected_ast); - - // Assert that even though the original packed_ast was moved, the clone of it is still valid. - assert_eq!(cloned_packed_ast.get_body(), &body); - assert_with_ast(&cloned_packed_ast, &expected_ast); - assert_eq!(cloned_packed_ast.get_ast(), &expected_ast); - - // Assert that even though the original packed_ast was dropped, the clone of it is still valid. - drop(moved_packed_ast); - assert_eq!(cloned_packed_ast.get_body(), &body); - assert_with_ast(&cloned_packed_ast, &expected_ast); - assert_eq!(cloned_packed_ast.get_ast(), &expected_ast); -} - -fn make_ast_with_stripped_body(body: &str) -> PackedAst { - // This is created on the stack. - let stripped_body = body.replace("\n", ""); - // Return Ast built from moved body, no lifetime hassle. - PackedAst::new(stripped_body) -} - -#[test] -fn return_self_ref_struct() { - let body = String::from("a\nb\nc\ndef"); - let expected_body = body.replace("\n", ""); - - // expected_ast is on the stack and lifetime dependent on body. - let expected_ast = Ast::from(&expected_body); - - let packed_ast = make_ast_with_stripped_body(&body); - assert_eq!(packed_ast.get_body(), &expected_body); - assert_eq!(packed_ast.get_ast(), &expected_ast); -} - -#[test] -fn failable_constructor_success() { - let owner = String::from("This string is no trout"); - let expected_ast = Ast::from(&owner); - - let ast_cell_result: Result = - PackedAstCell::try_new(owner.clone(), |owner| Ok(Ast::from(owner))); - - assert!(ast_cell_result.is_ok()); - - let ast_cell = ast_cell_result.unwrap(); - assert_eq!(ast_cell.borrow_owner(), &owner); - assert_eq!(ast_cell.borrow_dependent(), &expected_ast); -} - -#[test] -fn failable_constructor_fail() { - let owner = String::from("This string is no trout"); - - let ast_cell_result: Result = - PackedAstCell::try_new(owner.clone(), |_owner| Err(22)); - - assert!(ast_cell_result.is_err()); - - let err = ast_cell_result.unwrap_err(); - assert_eq!(err, 22); -} - -#[test] -fn from_fn() { - #[derive(Debug)] - struct Dependent<'a>(&'a String); - - self_cell!( - struct FnCell { - owner: String, - - #[covariant] - dependent: Dependent, - } - - impl {Debug} - ); - - let mut extra_outside_state = None; - - assert_eq!(extra_outside_state, None); - - let expected_str = "small pink bike"; - - let fn_cell = FnCell::new(expected_str.clone().into(), |owner| { - // Make sure it only gets called once. - extra_outside_state = if let Some(x) = extra_outside_state { - Some(x + 5) - } else { - Some(66) - }; - - Dependent(owner) - }); - - assert_eq!(extra_outside_state, Some(66)); - assert_eq!(fn_cell.borrow_owner(), expected_str); - assert_eq!(fn_cell.borrow_dependent().0, expected_str); - assert_eq!(extra_outside_state, Some(66)); -} - -#[test] -fn catch_panic_in_from() { - // This pattern allows users to opt into not leaking memory on panic during - // cell construction. - - #[derive(Debug, Clone, PartialEq)] - struct Owner(String); - - #[derive(Debug)] - struct PanicCtor<'a>(&'a i32); - - impl<'a> PanicCtor<'a> { - fn new(_: &'a Owner) -> Self { - let _stack_vec = vec![23, 44, 5]; - panic!() - } - } - - self_cell!( - struct NoLeakCell { - owner: Owner, - - #[covariant] - dependent: PanicCtor, - - } - - impl {Debug} - ); - - let owner = Owner("This string is no trout".into()); - - let ast_cell_result = NoLeakCell::try_new(owner.clone(), |owner| { - catch_unwind(|| PanicCtor::new(&owner)) - }); - assert!(ast_cell_result.is_err()); -} - -#[test] -fn no_derive_owner_type() { - #[derive(Debug)] - struct NoDerive(i32); - - #[derive(Debug)] - struct Dependent<'a>(&'a i32); - - self_cell!( - struct NoDeriveCell { - owner: NoDerive, - - #[covariant] - dependent: Dependent, - } - - impl {Debug} - ); - let no_derive = NoDeriveCell::new(NoDerive(22), |owner| Dependent(&owner.0)); - assert_eq!(no_derive.borrow_dependent().0, &22); -} - -#[test] -fn public_cell() { - self_cell!( - pub struct PubCell { - owner: String, - - #[covariant] - dependent: Ast, - } - - impl {} // empty impl - ); - - #[allow(dead_code)] - pub type PubTy = PubCell; -} - -#[test] -fn custom_drop() { - #[derive(Debug, PartialEq, Eq)] - struct Ref<'a, T: Debug>(&'a T); - - impl<'a, T: Debug> Drop for Ref<'a, T> { - fn drop(&mut self) { - println!("{:?}", self.0); - } - } - - #[derive(Debug, PartialEq, Eq)] - enum Void {} - - type OV = Option>; - type OvRef<'a> = Ref<'a, OV>; - - self_cell!( - struct CustomDrop { - owner: OV, - - #[covariant] - dependent: OvRef, - } - - impl {Debug, PartialEq, Eq} - ); - - let cell = CustomDrop::new(None, |owner| Ref(owner)); - - let expected_dependent = Ref::<'_, OV>(&None); - - assert_eq!(cell.borrow_dependent(), &expected_dependent); -} - -#[test] -fn drop_order() { - #[derive(Debug, PartialEq, Eq)] - enum Dropped { - Owner, - Dependent, - } - - struct Owner(Rc>>); - struct Dependent<'a>(&'a Owner, Rc>>); - - impl Drop for Owner { - fn drop(&mut self) { - self.0.borrow_mut().push(Dropped::Owner) - } - } - - impl Drop for Dependent<'_> { - fn drop(&mut self) { - self.1.borrow_mut().push(Dropped::Dependent) - } - } - - self_cell! { - struct DropOrder { - owner: Owner, - - #[covariant] - dependent: Dependent, - } - } - - let drops: Rc>> = <_>::default(); - let cell = DropOrder::new(Owner(drops.clone()), |o| Dependent(o, drops.clone())); - drop(cell); - assert_eq!(&drops.borrow()[..], &[Dropped::Dependent, Dropped::Owner]); -} - -#[test] -fn into_owner_drop_dependent_without_panic() { - // This test resulted in a double-free in a previous version of self-cell - type O = Cell>>; - - self_cell! { - struct S { - owner: O, - - #[covariant] - dependent: D, - } - } - - struct D<'a>(&'a O); - - impl Drop for D<'_> { - fn drop(&mut self) { - self.0.take(); - } - } - - let s = S::new(Cell::new(Some(Box::new(42))), |o| D(o)); - assert!(s.into_owner().into_inner().is_none()); -} - -#[test] -#[should_panic] // but should not leak or double-free -fn into_owner_drop_dependent_with_panic() { - type O = Cell>>; - - self_cell! { - struct S { - owner: O, - - #[covariant] - dependent: D, - } - } - - struct D<'a>(&'a O); - - impl Drop for D<'_> { - fn drop(&mut self) { - self.0.take(); - panic!(); - } - } - - let s = S::new(Cell::new(Some(Box::new(42))), |o| D(o)); - s.into_owner(); -} - -#[test] -fn drop_panic_owner() { - #[derive(Clone, Debug, PartialEq)] - struct Owner(String); - - type Dependent<'a> = &'a Owner; - - self_cell! { - struct DropPanicOwner { - owner: Owner, - - #[covariant] - dependent: Dependent, - } - } - - impl Drop for Owner { - fn drop(&mut self) { - panic!() - } - } - - let owner = Owner("s t e f f a h n <3 and some padding against sbo".into()); - - let cell = DropPanicOwner::new(owner.clone(), |o| o); - assert_eq!(cell.borrow_owner(), &owner); - assert_eq!(cell.borrow_dependent(), &&owner); - - assert!(std::panic::catch_unwind(move || drop(cell)).is_err()); - assert!(std::panic::catch_unwind(move || drop(owner)).is_err()); -} - -#[test] -fn drop_panic_dependent() { - #[derive(Clone, Debug, PartialEq)] - struct Owner(String); - - struct Dependent<'a>(&'a Owner); - - self_cell! { - struct DropPanicDependent { - owner: Owner, - - #[covariant] - dependent: Dependent, - } - } - - impl Drop for Dependent<'_> { - fn drop(&mut self) { - panic!() - } - } - - let owner = Owner("s t e f f a h n <3".into()); - - let cell = DropPanicDependent::new(owner.clone(), |o| Dependent(o)); - assert_eq!(cell.borrow_owner(), &owner); - assert_eq!(cell.borrow_dependent().0, &owner); - - assert!(std::panic::catch_unwind(move || drop(cell)).is_err()); -} - -#[test] -fn dependent_mutate() { - let mut ast_cell = PackedAstCell::new("Egal in welchen Farben ihr den ..".into(), |owner| { - owner.into() - }); - - assert_eq!(ast_cell.borrow_dependent().0.len(), 2); - - ast_cell.with_dependent_mut(|_, ast| { - ast.0.clear(); - }); - - assert_eq!(ast_cell.borrow_dependent().0.len(), 0); -} - -#[test] -fn dependent_replace() { - let before_input = String::from("Egal in welchen Farben ihr den .."); - let before_ast_expected = Ast::from(&before_input); - - let mut ast_cell = PackedAstCell::new(before_input.clone(), |owner| owner.into()); - - assert_eq!(ast_cell.borrow_owner(), &before_input); - assert_eq!(ast_cell.borrow_dependent(), &before_ast_expected); - - ast_cell.with_dependent_mut(|owner, ast| { - *ast = Ast(vec![&owner[0..2], &owner[0..3], &owner[5..9]]); - }); - - assert_eq!(ast_cell.borrow_dependent().0, vec!["Eg", "Ega", "in w"]); -} - -#[test] -fn try_new_or_recover() { - let original_input = String::from("Ein See aus Schweiß .."); - - // bad path - let (input, err) = - PackedAstCell::try_new_or_recover(original_input.clone(), |_| Err(-1)).unwrap_err(); - - assert_eq!(original_input, input); - assert_eq!(err, -1); - - // happy path - let cell = PackedAstCell::try_new_or_recover(original_input.clone(), |o| -> Result<_, ()> { - Ok(o.into()) - }) - .unwrap(); - assert_eq!(cell.borrow_owner(), &original_input); - assert_eq!(cell.borrow_dependent(), &Ast::from(&original_input)); -} - -#[test] -fn into_owner() { - // The Rc stuff here is somewhat tangential to what is being tested here. - // The regular type system should enforce most of the invariants of into_owner - // and miri should detect leaks. - - self_cell!( - struct RcAstCell { - owner: Rc, - - #[covariant] - dependent: Ast, - } - ); - - let expected_body = Rc::new(String::from("Endless joy for you never 2")); - - // expected_ast is on the stack and lifetime dependent on body. - let expected_ast = Ast::from(&*expected_body); - - let ast_cell = RcAstCell::new(Rc::clone(&expected_body), |s| Ast::from(&**s)); - assert_eq!(ast_cell.borrow_owner(), &expected_body); - assert_eq!(ast_cell.borrow_dependent(), &expected_ast); - - let body_recovered: Rc = ast_cell.into_owner(); - assert_eq!(&body_recovered, &expected_body); - assert_eq!(Rc::strong_count(&expected_body), 2); - - // This shouldn't be possible anymore. - // assert_eq!(ast_cell.borrow_owner(), &expected_body); -} - -#[test] -fn zero_size_cell() { - struct ZeroSizeRef<'a>(PhantomData<&'a ()>); - - self_cell!( - struct ZeroSizeCell { - owner: (), - - #[covariant] - dependent: ZeroSizeRef, - } - ); - - assert!(catch_unwind(|| ZeroSizeCell::new((), |_| ZeroSizeRef(PhantomData))).is_err()); - - assert!( - catch_unwind(|| ZeroSizeCell::try_new((), |_| -> Result<_, i32> { - Ok(ZeroSizeRef(PhantomData)) - })) - .is_err() - ); - - assert!(catch_unwind( - || ZeroSizeCell::try_new_or_recover((), |_| -> Result<_, i32> { - Ok(ZeroSizeRef(PhantomData)) - }) - ) - .is_err()); -} - -#[test] -fn nested_cells() { - // TODO allow automatic impls. - // impl {Debug, PartialEq, Eq, Hash} - self_cell!( - struct ChildCell<'a> { - owner: &'a String, - - #[covariant] - dependent: Ast, - } - ); - - self_cell!( - struct ParentCell { - owner: String, - - #[covariant] - dependent: ChildCell, - } - ); - - let parent_owner_expected = String::from("some string it is"); - let ast_expected = Ast::from(&parent_owner_expected); - - let parent_cell = ParentCell::new(parent_owner_expected.clone(), |parent| { - ChildCell::new(parent, |child| Ast::from(*child)) - }); - - assert_eq!(parent_cell.borrow_owner(), &parent_owner_expected); - - let child_cell = parent_cell.borrow_dependent(); - assert_eq!(*child_cell.borrow_owner(), &parent_owner_expected); - assert_eq!(child_cell.borrow_dependent(), &ast_expected); -} - -// partial nested cells - -#[test] -fn panic_in_from_owner() { - // panicing in user provided code shouldn't leak memory. - - type Dependent<'a> = &'a String; - - self_cell!( - struct PanicCell { - owner: String, - - #[covariant] - dependent: Dependent, - } - ); - - let owner = String::from("panic_in_from_owner"); - - let new_result = std::panic::catch_unwind(|| { - let _ = PanicCell::new(owner.clone(), |_| panic!()); - }); - assert!(new_result.is_err()); - - let try_new_result = std::panic::catch_unwind(|| { - let _ = PanicCell::try_new(owner.clone(), |_| -> Result<_, Box> { panic!() }); - }); - assert!(try_new_result.is_err()); - - let try_new_or_recover_result = std::panic::catch_unwind(|| { - let _ = PanicCell::try_new_or_recover(owner.clone(), |_| -> Result<_, i32> { panic!() }); - }); - assert!(try_new_or_recover_result.is_err()); -} - -#[test] -fn result_name_hygiene() { - // See https://github.com/Voultapher/self_cell/issues/16 - #[allow(dead_code)] - type Result = std::result::Result; - - type VecRef<'a> = &'a Vec; - - self_cell!( - struct SomeCell { - owner: Vec, - - #[covariant] - dependent: VecRef, - } - - impl {Debug, PartialEq, Eq, Hash} - ); -} - -#[test] -fn debug_impl() { - // See https://github.com/Voultapher/self_cell/pull/22 - let ast_cell = PackedAstCell::new("xyz, abv".into(), |owner| owner.into()); - - assert_eq!( - format!("{:?}", &ast_cell), - "PackedAstCell { owner: \"xyz, abv\", dependent: Ast([\"z, \", \"yz\"]) }" - ); - - let hash_fmt = r#"PackedAstCell { - owner: "xyz, abv", - dependent: Ast( - [ - "z, ", - "yz", - ], - ), -}"#; - - assert_eq!(format!("{:#?}", ast_cell), hash_fmt); -} - -#[test] -fn lazy_ast() { - #[derive(Debug)] - struct LazyAst<'a>(OnceCell>); - - impl<'a> From<&'a String> for LazyAst<'a> { - fn from(_: &'a String) -> Self { - Self(OnceCell::new()) - } - } - - self_cell!( - #[doc(hidden)] - struct LazyAstCell { - owner: String, - - #[not_covariant] - dependent: LazyAst, - } - - impl {Debug, PartialEq, Eq, Hash} - ); - - let body = String::from("How thou shall not see what trouts shall see"); - - let expected_ast = Ast::from(&body); - - let lazy_ast = LazyAstCell::new(body.clone(), |owner| owner.into()); - assert_eq!(lazy_ast.borrow_owner(), &body); - lazy_ast.with_dependent(|owner, dependent| { - assert_eq!(owner, &body); - assert!(dependent.0.get().is_none()); - }); - - lazy_ast.with_dependent(|owner, dependent| { - assert_eq!(owner, &body); - assert_eq!(dependent.0.get_or_init(|| owner.into()), &expected_ast); - }); - - lazy_ast.with_dependent(|owner, dependent| { - assert_eq!(owner, &body); - assert!(dependent.0.get().is_some()); - assert_eq!(dependent.0.get_or_init(|| owner.into()), &expected_ast); - }); -} - -#[test] -fn cell_mem_size() { - use std::mem::size_of; - - assert_eq!(size_of::(), size_of::<*const u8>()); - assert_eq!(size_of::>(), size_of::<*const u8>()); -}