about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_abi/src/layout.rs8
-rw-r--r--compiler/rustc_hir/src/lang_items.rs3
-rw-r--r--compiler/rustc_lint/src/types.rs4
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs13
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs7
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/marker.rs29
-rw-r--r--library/core/src/pin.rs5
-rw-r--r--library/core/src/pin/unsafe_pinned.rs197
-rw-r--r--tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs29
11 files changed, 290 insertions, 8 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 7bffeaf4cc9..42250aa173b 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -315,7 +315,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         repr: &ReprOptions,
         variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
         is_enum: bool,
-        is_unsafe_cell: bool,
+        is_special_no_niche: bool,
         scalar_valid_range: (Bound<u128>, Bound<u128>),
         discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
         discriminants: impl Iterator<Item = (VariantIdx, i128)>,
@@ -348,7 +348,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 repr,
                 variants,
                 is_enum,
-                is_unsafe_cell,
+                is_special_no_niche,
                 scalar_valid_range,
                 always_sized,
                 present_first,
@@ -505,7 +505,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         repr: &ReprOptions,
         variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
         is_enum: bool,
-        is_unsafe_cell: bool,
+        is_special_no_niche: bool,
         scalar_valid_range: (Bound<u128>, Bound<u128>),
         always_sized: bool,
         present_first: VariantIdx,
@@ -524,7 +524,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         let mut st = self.univariant(&variants[v], repr, kind)?;
         st.variants = Variants::Single { index: v };
 
-        if is_unsafe_cell {
+        if is_special_no_niche {
             let hide_niches = |scalar: &mut _| match scalar {
                 Scalar::Initialized { value, valid_range } => {
                     *valid_range = WrappingRange::full(value.size(dl))
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 90fab01ba2d..d4488b4b657 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -182,6 +182,7 @@ language_item_table! {
     DynMetadata,             sym::dyn_metadata,        dyn_metadata,               Target::Struct,         GenericRequirement::None;
 
     Freeze,                  sym::freeze,              freeze_trait,               Target::Trait,          GenericRequirement::Exact(0);
+    UnsafeUnpin,             sym::unsafe_unpin,        unsafe_unpin_trait,         Target::Trait,          GenericRequirement::Exact(0);
 
     FnPtrTrait,              sym::fn_ptr_trait,        fn_ptr_trait,               Target::Trait,          GenericRequirement::Exact(0);
     FnPtrAddr,               sym::fn_ptr_addr,         fn_ptr_addr,                Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
@@ -235,6 +236,8 @@ language_item_table! {
     IndexMut,                sym::index_mut,           index_mut_trait,            Target::Trait,          GenericRequirement::Exact(1);
 
     UnsafeCell,              sym::unsafe_cell,         unsafe_cell_type,           Target::Struct,         GenericRequirement::None;
+    UnsafePinned,            sym::unsafe_pinned,       unsafe_pinned_type,         Target::Struct,         GenericRequirement::None;
+
     VaList,                  sym::va_list,             va_list,                    Target::Struct,         GenericRequirement::None;
 
     Deref,                   sym::deref,               deref_trait,                Target::Trait,          GenericRequirement::Exact(0);
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index a6c82f574a1..a084dacfb5b 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -864,8 +864,8 @@ fn ty_is_known_nonnull<'tcx>(
                 return true;
             }
 
-            // `UnsafeCell` has its niche hidden.
-            if def.is_unsafe_cell() {
+            // `UnsafeCell` and `UnsafePinned` have their niche hidden.
+            if def.is_unsafe_cell() || def.is_unsafe_pinned() {
                 return false;
             }
 
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 00fe5cb0c5d..66517c97a68 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -53,6 +53,10 @@ bitflags::bitflags! {
         const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
         /// Indicates whether the type is `UnsafeCell`.
         const IS_UNSAFE_CELL              = 1 << 9;
+        /// Indicates whether the type is `UnsafePinned`.
+        const IS_UNSAFE_PINNED              = 1 << 10;
+        /// Indicates whether the type is anonymous.
+        const IS_ANONYMOUS                = 1 << 11;
     }
 }
 rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -302,6 +306,9 @@ impl AdtDefData {
         if tcx.is_lang_item(did, LangItem::UnsafeCell) {
             flags |= AdtFlags::IS_UNSAFE_CELL;
         }
+        if tcx.is_lang_item(did, LangItem::UnsafePinned) {
+            flags |= AdtFlags::IS_UNSAFE_PINNED;
+        }
 
         AdtDefData { did, variants, flags, repr }
     }
@@ -405,6 +412,12 @@ impl<'tcx> AdtDef<'tcx> {
         self.flags().contains(AdtFlags::IS_UNSAFE_CELL)
     }
 
+    /// Returns `true` if this is `UnsafePinned<T>`.
+    #[inline]
+    pub fn is_unsafe_pinned(self) -> bool {
+        self.flags().contains(AdtFlags::IS_UNSAFE_PINNED)
+    }
+
     /// Returns `true` if this is `ManuallyDrop<T>`.
     #[inline]
     pub fn is_manually_drop(self) -> bool {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 986370f5019..63be641d0c1 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2215,6 +2215,8 @@ symbols! {
         unsafe_fields,
         unsafe_no_drop_flag,
         unsafe_pin_internals,
+        unsafe_pinned,
+        unsafe_unpin,
         unsize,
         unsized_const_param_ty,
         unsized_const_params,
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 0017186c1b0..1915ba623cb 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -514,6 +514,9 @@ fn layout_of_uncached<'tcx>(
                 return map_layout(cx.calc.layout_of_union(&def.repr(), &variants));
             }
 
+            // UnsafeCell and UnsafePinned both disable niche optimizations
+            let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned();
+
             let get_discriminant_type =
                 |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max);
 
@@ -542,7 +545,7 @@ fn layout_of_uncached<'tcx>(
                     &def.repr(),
                     &variants,
                     def.is_enum(),
-                    def.is_unsafe_cell(),
+                    is_special_no_niche,
                     tcx.layout_scalar_valid_range(def.did()),
                     get_discriminant_type,
                     discriminants_iter(),
@@ -568,7 +571,7 @@ fn layout_of_uncached<'tcx>(
                     &def.repr(),
                     &variants,
                     def.is_enum(),
-                    def.is_unsafe_cell(),
+                    is_special_no_niche,
                     tcx.layout_scalar_valid_range(def.did()),
                     get_discriminant_type,
                     discriminants_iter(),
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index dc06aa4c38d..5f701921eb6 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -127,6 +127,7 @@
 #![feature(ub_checks)]
 #![feature(unchecked_neg)]
 #![feature(unchecked_shifts)]
+#![feature(unsafe_pinned)]
 #![feature(utf16_extra)]
 #![feature(variant_count)]
 // tidy-alphabetical-end
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 68011310d2c..9dc20beda6c 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -17,6 +17,7 @@ use crate::cell::UnsafeCell;
 use crate::cmp;
 use crate::fmt::Debug;
 use crate::hash::{Hash, Hasher};
+use crate::pin::UnsafePinned;
 
 /// Implements a given marker trait for multiple types at the same time.
 ///
@@ -878,6 +879,23 @@ marker_impls! {
         {T: ?Sized} &mut T,
 }
 
+/// Used to determine whether a type contains any `UnsafePinned` (or `PhantomPinned`) internally,
+/// but not through an indirection. This affects, for example, whether we emit `noalias` metadata
+/// for `&mut T` or not.
+///
+/// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is
+/// tracked by [#125735](https://github.com/rust-lang/rust/issues/125735).
+#[cfg_attr(not(bootstrap), lang = "unsafe_unpin")]
+#[cfg_attr(bootstrap, allow(dead_code))]
+pub(crate) unsafe auto trait UnsafeUnpin {}
+
+impl<T: ?Sized> !UnsafeUnpin for UnsafePinned<T> {}
+unsafe impl<T: ?Sized> UnsafeUnpin for PhantomData<T> {}
+unsafe impl<T: ?Sized> UnsafeUnpin for *const T {}
+unsafe impl<T: ?Sized> UnsafeUnpin for *mut T {}
+unsafe impl<T: ?Sized> UnsafeUnpin for &T {}
+unsafe impl<T: ?Sized> UnsafeUnpin for &mut T {}
+
 /// Types that do not require any pinning guarantees.
 ///
 /// For information on what "pinning" is, see the [`pin` module] documentation.
@@ -953,6 +971,11 @@ pub auto trait Unpin {}
 /// A marker type which does not implement `Unpin`.
 ///
 /// If a type contains a `PhantomPinned`, it will not implement `Unpin` by default.
+//
+// FIXME(unsafe_pinned): This is *not* a stable guarantee we want to make, at least not yet.
+// Note that for backwards compatibility with the new [`UnsafePinned`] wrapper type, placing this
+// marker in your struct acts as if you wrapped the entire struct in an `UnsafePinned`. This type
+// will likely eventually be deprecated, and all new code should be using `UnsafePinned` instead.
 #[stable(feature = "pin", since = "1.33.0")]
 #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
 pub struct PhantomPinned;
@@ -960,6 +983,12 @@ pub struct PhantomPinned;
 #[stable(feature = "pin", since = "1.33.0")]
 impl !Unpin for PhantomPinned {}
 
+// This is a small hack to allow existing code which uses PhantomPinned to opt-out of noalias to
+// continue working. Ideally PhantomPinned could just wrap an `UnsafePinned<()>` to get the same
+// effect, but we can't add a new field to an already stable unit struct -- that would be a breaking
+// change.
+impl !UnsafeUnpin for PhantomPinned {}
+
 marker_impls! {
     #[stable(feature = "pin", since = "1.33.0")]
     Unpin for
diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs
index 2ef1bbfd1fa..48ed5ae451e 100644
--- a/library/core/src/pin.rs
+++ b/library/core/src/pin.rs
@@ -931,6 +931,11 @@ use crate::{
 };
 use crate::{cmp, fmt};
 
+mod unsafe_pinned;
+
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+pub use self::unsafe_pinned::UnsafePinned;
+
 /// A pointer which pins its pointee in place.
 ///
 /// [`Pin`] is a wrapper around some kind of pointer `Ptr` which makes that pointer "pin" its
diff --git a/library/core/src/pin/unsafe_pinned.rs b/library/core/src/pin/unsafe_pinned.rs
new file mode 100644
index 00000000000..5fb628c8adb
--- /dev/null
+++ b/library/core/src/pin/unsafe_pinned.rs
@@ -0,0 +1,197 @@
+use crate::marker::{PointerLike, Unpin};
+use crate::ops::{CoerceUnsized, DispatchFromDyn};
+use crate::pin::Pin;
+use crate::{fmt, ptr};
+
+/// This type provides a way to opt-out of typical aliasing rules;
+/// specifically, `&mut UnsafePinned<T>` is not guaranteed to be a unique pointer.
+///
+/// However, even if you define your type like `pub struct Wrapper(UnsafePinned<...>)`, it is still
+/// very risky to have an `&mut Wrapper` that aliases anything else. Many functions that work
+/// generically on `&mut T` assume that the memory that stores `T` is uniquely owned (such as
+/// `mem::swap`). In other words, while having aliasing with `&mut Wrapper` is not immediate
+/// Undefined Behavior, it is still unsound to expose such a mutable reference to code you do not
+/// control! Techniques such as pinning via [`Pin`] are needed to ensure soundness.
+///
+/// Similar to [`UnsafeCell`](crate::cell::UnsafeCell), `UnsafePinned` will not usually show up in
+/// the public API of a library. It is an internal implementation detail of libraries that need to
+/// support aliasing mutable references.
+///
+/// Further note that this does *not* lift the requirement that shared references must be read-only!
+/// Use `UnsafeCell` for that.
+///
+/// This type blocks niches the same way `UnsafeCell` does.
+#[cfg_attr(not(bootstrap), lang = "unsafe_pinned")]
+#[repr(transparent)]
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+pub struct UnsafePinned<T: ?Sized> {
+    value: T,
+}
+
+/// When this type is used, that almost certainly means safe APIs need to use pinning to avoid the
+/// aliases from becoming invalidated. Therefore let's mark this as `!Unpin`. You can always opt
+/// back in to `Unpin` with an `impl` block, provided your API is still sound while unpinned.
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: ?Sized> !Unpin for UnsafePinned<T> {}
+
+/// The type is `Copy` when `T` is to avoid people assuming that `Copy` implies there is no
+/// `UnsafePinned` anywhere. (This is an issue with `UnsafeCell`: people use `Copy` bounds to mean
+/// `Freeze`.) Given that there is no `unsafe impl Copy for ...`, this is also the option that
+/// leaves the user more choices (as they can always wrap this in a `!Copy` type).
+// FIXME(unsafe_pinned): this may be unsound or a footgun?
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: Copy> Copy for UnsafePinned<T> {}
+
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: Copy> Clone for UnsafePinned<T> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+// `Send` and `Sync` are inherited from `T`. This is similar to `SyncUnsafeCell`, since
+// we eventually concluded that `UnsafeCell` implicitly making things `!Sync` is sometimes
+// unergonomic. A type that needs to be `!Send`/`!Sync` should really have an explicit
+// opt-out itself, e.g. via an `PhantomData<*mut T>` or (one day) via `impl !Send`/`impl !Sync`.
+
+impl<T> UnsafePinned<T> {
+    /// Constructs a new instance of `UnsafePinned` which will wrap the specified value.
+    ///
+    /// All access to the inner value through `&UnsafePinned<T>` or `&mut UnsafePinned<T>` or
+    /// `Pin<&mut UnsafePinned<T>>` requires `unsafe` code.
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    pub const fn new(value: T) -> Self {
+        UnsafePinned { value }
+    }
+
+    /// Unwraps the value, consuming this `UnsafePinned`.
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
+    pub const fn into_inner(self) -> T {
+        self.value
+    }
+}
+
+impl<T: ?Sized> UnsafePinned<T> {
+    /// Get read-write access to the contents of a pinned `UnsafePinned`.
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    pub const fn get_mut_pinned(self: Pin<&mut Self>) -> *mut T {
+        // SAFETY: we're not using `get_unchecked_mut` to unpin anything
+        unsafe { self.get_unchecked_mut() }.get_mut_unchecked()
+    }
+
+    /// Get read-write access to the contents of an `UnsafePinned`.
+    ///
+    /// You should usually be using `get_mut_pinned` instead to explicitly track the fact that this
+    /// memory is "pinned" due to there being aliases.
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    pub const fn get_mut_unchecked(&mut self) -> *mut T {
+        ptr::from_mut(self) as *mut T
+    }
+
+    /// Get read-only access to the contents of a shared `UnsafePinned`.
+    ///
+    /// Note that `&UnsafePinned<T>` is read-only if `&T` is read-only. This means that if there is
+    /// mutation of the `T`, future reads from the `*const T` returned here are UB! Use
+    /// [`UnsafeCell`] if you also need interior mutability.
+    ///
+    /// [`UnsafeCell`]: crate::cell::UnsafeCell
+    ///
+    /// ```rust,no_run
+    /// #![feature(unsafe_pinned)]
+    /// use std::pin::UnsafePinned;
+    ///
+    /// unsafe {
+    ///     let mut x = UnsafePinned::new(0);
+    ///     let ptr = x.get(); // read-only pointer, assumes immutability
+    ///     x.get_mut_unchecked().write(1);
+    ///     ptr.read(); // UB!
+    /// }
+    /// ```
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    pub const fn get(&self) -> *const T {
+        ptr::from_ref(self) as *const T
+    }
+
+    /// Gets an immutable pointer to the wrapped value.
+    ///
+    /// The difference from [`get`] is that this function accepts a raw pointer, which is useful to
+    /// avoid the creation of temporary references.
+    ///
+    /// [`get`]: UnsafePinned::get
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    pub const fn raw_get(this: *const Self) -> *const T {
+        this as *const T
+    }
+
+    /// Gets a mutable pointer to the wrapped value.
+    ///
+    /// The difference from [`get_mut_pinned`] and [`get_mut_unchecked`] is that this function
+    /// accepts a raw pointer, which is useful to avoid the creation of temporary references.
+    ///
+    /// [`get_mut_pinned`]: UnsafePinned::get_mut_pinned
+    /// [`get_mut_unchecked`]: UnsafePinned::get_mut_unchecked
+    #[inline(always)]
+    #[must_use]
+    #[unstable(feature = "unsafe_pinned", issue = "125735")]
+    pub const fn raw_get_mut(this: *mut Self) -> *mut T {
+        this as *mut T
+    }
+}
+
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: Default> Default for UnsafePinned<T> {
+    /// Creates an `UnsafePinned`, with the `Default` value for T.
+    fn default() -> Self {
+        UnsafePinned::new(T::default())
+    }
+}
+
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T> From<T> for UnsafePinned<T> {
+    /// Creates a new `UnsafePinned<T>` containing the given value.
+    fn from(value: T) -> Self {
+        UnsafePinned::new(value)
+    }
+}
+
+#[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: ?Sized> fmt::Debug for UnsafePinned<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("UnsafePinned").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "coerce_unsized", issue = "18598")]
+// #[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: CoerceUnsized<U>, U> CoerceUnsized<UnsafePinned<U>> for UnsafePinned<T> {}
+
+// Allow types that wrap `UnsafePinned` to also implement `DispatchFromDyn`
+// and become dyn-compatible method receivers.
+// Note that currently `UnsafePinned` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: UnsafePinned<&Self>` won't work
+// `self: UnsafePinned<Self>` becomes possible
+// FIXME(unsafe_pinned) this logic is copied from UnsafeCell, is it still sound?
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+// #[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<UnsafePinned<U>> for UnsafePinned<T> {}
+
+#[unstable(feature = "pointer_like_trait", issue = "none")]
+// #[unstable(feature = "unsafe_pinned", issue = "125735")]
+impl<T: PointerLike> PointerLike for UnsafePinned<T> {}
+
+// FIXME(unsafe_pinned): impl PinCoerceUnsized for UnsafePinned<T>?
diff --git a/tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs b/tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs
new file mode 100644
index 00000000000..a1ff9a1f69f
--- /dev/null
+++ b/tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs
@@ -0,0 +1,29 @@
+//@ check-pass
+// this test ensures that UnsafePinned hides the niche of its inner type, just like UnsafeCell does
+
+#![crate_type = "lib"]
+#![feature(unsafe_pinned)]
+
+use std::num::NonZero;
+use std::pin::UnsafePinned;
+
+macro_rules! assert_size_is {
+    ($ty:ty = $size:expr) => {
+        const _: () = assert!(size_of::<$ty>() == $size);
+    };
+}
+
+assert_size_is!(UnsafePinned<()> = 0);
+assert_size_is!(UnsafePinned<u8> = 1);
+
+assert_size_is!(       UnsafePinned<               u32>    = 4);
+assert_size_is!(       UnsafePinned<       NonZero<u32>>   = 4);
+assert_size_is!(       UnsafePinned<Option<NonZero<u32>>>  = 4);
+assert_size_is!(Option<UnsafePinned<               u32>>   = 8);
+assert_size_is!(Option<UnsafePinned<       NonZero<u32>>>  = 8);
+assert_size_is!(Option<UnsafePinned<Option<NonZero<u32>>>> = 8);
+
+assert_size_is!(       UnsafePinned<       &()>   = size_of::<usize>());
+assert_size_is!(       UnsafePinned<Option<&()>>  = size_of::<usize>());
+assert_size_is!(Option<UnsafePinned<       &()>>  = size_of::<usize>() * 2);
+assert_size_is!(Option<UnsafePinned<Option<&()>>> = size_of::<usize>() * 2);