Skip to content

[WIP] Update TransmuteFrom safety proofs #2469

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

Closed
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
57 changes: 33 additions & 24 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ const _: () = unsafe {
*byte.unaligned_as_ref() < 2
})
};
impl_size_eq!(bool, u8);

impl_size_compat!(bool, u8);

// SAFETY:
// - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s.
Expand Down Expand Up @@ -140,7 +141,7 @@ const _: () = unsafe {
});
};

impl_size_eq!(char, Unalign<u32>);
impl_size_compat!(char, Unalign<u32>);

// SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`.
// - `Immutable`: `[u8]` does not contain any `UnsafeCell`s.
Expand Down Expand Up @@ -173,14 +174,13 @@ const _: () = unsafe {
})
};

impl_size_eq!(str, [u8]);
impl_size_compat!(str, [u8]);

macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
($($nonzero:ident[$prim:ty]),*) => {
$(
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
impl_size_eq!($nonzero, Unalign<$prim>);

impl_size_compat!($nonzero, Unalign<$prim>);
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
$nonzero::new(n.read_unaligned().into_inner()).is_some()
});
Expand Down Expand Up @@ -396,63 +396,72 @@ mod atomics {
($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{
crate::util::macros::__unsafe();

use core::cell::UnsafeCell;
use crate::pointer::{PtrInner, SizeEq, TransmuteFrom, invariant::Valid};
use core::{cell::UnsafeCell};
use crate::pointer::{TransmuteFrom, PtrInner, SizeCompat, invariant::Valid};

$(
// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same size and bit validity.
// SAFETY: The caller promised that `$atomic` and `$prim`
// have the same size and bit validity. As a result of size
// equality, both impls of `SizeCompat::cast_from_raw`
// preserve referent size exactly.
unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, Valid, Valid> for $prim {}
// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same size and bit validity.
// SAFETY: The caller promised that `$atomic` and `$prim`
// have the same size and bit validity. As a result of size
// equality, both impls of `SizeCompat::cast_from_raw`
// preserve referent size exactly.
unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {}

// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same size.
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
// SAFETY: See inline safety comment.
unsafe impl<$($tyvar)?> SizeCompat<$atomic> for $prim {
#[inline]
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, $prim> {
// SAFETY: The caller promised that `$atomic` and
// `$prim` have the same size. Thus, this cast preserves
// SAFETY: The caller promised that `$atomic` and `$prim`
// have the same size. Thus, this cast preserves
// address, referent size, and provenance.
unsafe { cast!(a) }
}
}
// SAFETY: See previous safety comment.
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
unsafe impl<$($tyvar)?> SizeCompat<$prim> for $atomic {
#[inline]
fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> {
// SAFETY: See previous safety comment.
unsafe { cast!(p) }
}
}
// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same size. `UnsafeCell<T>` has the same size as `T` [1].

// SAFETY: The caller promised that `$atomic` and `$prim`
// have the same size. `UnsafeCell<T>` has the same size as
// `T` [1]. Thus, this cast preserves address, referent
// size, and provenance.
//
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
//
// `UnsafeCell<T>` has the same in-memory representation as
// its inner type `T`. A consequence of this guarantee is that
// it is possible to convert between `T` and `UnsafeCell<T>`.
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
unsafe impl<$($tyvar)?> SizeCompat<$atomic> for UnsafeCell<$prim> {
#[inline]
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> {
// SAFETY: See previous safety comment.
unsafe { cast!(a) }
}
}
// SAFETY: See previous safety comment.
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
unsafe impl<$($tyvar)?> SizeCompat<UnsafeCell<$prim>> for $atomic {
#[inline]
fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> {
// SAFETY: See previous safety comment.
unsafe { cast!(p) }
}
}

// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same bit validity. `UnsafeCell<T>` has the same bit
// validity as `T` [1].
// SAFETY: The caller promised that `$atomic` and `$prim`
// have the same bit validity. `UnsafeCell<T>` has the same
// bit validity as `T` [1]. `UnsafeCell<T>` also has the
// same size as `T` [1], and so both impls of
// `SizeCompat::cast_from_raw` preserve referent size
// exactly.
//
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
//
Expand Down
6 changes: 3 additions & 3 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,15 +612,15 @@ pub(crate) use cast_from_raw::cast_from_raw;
mod cast_from_raw {
use crate::{pointer::PtrInner, *};

/// Implements [`<Dst as SizeEq<Src>>::cast_from_raw`][cast_from_raw].
/// Implements [`<Dst as SizeCompat<Src>>::cast_from_raw`][cast_from_raw].
///
/// # PME
///
/// Generates a post-monomorphization error if it is not possible to satisfy
/// the soundness conditions of [`SizeEq::cast_from_raw`][cast_from_raw]
/// the soundness conditions of [`SizeCompat::cast_from_raw`][cast_from_raw]
/// for `Src` and `Dst`.
///
/// [cast_from_raw]: crate::pointer::SizeEq::cast_from_raw
/// [cast_from_raw]: crate::pointer::SizeCompat::cast_from_raw
//
// FIXME(#1817): Support Sized->Unsized and Unsized->Sized casts
pub(crate) fn cast_from_raw<Src, Dst>(src: PtrInner<'_, Src>) -> PtrInner<'_, Dst>
Expand Down
25 changes: 24 additions & 1 deletion src/pointer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ mod _def {
/// Rust allocation, `A`.
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
/// for at least `'a`.
pub(crate) const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
#[inline(always)]
#[must_use]
pub const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
// SAFETY: The caller has promised to satisfy all safety invariants
// of `PtrInner`.
Self { ptr, _marker: PhantomData }
Expand Down Expand Up @@ -160,6 +162,27 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
// single allocated object.
unsafe { Self::new(ptr) }
}

#[must_use]
#[inline(always)]
pub fn cast_sized<U>(self) -> PtrInner<'a, U>
where
T: Sized,
{
static_assert!(T, U => size_of::<T>() >= size_of::<U>());
let ptr = self.as_non_null().cast::<U>();

// SAFETY: By the preceding assert, `U` is no larger than `T`. Thus,
// `ptr` addresses a subset of the bytes addressed by `self`.
//
// 0. By invariant on `self`, if `self`'s referent is not zero sized,
// then `self` has valid provenance for its referent, which is
// entirely contained in some Rust allocation, `A`. Thus, the same
// holds of `ptr`.
// 1. By invariant on `self`, if `self`'s referent is not zero sized,
// then `A` is guaranteed to live for at least `'a`.
unsafe { PtrInner::new(ptr) }
}
}

#[allow(clippy::needless_lifetimes)]
Expand Down
Loading
Loading