Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pointer][WIP] Transmute #2408

Open
wants to merge 1 commit into
base: Ie66db9044be1dc310a6b7280a73652a357878376
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 84 additions & 54 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,14 @@ mod atomics {
use super::*;

macro_rules! impl_traits_for_atomics {
($($atomics:ident),* $(,)?) => {
($($atomics:ident [$primitives:ident]),* $(,)?) => {
$(
impl_size_eq!($atomics, $primitives);
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<$primitives>]);
impl_for_transparent_wrapper!(=> FromZeros for $atomics [UnsafeCell<$primitives>]);
impl_for_transparent_wrapper!(=> FromBytes for $atomics [UnsafeCell<$primitives>]);
impl_for_transparent_wrapper!(=> IntoBytes for $atomics [UnsafeCell<$primitives>]);
)*
};
}
Expand All @@ -435,13 +436,14 @@ mod atomics {

use super::*;

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

impl_size_eq!(AtomicBool, bool);
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_transparent_wrapper!(=> FromZeros for AtomicBool [UnsafeCell<bool>]);
impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool [UnsafeCell<bool>]);

safety_comment! {
/// SAFETY:
Expand All @@ -468,10 +470,8 @@ mod atomics {
unsafe_impl!(AtomicI8: Unaligned);
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);

/// 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]);
/// SAFETY: TODO
unsafe_impl_transmute_from_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]);
}
}

Expand All @@ -482,13 +482,11 @@ 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]);
/// SAFETY: TODO
unsafe_impl_transmute_from_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]);
}
}

Expand All @@ -499,13 +497,11 @@ 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]);
/// SAFETY: TODO
unsafe_impl_transmute_from_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]);
}
}

Expand All @@ -516,13 +512,11 @@ 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]);
/// SAFETY: TODO
unsafe_impl_transmute_from_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]);
}
}

Expand All @@ -533,21 +527,27 @@ mod atomics {

use super::*;

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

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

// SAFETY: `AtomicPtr<T>` and `*mut T` have the same size [1].
//
// [1] Per https://doc.rust-lang.org/1.85.0/std/sync/atomic/struct.AtomicPtr.html:
//
// This type has the same size and bit validity as a `*mut T`.
unsafe impl<T> crate::pointer::SizeEq<*mut T> for AtomicPtr<T> {}
// SAFETY: See previous safety comment.
unsafe impl<T> crate::pointer::SizeEq<AtomicPtr<T>> for *mut 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>]);

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]);
/// SAFETY: TODO
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 @@ -577,12 +577,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>);
unsafe_impl!(T: Immutable => Immutable for Wrapping<T>);
impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping<T>[T]);
impl_for_transparent_wrapper!(T: FromZeros => FromZeros for Wrapping<T>[T]);
impl_for_transparent_wrapper!(T: FromBytes => FromBytes for Wrapping<T>[T]);
impl_for_transparent_wrapper!(T: IntoBytes => IntoBytes for Wrapping<T>[T]);
unsafe_impl!(T: Unaligned => Unaligned for Wrapping<T>);
assert_unaligned!(Wrapping<()>, Wrapping<u8>);

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

impl_for_transparent_wrapper!(T: Immutable => Immutable for CoreMaybeUninit<T>);
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for CoreMaybeUninit<T>);
unsafe_impl!(T: Immutable => Immutable for CoreMaybeUninit<T>);
unsafe_impl!(T: Unaligned => Unaligned for CoreMaybeUninit<T>);
assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit<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>);
unsafe_impl!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>);

// SAFETY: See inline safety comment justifying that the implementation of
// `is_bit_valid`is sound.
unsafe impl<T: ?Sized + TryFromBytes> TryFromBytes for ManuallyDrop<T> {
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait() {}

#[inline(always)]
fn is_bit_valid<A: crate::pointer::invariant::Reference>(
candidate: Maybe<'_, Self, A>,
) -> bool {
// SAFETY: `ManuallyDrop<T>` and `T` have the same size [1], so this
// cast preserves size. It also preserves provenance.
//
// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
// validity as `T`
let c: Maybe<'_, T, A> = unsafe { candidate.cast_unsized(|p| cast!(p => NonNull<_>)) };

// SAFETY: `ManuallyDrop<T>` and `T` have the same bit validity [1], so
// this is a sound implementation of `ManuallyDrop::is_bit_valid`.
//
// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
// validity as `T`
<T as TryFromBytes>::is_bit_valid(c)
}
}

impl_for_transparent_wrapper!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop<T>[T]);
impl_for_transparent_wrapper!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>[T]);
impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop<T>[T]);
unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<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_transparent_wrapper!(T: ?Sized + FromZeros => FromZeros for UnsafeCell<T>[T]);
impl_for_transparent_wrapper!(T: ?Sized + FromBytes => FromBytes for UnsafeCell<T>[T]);
impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for UnsafeCell<T>[T]);
unsafe_impl!(T: ?Sized + Unaligned => Unaligned for UnsafeCell<T>);
assert_unaligned!(UnsafeCell<()>, UnsafeCell<u8>);

// SAFETY: See safety comment in `is_bit_valid` impl.
Expand Down
26 changes: 18 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,14 @@ pub unsafe trait KnownLayout {
// resulting size would not fit in a `usize`.
meta.size_for_metadata(Self::LAYOUT)
}

fn cast_from_raw<P: KnownLayout<PointerMetadata = Self::PointerMetadata> + ?Sized>(
ptr: NonNull<P>,
) -> NonNull<Self> {
let data = ptr.cast::<u8>();
let meta = P::pointer_to_metadata(ptr.as_ptr());
Self::raw_from_ptr_len(data, meta)
}
}

/// The metadata associated with a [`KnownLayout`] type.
Expand Down Expand Up @@ -2843,15 +2851,15 @@ 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();
// SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived from
// `candidate`, which the caller promises is entirely initialized. Since
// `candidate` is a `MaybeUninit`, it has no validity requirements, and so
// no values written to `c_ptr` can violate its validity. Since `c_ptr` has
// `Exclusive` aliasing, no mutations may happen except via `c_ptr` so long
// as it is live, so we don't need to worry about the fact that `c_ptr` may
// have more restricted validity than `candidate`.
// no values written to an `Initialized` `c_ptr` can violate its validity.
// Since `c_ptr` has `Exclusive` aliasing, no mutations may happen except
// via `c_ptr` so long as it is live, so we don't need to worry about the
// fact that `c_ptr` may have more restricted validity than `candidate`.
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
let c_ptr = c_ptr.transmute();

// This call may panic. If that happens, it doesn't cause any soundness
// issues, as we have not generated any invalid state which we need to
Expand All @@ -2861,7 +2869,7 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
// condition will not happen.
if !T::is_bit_valid(c_ptr.forget_aligned()) {
if !util::SizedKnownLayout::<T>::is_bit_valid(c_ptr.forget_aligned()) {
return Err(ValidityError::new(source).into());
}

Expand Down Expand Up @@ -4258,7 +4266,9 @@ pub unsafe trait FromBytes: FromZeros {
let source = Ptr::from_mut(source);
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
match maybe_slf {
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_mut()),
Ok(slf) => Ok(slf
.bikeshed_recall_valid::<(_, (_, (BecauseExclusive, BecauseExclusive)))>()
.as_mut()),
Err(err) => Err(err.map_src(|s| s.as_mut())),
}
}
Expand Down Expand Up @@ -4728,7 +4738,7 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
/// If there are insufficient bytes, or if that affix of `source` is not
/// appropriately aligned, this returns `Err`.
#[inline(always)]
fn mut_from_prefix_suffix<T: FromBytes + KnownLayout + ?Sized>(
fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
source: &mut [u8],
meta: Option<T::PointerMetadata>,
cast_type: CastType,
Expand Down
8 changes: 6 additions & 2 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ mod inner;
#[doc(hidden)]
pub mod invariant;
mod ptr;
mod transmute;

#[doc(hidden)]
pub use invariant::{BecauseExclusive, BecauseImmutable, Read};
pub(crate) use transmute::*;
#[doc(hidden)]
pub use ptr::Ptr;
pub use {
invariant::{BecauseExclusive, BecauseImmutable, Read},
ptr::Ptr,
};

use crate::Unaligned;

Expand Down
Loading