Skip to content

Commit 20f0bc3

Browse files
joshlfjswrenn
andcommitted
Generalize SizeEq to SizeCompat
Generalize `SizeEq`, renaming it to `SizeCompat`, and supporting casts which do not preserve referent size. This requires fairly deep surgery in our transmute taxonomy. We can no longer rely on `SizeCompat::cast_from_raw` preserving referent size, which means that all pointer transmutes must now reason about two types of transmutes: - Size-shrinking transmutes (in which the source value is larger than the destination value) - Tearing transmutes (in which the source value is larger than the destination value *and* the destination permits mutation). In a tearing transmute, the resulting value is a concatenation of a valid `T` and the *suffix* of a valid `U`. (Note that size-preserving transmutes can be viewed as a special case of size-shrinking transmutes.) Makes progress on #1817 Co-authored-by: Jack Wrenn <[email protected]> gherrit-pr-id: I6c793a9620ad75bdc0d26ab7c7cd1a0c7bef1b8b
1 parent 01918b7 commit 20f0bc3

File tree

9 files changed

+314
-232
lines changed

9 files changed

+314
-232
lines changed

src/impls.rs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ const _: () = unsafe {
109109
*byte.unaligned_as_ref() < 2
110110
})
111111
};
112-
impl_size_eq!(bool, u8);
112+
113+
impl_size_compat!(bool, u8);
113114

114115
// SAFETY:
115116
// - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s.
@@ -140,7 +141,7 @@ const _: () = unsafe {
140141
});
141142
};
142143

143-
impl_size_eq!(char, Unalign<u32>);
144+
impl_size_compat!(char, Unalign<u32>);
144145

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

176-
impl_size_eq!(str, [u8]);
177+
impl_size_compat!(str, [u8]);
177178

178179
macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
179180
($($nonzero:ident[$prim:ty]),*) => {
180181
$(
181182
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
182-
impl_size_eq!($nonzero, Unalign<$prim>);
183-
183+
impl_size_compat!($nonzero, Unalign<$prim>);
184184
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
185185
$nonzero::new(n.read_unaligned().into_inner()).is_some()
186186
});
@@ -396,63 +396,72 @@ mod atomics {
396396
($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{
397397
crate::util::macros::__unsafe();
398398

399-
use core::cell::UnsafeCell;
400-
use crate::pointer::{PtrInner, SizeEq, TransmuteFrom, invariant::Valid};
399+
use core::{cell::UnsafeCell};
400+
use crate::pointer::{TransmuteFrom, PtrInner, SizeCompat, invariant::Valid};
401401

402402
$(
403-
// SAFETY: The caller promised that `$atomic` and `$prim` have
404-
// the same size and bit validity.
403+
// SAFETY: The caller promised that `$atomic` and `$prim`
404+
// have the same size and bit validity. As a result of size
405+
// equality, both impls of `SizeCompat::cast_from_raw`
406+
// preserve referent size exactly.
405407
unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, Valid, Valid> for $prim {}
406-
// SAFETY: The caller promised that `$atomic` and `$prim` have
407-
// the same size and bit validity.
408+
// SAFETY: The caller promised that `$atomic` and `$prim`
409+
// have the same size and bit validity. As a result of size
410+
// equality, both impls of `SizeCompat::cast_from_raw`
411+
// preserve referent size exactly.
408412
unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {}
409413

410-
// SAFETY: The caller promised that `$atomic` and `$prim` have
411-
// the same size.
412-
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
414+
// SAFETY: See inline safety comment.
415+
unsafe impl<$($tyvar)?> SizeCompat<$atomic> for $prim {
413416
#[inline]
414417
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, $prim> {
415-
// SAFETY: The caller promised that `$atomic` and
416-
// `$prim` have the same size. Thus, this cast preserves
418+
// SAFETY: The caller promised that `$atomic` and `$prim`
419+
// have the same size. Thus, this cast preserves
417420
// address, referent size, and provenance.
418421
unsafe { cast!(a) }
419422
}
420423
}
421424
// SAFETY: See previous safety comment.
422-
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
425+
unsafe impl<$($tyvar)?> SizeCompat<$prim> for $atomic {
423426
#[inline]
424427
fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> {
425428
// SAFETY: See previous safety comment.
426429
unsafe { cast!(p) }
427430
}
428431
}
429-
// SAFETY: The caller promised that `$atomic` and `$prim` have
430-
// the same size. `UnsafeCell<T>` has the same size as `T` [1].
432+
433+
// SAFETY: The caller promised that `$atomic` and `$prim`
434+
// have the same size. `UnsafeCell<T>` has the same size as
435+
// `T` [1]. Thus, this cast preserves address, referent
436+
// size, and provenance.
431437
//
432438
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
433439
//
434440
// `UnsafeCell<T>` has the same in-memory representation as
435441
// its inner type `T`. A consequence of this guarantee is that
436442
// it is possible to convert between `T` and `UnsafeCell<T>`.
437-
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
443+
unsafe impl<$($tyvar)?> SizeCompat<$atomic> for UnsafeCell<$prim> {
438444
#[inline]
439445
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> {
440446
// SAFETY: See previous safety comment.
441447
unsafe { cast!(a) }
442448
}
443449
}
444450
// SAFETY: See previous safety comment.
445-
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
451+
unsafe impl<$($tyvar)?> SizeCompat<UnsafeCell<$prim>> for $atomic {
446452
#[inline]
447453
fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> {
448454
// SAFETY: See previous safety comment.
449455
unsafe { cast!(p) }
450456
}
451457
}
452458

453-
// SAFETY: The caller promised that `$atomic` and `$prim` have
454-
// the same bit validity. `UnsafeCell<T>` has the same bit
455-
// validity as `T` [1].
459+
// SAFETY: The caller promised that `$atomic` and `$prim`
460+
// have the same bit validity. `UnsafeCell<T>` has the same
461+
// bit validity as `T` [1]. `UnsafeCell<T>` also has the
462+
// same size as `T` [1], and so both impls of
463+
// `SizeCompat::cast_from_raw` preserve referent size
464+
// exactly.
456465
//
457466
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
458467
//

src/layout.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -612,15 +612,15 @@ pub(crate) use cast_from_raw::cast_from_raw;
612612
mod cast_from_raw {
613613
use crate::{pointer::PtrInner, *};
614614

615-
/// Implements [`<Dst as SizeEq<Src>>::cast_from_raw`][cast_from_raw].
615+
/// Implements [`<Dst as SizeCompat<Src>>::cast_from_raw`][cast_from_raw].
616616
///
617617
/// # PME
618618
///
619619
/// Generates a post-monomorphization error if it is not possible to satisfy
620-
/// the soundness conditions of [`SizeEq::cast_from_raw`][cast_from_raw]
620+
/// the soundness conditions of [`SizeCompat::cast_from_raw`][cast_from_raw]
621621
/// for `Src` and `Dst`.
622622
///
623-
/// [cast_from_raw]: crate::pointer::SizeEq::cast_from_raw
623+
/// [cast_from_raw]: crate::pointer::SizeCompat::cast_from_raw
624624
//
625625
// FIXME(#1817): Support Sized->Unsized and Unsized->Sized casts
626626
pub(crate) fn cast_from_raw<Src, Dst>(src: PtrInner<'_, Src>) -> PtrInner<'_, Dst>

src/pointer/inner.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ mod _def {
8989
/// Rust allocation, `A`.
9090
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
9191
/// for at least `'a`.
92-
pub(crate) const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
92+
#[inline(always)]
93+
#[must_use]
94+
pub const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
9395
// SAFETY: The caller has promised to satisfy all safety invariants
9496
// of `PtrInner`.
9597
Self { ptr, _marker: PhantomData }
@@ -160,6 +162,27 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
160162
// single allocated object.
161163
unsafe { Self::new(ptr) }
162164
}
165+
166+
#[must_use]
167+
#[inline(always)]
168+
pub fn cast_sized<U>(self) -> PtrInner<'a, U>
169+
where
170+
T: Sized,
171+
{
172+
static_assert!(T, U => size_of::<T>() >= size_of::<U>());
173+
let ptr = self.as_non_null().cast::<U>();
174+
175+
// SAFETY: By the preceding assert, `U` is no larger than `T`. Thus,
176+
// `ptr` addresses a subset of the bytes addressed by `self`.
177+
//
178+
// 0. By invariant on `self`, if `self`'s referent is not zero sized,
179+
// then `self` has valid provenance for its referent, which is
180+
// entirely contained in some Rust allocation, `A`. Thus, the same
181+
// holds of `ptr`.
182+
// 1. By invariant on `self`, if `self`'s referent is not zero sized,
183+
// then `A` is guaranteed to live for at least `'a`.
184+
unsafe { PtrInner::new(ptr) }
185+
}
163186
}
164187

165188
#[allow(clippy::needless_lifetimes)]

0 commit comments

Comments
 (0)