Skip to content

Commit

Permalink
[pointer][transmute] Support generic TransmuteFrom
Browse files Browse the repository at this point in the history
This commit replaces the `TransparentWrapper` trait with a more generic,
bidirectional `TransmuteFrom<T>` trait. `U: TransmuteFrom<T>` indicates
that `T` and `U` have the same sizes and that `T` can be transmuted into
`U` given certain alignment and validity invariant mappings.

Makes progress on #1122

gherrit-pr-id: I7af68cdaa5ea47ab245a942d1cbd8bc5e6407223
  • Loading branch information
joshlf committed Oct 18, 2024
1 parent 4795dcd commit 9b71dc8
Show file tree
Hide file tree
Showing 7 changed files with 730 additions and 442 deletions.
78 changes: 39 additions & 39 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,13 +443,13 @@ mod atomics {
use super::*;

macro_rules! impl_traits_for_atomics {
($($atomics:ident),* $(,)?) => {
($($atomics:ident[$repr:ty]),* $(,)?) => {
$(
impl_known_layout!($atomics);
impl_for_transparent_wrapper!(=> TryFromBytes for $atomics);
impl_for_transparent_wrapper!(=> FromZeros for $atomics);
impl_for_transparent_wrapper!(=> FromBytes for $atomics);
impl_for_transparent_wrapper!(=> IntoBytes for $atomics);
impl_for_transmute_from!(=> TryFromBytes for $atomics[UnsafeCell<$repr>]);
impl_for_transmute_from!(=> FromZeros for $atomics[UnsafeCell<$repr>]);
impl_for_transmute_from!(=> FromBytes for $atomics[UnsafeCell<$repr>]);
impl_for_transmute_from!(=> IntoBytes for $atomics[UnsafeCell<$repr>]);
)*
};
}
Expand All @@ -461,13 +461,13 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU8, AtomicI8);
impl_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]);

impl_known_layout!(AtomicBool);

impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool);
impl_for_transparent_wrapper!(=> FromZeros for AtomicBool);
impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool);
impl_for_transmute_from!(=> TryFromBytes for AtomicBool[UnsafeCell<bool>]);
impl_for_transmute_from!(=> FromZeros for AtomicBool[UnsafeCell<bool>]);
impl_for_transmute_from!(=> IntoBytes for AtomicBool[UnsafeCell<bool>]);

safety_comment! {
/// SAFETY:
Expand Down Expand Up @@ -497,7 +497,7 @@ mod atomics {
/// SAFETY:
/// All of these pass an atomic type and that type's native equivalent, as
/// required by the macro safety preconditions.
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]);
unsafe_impl_transmute_from_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]);
}
}

Expand All @@ -508,13 +508,13 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU16, AtomicI16);
impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]);

safety_comment! {
/// SAFETY:
/// All of these pass an atomic type and that type's native equivalent, as
/// required by the macro safety preconditions.
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]);
unsafe_impl_transmute_from_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]);
}
}

Expand All @@ -525,13 +525,13 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU32, AtomicI32);
impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]);

safety_comment! {
/// SAFETY:
/// All of these pass an atomic type and that type's native equivalent, as
/// required by the macro safety preconditions.
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]);
unsafe_impl_transmute_from_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]);
}
}

Expand All @@ -542,13 +542,13 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU64, AtomicI64);
impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]);

safety_comment! {
/// SAFETY:
/// All of these pass an atomic type and that type's native equivalent, as
/// required by the macro safety preconditions.
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]);
unsafe_impl_transmute_from_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]);
}
}

Expand All @@ -559,21 +559,21 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicUsize, AtomicIsize);
impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]);

impl_known_layout!(T => AtomicPtr<T>);

// TODO(#170): Implement `FromBytes` and `IntoBytes` once we implement
// those traits for `*mut T`.
impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr<T>);
impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr<T>);
impl_for_transmute_from!(T => TryFromBytes for AtomicPtr<T>[UnsafeCell<*mut T>]);
impl_for_transmute_from!(T => FromZeros for AtomicPtr<T>[UnsafeCell<*mut T>]);

safety_comment! {
/// SAFETY:
/// This passes an atomic type and that type's native equivalent, as
/// required by the macro safety preconditions.
unsafe_impl_transparent_wrapper_for_atomic!(AtomicUsize [usize], AtomicIsize [isize]);
unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr<T> [*mut T]);
unsafe_impl_transmute_from_for_atomic!(AtomicUsize [usize], AtomicIsize [isize]);
unsafe_impl_transmute_from_for_atomic!(T => AtomicPtr<T> [*mut T]);
}
}
}
Expand Down Expand Up @@ -603,12 +603,12 @@ safety_comment! {
assert_unaligned!(PhantomData<()>, PhantomData<u8>, PhantomData<u64>);
}

impl_for_transparent_wrapper!(T: Immutable => Immutable for Wrapping<T>);
impl_for_transparent_wrapper!(T: TryFromBytes => TryFromBytes for Wrapping<T>);
impl_for_transparent_wrapper!(T: FromZeros => FromZeros for Wrapping<T>);
impl_for_transparent_wrapper!(T: FromBytes => FromBytes for Wrapping<T>);
impl_for_transparent_wrapper!(T: IntoBytes => IntoBytes for Wrapping<T>);
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for Wrapping<T>);
impl_for_transmute_from!(T: Immutable => Immutable for Wrapping<T>[T]);
impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping<T>[T]);
impl_for_transmute_from!(T: FromZeros => FromZeros for Wrapping<T>[T]);
impl_for_transmute_from!(T: FromBytes => FromBytes for Wrapping<T>[T]);
impl_for_transmute_from!(T: IntoBytes => IntoBytes for Wrapping<T>[T]);
impl_for_transmute_from!(T: Unaligned => Unaligned for Wrapping<T>[T]);
assert_unaligned!(Wrapping<()>, Wrapping<u8>);

safety_comment! {
Expand All @@ -620,22 +620,22 @@ safety_comment! {
unsafe_impl!(T => FromBytes for MaybeUninit<T>);
}

impl_for_transparent_wrapper!(T: Immutable => Immutable for MaybeUninit<T>);
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for MaybeUninit<T>);
impl_for_transmute_from!(T: Immutable => Immutable for MaybeUninit<T>[T]);
impl_for_transmute_from!(T: Unaligned => Unaligned for MaybeUninit<T>[T]);
assert_unaligned!(MaybeUninit<()>, MaybeUninit<u8>);

impl_for_transparent_wrapper!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>);
impl_for_transparent_wrapper!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>);
impl_for_transparent_wrapper!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop<T>);
impl_for_transparent_wrapper!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>);
impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop<T>);
impl_for_transparent_wrapper!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>);
impl_for_transmute_from!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>[T]);
impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>[T]);
impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop<T>[T]);
impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>[T]);
impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop<T>[T]);
impl_for_transmute_from!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>[T]);
assert_unaligned!(ManuallyDrop<()>, ManuallyDrop<u8>);

impl_for_transparent_wrapper!(T: ?Sized + FromZeros => FromZeros for UnsafeCell<T>);
impl_for_transparent_wrapper!(T: ?Sized + FromBytes => FromBytes for UnsafeCell<T>);
impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for UnsafeCell<T>);
impl_for_transparent_wrapper!(T: ?Sized + Unaligned => Unaligned for UnsafeCell<T>);
impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for UnsafeCell<T>[T]);
impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for UnsafeCell<T>[T]);
impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for UnsafeCell<T>[T]);
impl_for_transmute_from!(T: ?Sized + Unaligned => Unaligned for UnsafeCell<T>[T]);
assert_unaligned!(UnsafeCell<()>, UnsafeCell<u8>);

// SAFETY: See safety comment in `is_bit_valid` impl.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2720,7 +2720,7 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
// We use `from_mut` despite not mutating via `c_ptr` so that we don't need
// to add a `T: Immutable` bound.
let c_ptr = Ptr::from_mut(&mut candidate);
let c_ptr = c_ptr.transparent_wrapper_into_inner();
let c_ptr = c_ptr.transmute::<_, pointer::transmute::BecauseBidirectional>();
// SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived from
// `candidate`, which the caller promises is entirely initialized.
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
Expand Down
130 changes: 112 additions & 18 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl<A: Aliasing, AA: Alignment, V: Validity> Invariants for (A, AA, V) {
/// The aliasing invariant of a [`Ptr`][super::Ptr].
///
/// All aliasing invariants must permit reading from the bytes of a pointer's
/// referent which are not covered by [`UnsafeCell`]s.
/// referent which are not covered by [`UnsafeCell`](core::cell::UnsafeCell)s.
pub trait Aliasing: Sealed {
/// Is `Self` [`Exclusive`]?
#[doc(hidden)]
Expand All @@ -70,18 +70,56 @@ pub trait Aliasing: Sealed {
type Variance<'a, T: 'a + ?Sized>;
}

/// The alignment invariant of a [`Ptr`][super::Ptr].
pub trait Alignment: Sealed {
#[doc(hidden)]
// NOTE: The `AlignmentInner`/`Alignment` distinction is required so that we can
// add a `MappedTo<Preserved> = Self` bound. For an explanation of the design
// space (and prior attempts), see:
// https://users.rust-lang.org/t/how-to-implement-a-type-level-map-with-an-identity-function/119745
#[doc(hidden)]
pub trait AlignmentInner: Sealed {
type MappedTo<M: AlignmentMapping>: Alignment;
}

/// The validity invariant of a [`Ptr`][super::Ptr].
pub trait Validity: Sealed {
#[doc(hidden)]
/// The alignment invariant of a [`Ptr`][super::Ptr].
pub trait Alignment:
AlignmentInner<MappedTo<Preserved> = Self>
+ AlignmentInner<MappedTo<Unknown> = Unknown>
+ AlignmentInner<MappedTo<Aligned> = Aligned>
+ Sealed
{
}
impl<
A: AlignmentInner<MappedTo<Preserved> = Self>
+ AlignmentInner<MappedTo<Unknown> = Unknown>
+ AlignmentInner<MappedTo<Aligned> = Aligned>,
> Alignment for A
{
}

#[doc(hidden)]
pub trait ValidityInner: Sealed {
type MappedTo<M: ValidityMapping>: Validity;
}

/// The validity invariant of a [`Ptr`][super::Ptr].
pub trait Validity:
ValidityInner<MappedTo<Preserved> = Self>
+ ValidityInner<MappedTo<Unknown> = Unknown>
+ ValidityInner<MappedTo<AsInitialized> = AsInitialized>
+ ValidityInner<MappedTo<Initialized> = Initialized>
+ ValidityInner<MappedTo<Valid> = Valid>
+ Sealed
{
}
impl<
V: ValidityInner<MappedTo<Preserved> = Self>
+ ValidityInner<MappedTo<Unknown> = Unknown>
+ ValidityInner<MappedTo<AsInitialized> = AsInitialized>
+ ValidityInner<MappedTo<Initialized> = Initialized>
+ ValidityInner<MappedTo<Valid> = Valid>,
> Validity for V
{
}

/// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`].
///
/// # Safety
Expand All @@ -93,10 +131,10 @@ pub trait Reference: Aliasing + Sealed {}
/// It is unknown whether any invariant holds.
pub enum Unknown {}

impl Alignment for Unknown {
impl AlignmentInner for Unknown {
type MappedTo<M: AlignmentMapping> = M::FromUnknown;
}
impl Validity for Unknown {
impl ValidityInner for Unknown {
type MappedTo<M: ValidityMapping> = M::FromUnknown;
}

Expand Down Expand Up @@ -127,10 +165,10 @@ impl Aliasing for Exclusive {
}
impl Reference for Exclusive {}

/// The referent is aligned: for `Ptr<T>`, the referent's address is a
/// multiple of the `T`'s alignment.
/// The referent is aligned: for `Ptr<T>`, the referent's address is a multiple
/// of the `T`'s alignment.
pub enum Aligned {}
impl Alignment for Aligned {
impl AlignmentInner for Aligned {
type MappedTo<M: AlignmentMapping> = M::FromAligned;
}

Expand Down Expand Up @@ -161,20 +199,20 @@ impl Alignment for Aligned {
/// enum type, in which case the same rules apply depending on the state of
/// its discriminant, and so on recursively).
pub enum AsInitialized {}
impl Validity for AsInitialized {
impl ValidityInner for AsInitialized {
type MappedTo<M: ValidityMapping> = M::FromAsInitialized;
}

/// The byte ranges in the referent are fully initialized. In other words, if
/// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`.
/// The byte ranges in the referent are fully initialized. In other words,
/// if the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`.
pub enum Initialized {}
impl Validity for Initialized {
impl ValidityInner for Initialized {
type MappedTo<M: ValidityMapping> = M::FromInitialized;
}

/// The referent is bit-valid for `T`.
pub enum Valid {}
impl Validity for Valid {
impl ValidityInner for Valid {
type MappedTo<M: ValidityMapping> = M::FromValid;
}

Expand Down Expand Up @@ -272,6 +310,12 @@ mod mapping {
type FromValid: Validity;
}

/// A mapping which preserves all invariants as-is.
///
/// `Preserved` is a valid type for any mapping trait ([`AlignmentMapping`]
/// and [`ValidityMapping`]).
pub enum Preserved {}

/// The application of the [`AlignmentMapping`] `M` to the [`Alignment`] `A`.
#[allow(type_alias_bounds)]
pub type MappedAlignment<A: Alignment, M: AlignmentMapping> = A::MappedTo<M>;
Expand All @@ -281,12 +325,27 @@ mod mapping {
pub type MappedValidity<V: Validity, M: ValidityMapping> = V::MappedTo<M>;

impl<FromUnknown: Alignment, FromAligned: Alignment> AlignmentMapping
for ((Unknown, FromUnknown), (Shared, FromAligned))
for ((Unknown, FromUnknown), (Aligned, FromAligned))
{
type FromUnknown = FromUnknown;
type FromAligned = FromAligned;
}

impl AlignmentMapping for Unknown {
type FromUnknown = Unknown;
type FromAligned = Unknown;
}

impl AlignmentMapping for Preserved {
type FromUnknown = Unknown;
type FromAligned = Aligned;
}

impl AlignmentMapping for Aligned {
type FromUnknown = Aligned;
type FromAligned = Aligned;
}

impl<
FromUnknown: Validity,
FromAsInitialized: Validity,
Expand All @@ -312,4 +371,39 @@ mod mapping {
type FromInitialized = FromInitialized;
type FromValid = Unknown;
}

impl ValidityMapping for Unknown {
type FromUnknown = Unknown;
type FromAsInitialized = Unknown;
type FromInitialized = Unknown;
type FromValid = Unknown;
}

impl ValidityMapping for Preserved {
type FromUnknown = Unknown;
type FromAsInitialized = AsInitialized;
type FromInitialized = Initialized;
type FromValid = Valid;
}

impl ValidityMapping for AsInitialized {
type FromUnknown = AsInitialized;
type FromAsInitialized = AsInitialized;
type FromInitialized = AsInitialized;
type FromValid = AsInitialized;
}

impl ValidityMapping for Initialized {
type FromUnknown = Initialized;
type FromAsInitialized = Initialized;
type FromInitialized = Initialized;
type FromValid = Initialized;
}

impl ValidityMapping for Valid {
type FromUnknown = Valid;
type FromAsInitialized = Valid;
type FromInitialized = Valid;
type FromValid = Valid;
}
}
Loading

0 comments on commit 9b71dc8

Please sign in to comment.