Skip to content

Commit 3a19028

Browse files
committed
[pointer] SizeEq supports raw pointer transmutes
This permits us to remove the `KnownLayout` bounds from `Ptr::transmute`, and thus to remove `Ptr::transmute_sized` entirely. This also allows us to remove the hand-rolled impl of `TryFromBytes` for `ManuallyDrop<T>`, replacing it with an invocation of `impl_for_transmute_from!`. gherrit-pr-id: I090715fd9ed6f100fd35513b05054015621ecb9c
1 parent 5ad3b3f commit 3a19028

File tree

6 files changed

+136
-112
lines changed

6 files changed

+136
-112
lines changed

src/impls.rs

+43-39
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// This file may not be copied, modified, or distributed except according to
88
// those terms.
99

10-
use core::mem::MaybeUninit as CoreMaybeUninit;
10+
use core::{cell::UnsafeCell, mem::MaybeUninit as CoreMaybeUninit, ptr::NonNull};
1111

1212
use super::*;
1313

@@ -223,16 +223,32 @@ safety_comment! {
223223
//
224224
// String slices are a UTF-8 representation of characters that have the same
225225
// layout as slices of type `[u8]`.
226-
unsafe impl pointer::SizeEq<str> for [u8] {}
226+
unsafe impl pointer::SizeEq<str> for [u8] {
227+
fn cast_from_raw(s: NonNull<str>) -> NonNull<[u8]> {
228+
cast!(s)
229+
}
230+
}
227231
// SAFETY: See previous safety comment.
228-
unsafe impl pointer::SizeEq<[u8]> for str {}
232+
unsafe impl pointer::SizeEq<[u8]> for str {
233+
fn cast_from_raw(bytes: NonNull<[u8]>) -> NonNull<str> {
234+
cast!(bytes)
235+
}
236+
}
229237

230238
macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
231239
($($nonzero:ident[$prim:ty]),*) => {
232240
$(
233241
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
234-
unsafe impl pointer::SizeEq<$nonzero> for Unalign<$prim> {}
235-
unsafe impl pointer::SizeEq<Unalign<$prim>> for $nonzero {}
242+
unsafe impl pointer::SizeEq<$nonzero> for Unalign<$prim> {
243+
fn cast_from_raw(n: NonNull<$nonzero>) -> NonNull<Unalign<$prim>> {
244+
cast!(n)
245+
}
246+
}
247+
unsafe impl pointer::SizeEq<Unalign<$prim>> for $nonzero {
248+
fn cast_from_raw(p: NonNull<Unalign<$prim>>) -> NonNull<$nonzero> {
249+
cast!(p)
250+
}
251+
}
236252

237253
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
238254
$nonzero::new(n.read_unaligned().into_inner()).is_some()
@@ -470,6 +486,7 @@ mod atomics {
470486
macro_rules! unsafe_impl_transmute_from_for_atomic {
471487
($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {
472488
const _: () = {
489+
use core::{cell::UnsafeCell, ptr::NonNull};
473490
use crate::pointer::{TransmuteFrom, SizeEq, invariant::Valid};
474491

475492
$(
@@ -485,10 +502,18 @@ mod atomics {
485502

486503
// SAFETY: THe caller promised that `$atomic` and `$prim`
487504
// have the same size.
488-
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {}
505+
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
506+
fn cast_from_raw(a: NonNull<$atomic>) -> NonNull<$prim> {
507+
cast!(a)
508+
}
509+
}
489510
// SAFETY: THe caller promised that `$atomic` and `$prim`
490511
// have the same size.
491-
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {}
512+
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
513+
fn cast_from_raw(p: NonNull<$prim>) -> NonNull<$atomic> {
514+
cast!(p)
515+
}
516+
}
492517
// SAFETY: The caller promised that `$atomic` and `$prim`
493518
// have the same size. `UnsafeCell<T>` has the same size as
494519
// `T` [1].
@@ -498,9 +523,17 @@ mod atomics {
498523
// `UnsafeCell<T>` has the same in-memory representation as
499524
// its inner type `T`. A consequence of this guarantee is that
500525
// it is possible to convert between `T` and `UnsafeCell<T>`.
501-
unsafe impl<$($tyvar)?> SizeEq<$atomic> for core::cell::UnsafeCell<$prim> {}
526+
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
527+
fn cast_from_raw(a: NonNull<$atomic>) -> NonNull<UnsafeCell<$prim>> {
528+
cast!(a)
529+
}
530+
}
502531
// SAFETY: See previous safety comment.
503-
unsafe impl<$($tyvar)?> SizeEq<core::cell::UnsafeCell<$prim>> for $atomic {}
532+
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
533+
fn cast_from_raw(p: NonNull<UnsafeCell<$prim>>) -> NonNull<$atomic> {
534+
cast!(p)
535+
}
536+
}
504537

505538
// SAFETY: The caller promised that `$atomic` and `$prim`
506539
// have the same bit validity. `UnsafeCell<T>` has the same
@@ -812,36 +845,7 @@ safety_comment! {
812845
unsafe_impl!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>);
813846
}
814847

815-
// SAFETY: See inline safety comment justifying that the implementation of
816-
// `is_bit_valid`is sound.
817-
unsafe impl<T: ?Sized + TryFromBytes> TryFromBytes for ManuallyDrop<T> {
818-
#[allow(clippy::missing_inline_in_public_items)]
819-
fn only_derive_is_allowed_to_implement_this_trait() {}
820-
821-
#[inline(always)]
822-
fn is_bit_valid<A: crate::pointer::invariant::Reference>(
823-
candidate: Maybe<'_, Self, A>,
824-
) -> bool {
825-
// SAFETY: `ManuallyDrop<T>` and `T` have the same size [1], so this
826-
// cast preserves size. It also preserves provenance.
827-
//
828-
// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
829-
//
830-
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
831-
// validity as `T`
832-
let c: Maybe<'_, T, A> = unsafe { candidate.cast_unsized(cast!()) };
833-
834-
// SAFETY: `ManuallyDrop<T>` and `T` have the same bit validity [1], so
835-
// this is a sound implementation of `ManuallyDrop::is_bit_valid`.
836-
//
837-
// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
838-
//
839-
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
840-
// validity as `T`
841-
<T as TryFromBytes>::is_bit_valid(c)
842-
}
843-
}
844-
848+
impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>[<T>]);
845849
impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop<T>[<T>]);
846850
impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>[<T>]);
847851
impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop<T>[<T>]);

src/lib.rs

-11
Original file line numberDiff line numberDiff line change
@@ -805,17 +805,6 @@ pub unsafe trait KnownLayout {
805805
// resulting size would not fit in a `usize`.
806806
meta.size_for_metadata(Self::LAYOUT)
807807
}
808-
809-
#[doc(hidden)]
810-
#[must_use]
811-
#[inline(always)]
812-
fn cast_from_raw<P: KnownLayout<PointerMetadata = Self::PointerMetadata> + ?Sized>(
813-
ptr: NonNull<P>,
814-
) -> NonNull<Self> {
815-
let data = ptr.cast::<u8>();
816-
let meta = P::pointer_to_metadata(ptr.as_ptr());
817-
Self::raw_from_ptr_len(data, meta)
818-
}
819808
}
820809

821810
/// The metadata associated with a [`KnownLayout`] type.

src/pointer/ptr.rs

+1-27
Original file line numberDiff line numberDiff line change
@@ -393,12 +393,8 @@ mod _conversions {
393393
{
394394
pub(crate) fn transmute<U, V, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
395395
where
396-
T: KnownLayout,
397396
V: Validity,
398-
U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, R>
399-
+ KnownLayout<PointerMetadata = T::PointerMetadata>
400-
+ SizeEq<T>
401-
+ ?Sized,
397+
U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, R> + SizeEq<T> + ?Sized,
402398
{
403399
// SAFETY:
404400
// - This cast preserves address and provenance
@@ -416,28 +412,6 @@ mod _conversions {
416412
unsafe { self.transmute_unchecked(|t: NonNull<T>| U::cast_from_raw(t)) }
417413
}
418414

419-
pub(crate) fn transmute_sized<U, V, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
420-
where
421-
T: Sized,
422-
V: Validity,
423-
U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, R> + SizeEq<T>,
424-
{
425-
// SAFETY:
426-
// - This cast preserves address and provenance
427-
// - `U: SizeEq<T>` guarantees that this cast preserves the number
428-
// of bytes in the referent
429-
// - If aliasing is `Shared`, then by `U: TransmuteFromPtr<T>`, at
430-
// least one of the following holds:
431-
// - `T: Immutable` and `U: Immutable`, in which case it is
432-
// trivially sound for shared code to operate on a `&T` and `&U`
433-
// at the same time, as neither can perform interior mutation
434-
// - It is directly guaranteed that it is sound for shared code to
435-
// operate on these references simultaneously
436-
// - By `U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V>`, it is
437-
// sound to perform this transmute.
438-
unsafe { self.transmute_unchecked(cast!()) }
439-
}
440-
441415
#[doc(hidden)]
442416
#[inline(always)]
443417
#[must_use]

src/pointer/transmute.rs

+22-18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use core::{
1010
cell::UnsafeCell,
1111
mem::{ManuallyDrop, MaybeUninit},
1212
num::Wrapping,
13+
ptr::NonNull,
1314
};
1415

1516
use crate::{pointer::invariant::*, FromBytes, Immutable, IntoBytes, Unalign};
@@ -227,20 +228,8 @@ macro_rules! unsafe_impl_invariants_eq {
227228
};
228229
}
229230

230-
// SAFETY: See bounds.
231-
unsafe impl<T> SizeEq<MaybeUninit<T>> for Wrapping<T>
232-
where
233-
T: SizeEq<Wrapping<T>>,
234-
T: SizeEq<MaybeUninit<T>>,
235-
{
236-
}
237-
// SAFETY: See bounds.
238-
unsafe impl<T> SizeEq<Wrapping<T>> for MaybeUninit<T>
239-
where
240-
T: SizeEq<Wrapping<T>>,
241-
T: SizeEq<MaybeUninit<T>>,
242-
{
243-
}
231+
impl_transitive_transmute_from!(T => MaybeUninit<T> => T => Wrapping<T>);
232+
impl_transitive_transmute_from!(T => Wrapping<T> => T => MaybeUninit<T>);
244233

245234
// SAFETY: `ManuallyDrop<T>` has the same size and bit validity as `T` [1], and
246235
// implements `Deref<Target = T>` [2]. Thus, it is already possible for safe
@@ -298,12 +287,18 @@ pub unsafe trait TransmuteFrom<Src: ?Sized, SV, DV>: SizeEq<Src> {}
298287
/// - If `T: ?Sized` and `Self: ?Sized`, then it must be the case that, given
299288
/// any `t: *mut T`, `t as *mut Self` produces a pointer which addresses the
300289
/// same number of bytes as `t`.
301-
pub unsafe trait SizeEq<T: ?Sized> {}
290+
pub unsafe trait SizeEq<T: ?Sized> {
291+
fn cast_from_raw(t: NonNull<T>) -> NonNull<Self>;
292+
}
302293

303294
// SAFETY: `T` trivially has the same size and vtable kind as `T`, and since
304295
// pointer `*mut T -> *mut T` pointer casts are no-ops, this cast trivially
305296
// preserves referent size (when `T: ?Sized`).
306-
unsafe impl<T: ?Sized> SizeEq<T> for T {}
297+
unsafe impl<T: ?Sized> SizeEq<T> for T {
298+
fn cast_from_raw(t: NonNull<T>) -> NonNull<T> {
299+
t
300+
}
301+
}
307302

308303
// SAFETY: Since `Src: IntoBytes`, the set of valid `Src`'s is the set of
309304
// initialized bit patterns, which is exactly the set allowed in the referent of
@@ -436,6 +431,15 @@ unsafe impl<T> TransmuteFrom<T, Uninit, Valid> for MaybeUninit<T> {}
436431
//
437432
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and ABI as
438433
// `T`
439-
unsafe impl<T> SizeEq<T> for MaybeUninit<T> {}
434+
unsafe impl<T> SizeEq<T> for MaybeUninit<T> {
435+
fn cast_from_raw(t: NonNull<T>) -> NonNull<MaybeUninit<T>> {
436+
cast!(t)
437+
}
438+
}
439+
440440
// SAFETY: See previous safety comment.
441-
unsafe impl<T> SizeEq<MaybeUninit<T>> for T {}
441+
unsafe impl<T> SizeEq<MaybeUninit<T>> for T {
442+
fn cast_from_raw(t: NonNull<MaybeUninit<T>>) -> NonNull<T> {
443+
cast!(t)
444+
}
445+
}

src/util/macros.rs

+65-9
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ macro_rules! impl_for_transmute_from {
254254
// SAFETY: This macro ensures that `$repr` and `Self` have the same
255255
// size and bit validity. Thus, a bit-valid instance of `$repr` is
256256
// also a bit-valid instance of `Self`.
257-
<$repr as TryFromBytes>::is_bit_valid(candidate.transmute_sized())
257+
<$repr as TryFromBytes>::is_bit_valid(candidate.transmute())
258258
}
259259
};
260260
(
@@ -694,13 +694,19 @@ macro_rules! static_assert_dst_is_not_zst {
694694

695695
macro_rules! cast {
696696
() => {
697-
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the argument
698-
// to `NonNull::new_unchecked` is also non-null.
699697
|p| {
700-
#[allow(clippy::as_conversions)]
701-
return core::ptr::NonNull::new_unchecked(core::ptr::NonNull::as_ptr(p) as *mut _);
698+
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the
699+
// argument to `NonNull::new_unchecked` is also non-null.
700+
#[allow(clippy::as_conversions, unused_unsafe)]
701+
#[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
702+
return unsafe {
703+
core::ptr::NonNull::new_unchecked(core::ptr::NonNull::as_ptr(p) as *mut _)
704+
};
702705
}
703706
};
707+
($p:ident) => {
708+
cast!()($p)
709+
};
704710
}
705711

706712
/// Implements `TransmuteFrom` and `SizeEq` for `T` and `$wrapper<T>`.
@@ -712,17 +718,27 @@ macro_rules! cast {
712718
macro_rules! unsafe_impl_for_transparent_wrapper {
713719
(T $(: ?$optbound:ident)? => $wrapper:ident<T>) => {
714720
const _: () = {
721+
use core::ptr::NonNull;
715722
use crate::pointer::{TransmuteFrom, SizeEq, invariant::Valid};
723+
716724
// SAFETY: The caller promises that `T` and `$wrapper<T>` have the
717725
// same bit validity.
718726
unsafe impl<T $(: ?$optbound)?> TransmuteFrom<T, Valid, Valid> for $wrapper<T> {}
719727
// SAFETY: See previous safety comment.
720728
unsafe impl<T $(: ?$optbound)?> TransmuteFrom<$wrapper<T>, Valid, Valid> for T {}
721729
// SAFETY: The caller promises that `T` and `$wrapper<T>` satisfy
722730
// `SizeEq`.
723-
unsafe impl<T $(: ?$optbound)?> SizeEq<T> for $wrapper<T> {}
731+
unsafe impl<T $(: ?$optbound)?> SizeEq<T> for $wrapper<T> {
732+
fn cast_from_raw(t: NonNull<T>) -> NonNull<$wrapper<T>> {
733+
cast!(t)
734+
}
735+
}
724736
// SAFETY: See previous safety comment.
725-
unsafe impl<T $(: ?$optbound)?> SizeEq<$wrapper<T>> for T {}
737+
unsafe impl<T $(: ?$optbound)?> SizeEq<$wrapper<T>> for T {
738+
fn cast_from_raw(t: NonNull<$wrapper<T>>) -> NonNull<T> {
739+
cast!(t)
740+
}
741+
}
726742
};
727743

728744
// So that this macro must be invoked inside `safety_comment!` or else
@@ -732,17 +748,57 @@ macro_rules! unsafe_impl_for_transparent_wrapper {
732748
};
733749
}
734750

751+
macro_rules! impl_transitive_transmute_from {
752+
($($tyvar:ident $(: ?$optbound:ident)?)? => $t:ty => $u:ty => $v:ty) => {
753+
const _: () = {
754+
use core::ptr::NonNull;
755+
use crate::pointer::{TransmuteFrom, SizeEq, invariant::Valid};
756+
757+
// SAFETY: Since `$u: SizeEq<$t>` and `$v: SizeEq<U>`, this impl is
758+
// transitively sound.
759+
unsafe impl<$($tyvar $(: ?$optbound)?)?> SizeEq<$t> for $v
760+
where
761+
$u: SizeEq<$t>,
762+
$v: SizeEq<$u>,
763+
{
764+
fn cast_from_raw(t: NonNull<$t>) -> NonNull<$v> {
765+
cast!(t)
766+
}
767+
}
768+
769+
// SAFETY: Since `$u: TransmuteFrom<$t, Valid, Valid>`, it is sound
770+
// to transmute a bit-valid `$t` to a bit-valid `$u`. Since `$v:
771+
// TransmuteFrom<$u, Valid, Valid>`, it is sound to transmute that
772+
// bit-valid `$u` to a bit-valid `$v`.
773+
unsafe impl<$($tyvar $(: ?$optbound)?)?> TransmuteFrom<$t, Valid, Valid> for $v
774+
where
775+
$u: TransmuteFrom<$t, Valid, Valid>,
776+
$v: TransmuteFrom<$u, Valid, Valid>,
777+
{}
778+
};
779+
};
780+
}
781+
735782
macro_rules! impl_size_eq {
736783
($t:ty, $u:ty) => {
737784
const _: () = {
738785
use crate::pointer::SizeEq;
786+
use core::ptr::NonNull;
739787

740788
static_assert!(=> mem::size_of::<$t>() == mem::size_of::<$u>());
741789

742790
// SAFETY: We've asserted that their sizes are equal.
743-
unsafe impl SizeEq<$t> for $u {}
791+
unsafe impl SizeEq<$t> for $u {
792+
fn cast_from_raw(t: NonNull<$t>) -> NonNull<$u> {
793+
cast!(t)
794+
}
795+
}
744796
// SAFETY: We've asserted that their sizes are equal.
745-
unsafe impl SizeEq<$u> for $t {}
797+
unsafe impl SizeEq<$u> for $t {
798+
fn cast_from_raw(u: NonNull<$u>) -> NonNull<$t> {
799+
cast!(u)
800+
}
801+
}
746802
};
747803
};
748804
}

0 commit comments

Comments
 (0)