Skip to content

Commit 88213f3

Browse files
joshlfjswrenn
andcommitted
Generalize SizeEq to SizeFrom, support shrinking
Generalize `SizeEq`, renaming it to `SizeFrom`, and supporting casts which shrink rather than preserve referent size. This requires fairly deep surgery in our transmute taxonomy. We can no longer rely on `SizeFrom::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) - Overwriting transmutes (in which the source value is larger than the destination value *and* the destination permits mutation). In an overwriting 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.) In order to support these semantics, we split `TransmuteFrom` into two traits: `TransmuteFrom` (which supports shriking transmutes) and `TransmuteOverwrite` (which supports overwriting transmutes). While we're here, we simplify the relationship between `TryTransmuteFromPtr`, `MutationCompatible`, and `Read`. In particular, `TryTransmuteFromPtr` now has a single blanket impl which is bounded by `MutationCompatible`, and `MutationCompatible` now directly supports three cases (and no longer delegates to `Read`): - Exclusive reference transmutes in which the destination type may be transmuted into the source type - Shared reference transmutes in which both the source and the destination type are `Immutable` - Shared reference transmutes which permit interior mutation, in which the source and destination types may both be transmuted into one another, and in which the source and destination types support compatible interior mutation Makes progress on #1817 Co-authored-by: Jack Wrenn <[email protected]> gherrit-pr-id: I6c793a9620ad75bdc0d26ab7c7cd1a0c7bef1b8b
1 parent 0dc4ac5 commit 88213f3

File tree

13 files changed

+450
-300
lines changed

13 files changed

+450
-300
lines changed

src/impls.rs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,13 @@ assert_unaligned!(bool);
105105
// pattern 0x01.
106106
const _: () = unsafe {
107107
unsafe_impl!(=> TryFromBytes for bool; |byte| {
108-
let byte = byte.transmute::<u8, invariant::Valid, _>();
108+
let mut byte = byte;
109+
let byte = byte.reborrow().into_shared().transmute::<u8, invariant::Valid, _>();
109110
*byte.unaligned_as_ref() < 2
110111
})
111112
};
112-
impl_size_eq!(bool, u8);
113+
114+
impl_size_from!(bool, u8);
113115

114116
// SAFETY:
115117
// - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s.
@@ -134,13 +136,14 @@ const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) };
134136
// `char`.
135137
const _: () = unsafe {
136138
unsafe_impl!(=> TryFromBytes for char; |c| {
137-
let c = c.transmute::<Unalign<u32>, invariant::Valid, _>();
138-
let c = c.read_unaligned().into_inner();
139+
let mut c = c;
140+
let c = c.reborrow().into_shared().transmute::<Unalign<u32>, invariant::Valid, _>();
141+
let c = c.read_unaligned::<BecauseImmutable>().into_inner();
139142
char::from_u32(c).is_some()
140143
});
141144
};
142145

143-
impl_size_eq!(char, Unalign<u32>);
146+
impl_size_from!(char, Unalign<u32>);
144147

145148
// SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`.
146149
// - `Immutable`: `[u8]` does not contain any `UnsafeCell`s.
@@ -167,22 +170,23 @@ const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unalig
167170
// Returns `Err` if the slice is not UTF-8.
168171
const _: () = unsafe {
169172
unsafe_impl!(=> TryFromBytes for str; |c| {
170-
let c = c.transmute::<[u8], invariant::Valid, _>();
173+
let mut c = c;
174+
let c = c.reborrow().into_shared().transmute::<[u8], invariant::Valid, _>();
171175
let c = c.unaligned_as_ref();
172176
core::str::from_utf8(c).is_ok()
173177
})
174178
};
175179

176-
impl_size_eq!(str, [u8]);
180+
impl_size_from!(str, [u8]);
177181

178182
macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
179183
($($nonzero:ident[$prim:ty]),*) => {
180184
$(
181185
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
182-
impl_size_eq!($nonzero, Unalign<$prim>);
183-
184-
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
185-
$nonzero::new(n.read_unaligned().into_inner()).is_some()
186+
impl_size_from!($nonzero, Unalign<$prim>);
187+
let mut n = n;
188+
let n = n.reborrow().into_shared().transmute::<Unalign<$prim>, invariant::Valid, _>();
189+
$nonzero::new(n.read_unaligned::<BecauseImmutable>().into_inner()).is_some()
186190
});
187191
)*
188192
}
@@ -396,53 +400,59 @@ mod atomics {
396400
($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{
397401
crate::util::macros::__unsafe();
398402

399-
use core::cell::UnsafeCell;
400-
use crate::pointer::{PtrInner, SizeEq, TransmuteFrom, invariant::Valid};
403+
use core::{cell::UnsafeCell};
404+
use crate::pointer::{TransmuteFrom, PtrInner, SizeFrom, invariant::Valid};
401405

402406
$(
403407
// SAFETY: The caller promised that `$atomic` and `$prim` have
404-
// the same size and bit validity.
408+
// the same size and bit validity. As a result of size equality,
409+
// both impls of `SizeFrom::cast_from_raw` preserve referent
410+
// size exactly.
405411
unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, Valid, Valid> for $prim {}
406412
// SAFETY: The caller promised that `$atomic` and `$prim` have
407-
// the same size and bit validity.
413+
// the same size and bit validity. As a result of size equality,
414+
// both impls of `SizeFrom::cast_from_raw` preserve referent
415+
// size exactly.
408416
unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {}
409417

410-
// SAFETY: The caller promised that `$atomic` and `$prim` have
411-
// the same size.
412-
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
418+
// SAFETY: See inline safety comment.
419+
unsafe impl<$($tyvar)?> SizeFrom<$atomic> for $prim {
413420
#[inline]
414421
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
422+
// SAFETY: The caller promised that `$atomic` and `$prim`
423+
// have the same size. Thus, this cast preserves
417424
// address, referent size, and provenance.
418425
unsafe { cast!(a) }
419426
}
420427
}
421428
// SAFETY: See previous safety comment.
422-
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
429+
unsafe impl<$($tyvar)?> SizeFrom<$prim> for $atomic {
423430
#[inline]
424431
fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> {
425432
// SAFETY: See previous safety comment.
426433
unsafe { cast!(p) }
427434
}
428435
}
429-
// SAFETY: The caller promised that `$atomic` and `$prim` have
430-
// the same size. `UnsafeCell<T>` has the same size as `T` [1].
436+
437+
// SAFETY: The caller promised that `$atomic` and `$prim`
438+
// have the same size. `UnsafeCell<T>` has the same size as
439+
// `T` [1]. Thus, this cast preserves address, referent
440+
// size, and provenance.
431441
//
432442
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
433443
//
434444
// `UnsafeCell<T>` has the same in-memory representation as
435445
// its inner type `T`. A consequence of this guarantee is that
436446
// it is possible to convert between `T` and `UnsafeCell<T>`.
437-
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
447+
unsafe impl<$($tyvar)?> SizeFrom<$atomic> for UnsafeCell<$prim> {
438448
#[inline]
439449
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> {
440450
// SAFETY: See previous safety comment.
441451
unsafe { cast!(a) }
442452
}
443453
}
444454
// SAFETY: See previous safety comment.
445-
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
455+
unsafe impl<$($tyvar)?> SizeFrom<UnsafeCell<$prim>> for $atomic {
446456
#[inline]
447457
fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> {
448458
// SAFETY: See previous safety comment.
@@ -452,7 +462,9 @@ mod atomics {
452462

453463
// SAFETY: The caller promised that `$atomic` and `$prim` have
454464
// the same bit validity. `UnsafeCell<T>` has the same bit
455-
// validity as `T` [1].
465+
// validity as `T` [1]. `UnsafeCell<T>` also has the same size
466+
// as `T` [1], and so both impls of `SizeFrom::cast_from_raw`
467+
// preserve referent size exactly.
456468
//
457469
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
458470
//

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 SizeFrom<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 [`SizeFrom::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::SizeFrom::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/lib.rs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ use core::{
381381
#[cfg(feature = "std")]
382382
use std::io;
383383

384-
use crate::pointer::invariant::{self, BecauseExclusive};
384+
use crate::pointer::{invariant, BecauseBidirectional};
385385

386386
#[cfg(any(feature = "alloc", test, kani))]
387387
extern crate alloc;
@@ -1840,7 +1840,7 @@ pub unsafe trait TryFromBytes {
18401840
Self: KnownLayout + IntoBytes,
18411841
{
18421842
static_assert_dst_is_not_zst!(Self);
1843-
match Ptr::from_mut(bytes).try_cast_into_no_leftover::<Self, BecauseExclusive>(None) {
1843+
match Ptr::from_mut(bytes).try_cast_into_no_leftover(None) {
18441844
Ok(source) => {
18451845
// This call may panic. If that happens, it doesn't cause any soundness
18461846
// issues, as we have not generated any invalid state which we need to
@@ -1852,9 +1852,7 @@ pub unsafe trait TryFromBytes {
18521852
// condition will not happen.
18531853
match source.try_into_valid() {
18541854
Ok(source) => Ok(source.as_mut()),
1855-
Err(e) => {
1856-
Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into())
1857-
}
1855+
Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()),
18581856
}
18591857
}
18601858
Err(e) => Err(e.map_src(Ptr::as_mut).into()),
@@ -2421,8 +2419,7 @@ pub unsafe trait TryFromBytes {
24212419
where
24222420
Self: KnownLayout<PointerMetadata = usize> + IntoBytes,
24232421
{
2424-
match Ptr::from_mut(source).try_cast_into_no_leftover::<Self, BecauseExclusive>(Some(count))
2425-
{
2422+
match Ptr::from_mut(source).try_cast_into_no_leftover(Some(count)) {
24262423
Ok(source) => {
24272424
// This call may panic. If that happens, it doesn't cause any soundness
24282425
// issues, as we have not generated any invalid state which we need to
@@ -2434,9 +2431,7 @@ pub unsafe trait TryFromBytes {
24342431
// condition will not happen.
24352432
match source.try_into_valid() {
24362433
Ok(source) => Ok(source.as_mut()),
2437-
Err(e) => {
2438-
Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into())
2439-
}
2434+
Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()),
24402435
}
24412436
}
24422437
Err(e) => Err(e.map_src(Ptr::as_mut).into()),
@@ -2844,7 +2839,7 @@ fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized
28442839
cast_type: CastType,
28452840
meta: Option<T::PointerMetadata>,
28462841
) -> Result<(&mut T, &mut [u8]), TryCastError<&mut [u8], T>> {
2847-
match Ptr::from_mut(candidate).try_cast_into::<T, BecauseExclusive>(cast_type, meta) {
2842+
match Ptr::from_mut(candidate).try_cast_into(cast_type, meta) {
28482843
Ok((candidate, prefix_suffix)) => {
28492844
// This call may panic. If that happens, it doesn't cause any soundness
28502845
// issues, as we have not generated any invalid state which we need to
@@ -2856,7 +2851,7 @@ fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized
28562851
// condition will not happen.
28572852
match candidate.try_into_valid() {
28582853
Ok(valid) => Ok((valid.as_mut(), prefix_suffix.as_mut())),
2859-
Err(e) => Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into()),
2854+
Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()),
28602855
}
28612856
}
28622857
Err(e) => Err(e.map_src(Ptr::as_mut).into()),
@@ -3832,8 +3827,8 @@ pub unsafe trait FromBytes: FromZeros {
38323827
Self: IntoBytes + KnownLayout,
38333828
{
38343829
static_assert_dst_is_not_zst!(Self);
3835-
match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) {
3836-
Ok(ptr) => Ok(ptr.recall_validity::<_, (_, (_, _))>().as_mut()),
3830+
match Ptr::from_mut(source).try_cast_into_no_leftover(None) {
3831+
Ok(ptr) => Ok(ptr.recall_validity::<_, BecauseBidirectional>().as_mut()),
38373832
Err(err) => Err(err.map_src(|src| src.as_mut())),
38383833
}
38393834
}
@@ -4301,11 +4296,9 @@ pub unsafe trait FromBytes: FromZeros {
43014296
Self: IntoBytes + KnownLayout<PointerMetadata = usize> + Immutable,
43024297
{
43034298
let source = Ptr::from_mut(source);
4304-
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
4299+
let maybe_slf = source.try_cast_into_no_leftover(Some(count));
43054300
match maybe_slf {
4306-
Ok(slf) => Ok(slf
4307-
.recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>()
4308-
.as_mut()),
4301+
Ok(slf) => Ok(slf.recall_validity::<_, BecauseBidirectional>().as_mut()),
43094302
Err(err) => Err(err.map_src(|s| s.as_mut())),
43104303
}
43114304
}
@@ -4662,7 +4655,7 @@ pub unsafe trait FromBytes: FromZeros {
46624655
// cannot be violated even though `buf` may have more permissive bit
46634656
// validity than `ptr`.
46644657
let ptr = unsafe { ptr.assume_validity::<invariant::Initialized>() };
4665-
let ptr = ptr.as_bytes::<BecauseExclusive>();
4658+
let ptr = ptr.as_bytes();
46664659
src.read_exact(ptr.as_mut())?;
46674660
// SAFETY: `buf` entirely consists of initialized bytes, and `Self` is
46684661
// `FromBytes`.
@@ -4781,9 +4774,9 @@ fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
47814774
cast_type: CastType,
47824775
) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> {
47834776
let (slf, prefix_suffix) = Ptr::from_mut(source)
4784-
.try_cast_into::<_, BecauseExclusive>(cast_type, meta)
4777+
.try_cast_into(cast_type, meta)
47854778
.map_err(|err| err.map_src(|s| s.as_mut()))?;
4786-
Ok((slf.recall_validity::<_, (_, (_, _))>().as_mut(), prefix_suffix.as_mut()))
4779+
Ok((slf.recall_validity::<_, BecauseBidirectional>().as_mut(), prefix_suffix.as_mut()))
47874780
}
47884781

47894782
/// Analyzes whether a type is [`IntoBytes`].

src/pointer/invariant.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -198,25 +198,6 @@ unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Uninit, Uninit> for DT {}
198198
// SAFETY: `SV = DV = Initialized`.
199199
unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Initialized, Initialized> for DT {}
200200

201-
/// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations.
202-
///
203-
/// `T: Read<A, R>` implies that a pointer to `T` with aliasing `A` permits
204-
/// unsynchronized read operations. This can be because `A` is [`Exclusive`] or
205-
/// because `T` does not permit interior mutation.
206-
///
207-
/// # Safety
208-
///
209-
/// `T: Read<A, R>` if either of the following conditions holds:
210-
/// - `A` is [`Exclusive`]
211-
/// - `T` implements [`Immutable`](crate::Immutable)
212-
///
213-
/// As a consequence, if `T: Read<A, R>`, then any `Ptr<T, (A, ...)>` is
214-
/// permitted to perform unsynchronized reads from its referent.
215-
pub trait Read<A: Aliasing, R> {}
216-
217-
impl<A: Aliasing, T: ?Sized + crate::Immutable> Read<A, BecauseImmutable> for T {}
218-
impl<T: ?Sized> Read<Exclusive, BecauseExclusive> for T {}
219-
220201
/// Unsynchronized reads are permitted because only one live [`Ptr`](crate::Ptr)
221202
/// or reference may exist to the referent bytes at a time.
222203
#[derive(Copy, Clone, Debug)]

src/pointer/mod.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ mod transmute;
1717
#[doc(hidden)]
1818
pub use {inner::PtrInner, transmute::*};
1919
#[doc(hidden)]
20-
pub use {
21-
invariant::{BecauseExclusive, BecauseImmutable, Read},
22-
ptr::Ptr,
23-
};
20+
pub use {invariant::BecauseExclusive, ptr::Ptr};
2421

2522
/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
2623
/// to [`TryFromBytes::is_bit_valid`].
@@ -30,11 +27,11 @@ pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unali
3027
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
3128

3229
/// Checks if the referent is zeroed.
33-
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool
30+
pub(crate) fn is_zeroed<T, I>(mut ptr: Ptr<'_, T, I>) -> bool
3431
where
3532
T: crate::Immutable + crate::KnownLayout,
3633
I: invariant::Invariants<Validity = invariant::Initialized>,
3734
I::Aliasing: invariant::Reference,
3835
{
39-
ptr.as_bytes::<BecauseImmutable>().as_ref().iter().all(|&byte| byte == 0)
36+
ptr.reborrow().into_shared().as_bytes().as_ref().iter().all(|&byte| byte == 0)
4037
}

0 commit comments

Comments
 (0)