diff --git a/src/lib.rs b/src/lib.rs index 0f5ddc98147..bc75a6bd298 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -783,6 +783,9 @@ pub unsafe trait KnownLayout { // resulting size would not fit in a `usize`. meta.size_for_metadata(Self::LAYOUT) } + + #[doc(hidden)] + unsafe fn drop(slf: &mut UnalignUnsized); } /// The metadata associated with a [`KnownLayout`] type. @@ -898,6 +901,25 @@ unsafe impl KnownLayout for [T] { // struct `Foo(i32, [u8])` or `(u64, Foo)`. slc.len() } + + #[cfg(feature = "alloc")] + unsafe fn drop(slf: &mut UnalignUnsized) { + let meta = KnownLayout::pointer_to_metadata(slf); + let size = meta.size_for_metadata(Self::LAYOUT).unwrap(); + let aligned = MaybeUninit::::new_boxed_uninit(meta).unwrap(); + let aligned = Box::into_raw(aligned); + // SAFETY: todo + unsafe { + core::ptr::copy_nonoverlapping(slf as *mut _ as *mut u8, aligned as *mut u8, size); + } + let _ = Box::from_raw(aligned); + } + + #[cfg(not(feature = "alloc"))] + unsafe fn drop(_: &mut UnalignUnsized) { + // PME if T needs drop. + static_assert!(T=> !core::mem::needs_drop::()); + } } #[rustfmt::skip] diff --git a/src/util/macros.rs b/src/util/macros.rs index 03805ada254..9de47f51a51 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -546,6 +546,10 @@ macro_rules! impl_known_layout { #[inline(always)] fn pointer_to_metadata(_ptr: *mut Self) -> () { } + + unsafe fn drop(slf: &mut crate::UnalignUnsized) { + let _ = unsafe { slf.take() }; + } } }; }; @@ -563,7 +567,7 @@ macro_rules! impl_known_layout { /// - It must be valid to perform an `as` cast from `*mut $repr` to `*mut $ty`, /// and this operation must preserve referent size (ie, `size_of_val_raw`). macro_rules! unsafe_impl_known_layout { - ($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => { + ($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($(packed,)? $repr:ty)] $ty:ty) => { const _: () = { use core::ptr::NonNull; @@ -597,6 +601,13 @@ macro_rules! unsafe_impl_known_layout { let ptr = ptr as *mut $repr; <$repr>::pointer_to_metadata(ptr) } + + unsafe fn drop(slf: &mut UnalignUnsized) { + // SAFETY: TODO + let slf = unsafe { &mut *(slf as *mut _ as *mut UnalignUnsized<$repr>) }; + // SAFETY: TODO + unsafe { KnownLayout::drop(slf) } + } } }; }; diff --git a/src/wrappers.rs b/src/wrappers.rs index dcb40bf5b95..7b127e4b810 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -464,6 +464,89 @@ impl Display for Unalign { } } +/// A type with no alignment requirement. +#[derive(Debug)] +#[repr(C, packed)] +pub struct UnalignUnsized(ManuallyDrop) +where + T: KnownLayout; + +// SAFETY: TODO +unsafe impl KnownLayout for UnalignUnsized { + #[allow(clippy::missing_inline_in_public_items)] + #[cfg_attr(coverage_nightly, coverage(off))] + fn only_derive_is_allowed_to_implement_this_trait() {} + + type PointerMetadata = ::PointerMetadata; + + type MaybeUninit = UnalignUnsized<::MaybeUninit>; + + const LAYOUT: DstLayout = DstLayout { + // The alignment is `1`, since `Self` is `repr(packed)`. + align: unsafe { NonZeroUsize::new_unchecked(1) }, + // Otherwise, we retain the size of the inner `T`. + size_info: ::LAYOUT.size_info, + }; + + #[inline(always)] + fn raw_from_ptr_len( + bytes: NonNull, + meta: ::PointerMetadata, + ) -> NonNull { + #[allow(clippy::as_conversions)] + let ptr = ::raw_from_ptr_len(bytes, meta).as_ptr() as *mut Self; + unsafe { NonNull::new_unchecked(ptr) } + } + + #[inline(always)] + fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata { + #[allow(clippy::as_conversions)] + let ptr = ptr as *mut T; + ::pointer_to_metadata(ptr) + } + + #[inline(always)] + unsafe fn drop(slf: &mut UnalignUnsized) { + let slf = slf.peel(); + // SAFETY: TODO + unsafe { + KnownLayout::drop(slf); + } + } +} + +impl UnalignUnsized +where + T: KnownLayout, +{ + pub(crate) unsafe fn take(&mut self) -> T { + let inner = core::ptr::addr_of!(self.0) as *const _; + // SAFETY: TODO + unsafe { core::ptr::read_unaligned(inner) } + } +} + +impl UnalignUnsized> +where + T: KnownLayout, +{ + pub(crate) fn peel(&mut self) -> &mut UnalignUnsized { + // SAFETY: TODO + unsafe { &mut *(self as *mut _ as *mut UnalignUnsized) } + } +} + +impl Drop for UnalignUnsized +where + T: KnownLayout, +{ + fn drop(&mut self) { + if core::mem::needs_drop::() { + unsafe { T::drop(self) } + } + } +} + /// A wrapper type to construct uninitialized instances of `T`. /// /// `MaybeUninit` is identical to the [standard library