Skip to content

Commit

Permalink
[pointer][WIP] Transmute
Browse files Browse the repository at this point in the history
gherrit-pr-id: Iad14813bc6d933312bc8d7a1ddcf1aafc7126938
  • Loading branch information
joshlf committed Mar 3, 2025
1 parent daf3a21 commit 3e52166
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 81 deletions.
10 changes: 9 additions & 1 deletion src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ mod atomics {

impl_known_layout!(AtomicBool);

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

Expand Down Expand Up @@ -472,6 +473,7 @@ mod atomics {
/// 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 @@ -489,6 +491,7 @@ mod atomics {
/// 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 @@ -506,6 +509,7 @@ mod atomics {
/// 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 @@ -523,6 +527,7 @@ mod atomics {
/// 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 @@ -548,6 +553,9 @@ mod atomics {
/// 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
21 changes: 16 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,10 @@ use core::{
#[cfg(feature = "std")]
use std::io;

use crate::pointer::invariant::{self, BecauseExclusive};
use crate::pointer::{
invariant::{self, BecauseExclusive},
BecauseRead,
};

#[cfg(any(feature = "alloc", test))]
extern crate alloc;
Expand Down Expand Up @@ -805,6 +808,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,7 +2854,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();
// 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
Expand All @@ -2861,7 +2872,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 +4269,7 @@ 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::<(BecauseRead, BecauseExclusive)>().as_mut()),
Err(err) => Err(err.map_src(|s| s.as_mut())),
}
}
Expand Down Expand Up @@ -4728,7 +4739,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
69 changes: 40 additions & 29 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use core::{
ptr::NonNull,
};

use super::{inner::PtrInner, invariant::*};
use crate::{
pointer::{inner::PtrInner, invariant::*, transmute::TransmuteFromPtr},
util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance},
AlignmentError, CastError, CastType, KnownLayout, SizeError, TryFromBytes, ValidityError,
};
Expand Down Expand Up @@ -391,7 +391,7 @@ mod _conversions {
{
/// Converts `self` to a transparent wrapper type into a `Ptr` to the
/// wrapped inner type.
pub(crate) fn transparent_wrapper_into_inner(
fn transparent_wrapper_into_inner(
self,
) -> Ptr<
'a,
Expand Down Expand Up @@ -430,6 +430,17 @@ mod _conversions {
where
I: Invariants,
{
pub fn transmute<U, V, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
where
T: KnownLayout,
V: Validity,
U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, R>
+ KnownLayout<PointerMetadata = T::PointerMetadata>
+ ?Sized,
{
self.transmute_unchecked(|t: NonNull<T>| U::cast_from_raw(t))
}

/// Casts to a different (unsized) target type without checking interior
/// mutability.
///
Expand Down Expand Up @@ -460,14 +471,14 @@ mod _conversions {
) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
where
V: Validity,
F: FnOnce(*mut T) -> *mut U,
F: FnOnce(NonNull<T>) -> NonNull<U>,
{
let ptr = cast(self.as_inner().as_non_null().as_ptr());
let ptr = cast(self.as_inner().as_non_null());

// SAFETY: Caller promises that `cast` returns a pointer whose
// address is in the range of `self.as_inner().as_non_null()`'s referent. By
// invariant, none of these addresses are null.
let ptr = unsafe { NonNull::new_unchecked(ptr) };
// // SAFETY: Caller promises that `cast` returns a pointer whose
// // address is in the range of `self.as_inner().as_non_null()`'s referent. By
// // invariant, none of these addresses are null.
// let ptr = unsafe { NonNull::new_unchecked(ptr) };

// SAFETY:
//
Expand Down Expand Up @@ -552,7 +563,7 @@ mod _conversions {
// validity of the other.
let ptr = unsafe {
#[allow(clippy::as_conversions)]
self.transmute_unchecked(|p: *mut T| p as *mut crate::Unalign<T>)
self.transmute_unchecked(NonNull::cast::<crate::Unalign<T>>)
};
ptr.bikeshed_recall_aligned()
}
Expand All @@ -561,6 +572,8 @@ mod _conversions {

/// State transitions between invariants.
mod _transitions {
use crate::pointer::transmute::TryTransmuteFromPtr;

use super::*;

impl<'a, T, I> Ptr<'a, T, I>
Expand Down Expand Up @@ -819,14 +832,11 @@ mod _transitions {
#[inline]
// TODO(#859): Reconsider the name of this method before making it
// public.
pub fn bikeshed_recall_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>
pub fn bikeshed_recall_valid<R>(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>
where
T: crate::FromBytes,
T: crate::FromBytes + TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid, R>,
I: Invariants<Validity = Initialized>,
{
// TODO(#1866): Fix this unsoundness.

// SAFETY: This is unsound!
unsafe { self.assume_valid() }
}

Expand All @@ -843,24 +853,24 @@ mod _transitions {
/// On error, unsafe code may rely on this method's returned
/// `ValidityError` containing `self`.
#[inline]
pub(crate) fn try_into_valid<R>(
pub(crate) fn try_into_valid<R, S>(
mut self,
) -> Result<Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>, ValidityError<Self, T>>
where
T: TryFromBytes + Read<I::Aliasing, R>,
T: TryFromBytes
+ Read<I::Aliasing, R>
+ TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid, S>,
I::Aliasing: Reference,
I: Invariants<Validity = Initialized>,
{
// 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
// fix before returning.
if T::is_bit_valid(self.reborrow().forget_aligned()) {
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
// contains a bit-valid instance of `Self`.
// TODO: Complete this safety comment.
//
// TODO(#1866): This is unsound! The returned `Ptr` may permit
// writing referents which do not satisfy the `Initialized`
// validity invariant of `self`.
// If `T::is_bit_valid`, code may assume that `self` contains a
// bit-valid instance of `Self`.
Ok(unsafe { self.assume_valid() })
} else {
Err(ValidityError::new(self))
Expand Down Expand Up @@ -904,7 +914,7 @@ mod _casts {
/// at ranges identical to those at which `UnsafeCell`s exist in `*p`
#[doc(hidden)]
#[inline]
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(*mut T) -> *mut U>(
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(NonNull<T>) -> NonNull<U>>(
self,
cast: F,
) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)>
Expand Down Expand Up @@ -947,7 +957,7 @@ mod _casts {
where
T: Read<I::Aliasing, R>,
U: 'a + ?Sized + Read<I::Aliasing, S> + CastableFrom<T, I::Validity, I::Validity>,
F: FnOnce(*mut T) -> *mut U,
F: FnOnce(NonNull<T>) -> NonNull<U>,
{
// SAFETY: Because `T` and `U` both implement `Read<I::Aliasing, _>`,
// either:
Expand Down Expand Up @@ -988,9 +998,8 @@ mod _casts {
// returned pointer addresses the same bytes as `p`
// - `slice_from_raw_parts_mut` and `.cast` both preserve provenance
let ptr: Ptr<'a, [u8], _> = unsafe {
self.cast_unsized(|p: *mut T| {
#[allow(clippy::as_conversions)]
core::ptr::slice_from_raw_parts_mut(p.cast::<u8>(), bytes)
self.cast_unsized(|p: NonNull<T>| {
core::ptr::NonNull::slice_from_raw_parts(p.cast::<u8>(), bytes)
})
};

Expand Down Expand Up @@ -1214,7 +1223,7 @@ mod _casts {
// inner type `T`. A consequence of this guarantee is that it is
// possible to convert between `T` and `UnsafeCell<T>`.
#[allow(clippy::as_conversions)]
let ptr = unsafe { self.transmute_unchecked(|p| p as *mut T) };
let ptr = unsafe { self.transmute_unchecked(|p| cast!(p => NonNull<T>)) };

// SAFETY: `UnsafeCell<T>` has the same alignment as `T` [1],
// and so if `self` is guaranteed to be aligned, then so is the
Expand Down Expand Up @@ -1321,10 +1330,12 @@ mod tests {
};

// SAFETY: The bytes in `slf` must be initialized.
unsafe fn validate_and_get_len<T: ?Sized + KnownLayout + FromBytes>(
unsafe fn validate_and_get_len<
T: ?Sized + KnownLayout + FromBytes + Immutable,
>(
slf: Ptr<'_, T, (Shared, Aligned, Initialized)>,
) -> usize {
let t = slf.bikeshed_recall_valid().as_ref();
let t = slf.bikeshed_recall_valid::<BecauseImmutable>().as_ref();

let bytes = {
let len = mem::size_of_val(t);
Expand Down
Loading

0 comments on commit 3e52166

Please sign in to comment.