Skip to content

Commit 02f5982

Browse files
joshlfjswrenn
andcommitted
[WIP] Transmute
Co-authored-by: Jack Wrenn <jswrenn@amazon.com> gherrit-pr-id: I8d5d162c1b6fe43e3dcb90a6dc5bf58a7a203bf8
1 parent 8ee868d commit 02f5982

File tree

8 files changed

+784
-124
lines changed

8 files changed

+784
-124
lines changed

src/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,20 @@ impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
594594
ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() }
595595
}
596596

597+
/// Changes the destination type.
598+
///
599+
/// # Safety
600+
///
601+
/// `NewDst` must have the same validity - as implemented by
602+
/// [`TryFromBytes::is_bit_valid`] - as `Dst`.
603+
pub(crate) unsafe fn with_dst<NewDst: TryFromBytes>(self) -> ValidityError<Src, NewDst> {
604+
// SAFETY: There is currently no invariant required of `dst`, so this
605+
// method's safety precondition is unnecessary. However, we require it
606+
// to be forwards-compatible with a point in time where we add an
607+
// invariant to the `Dst` type.
608+
ValidityError { src: self.src, dst: SendSyncPhantomData::default() }
609+
}
610+
597611
/// Converts the error into a general [`ConvertError`].
598612
pub(crate) const fn into<A, S>(self) -> ConvertError<A, S, Self> {
599613
ConvertError::Validity(self)

src/lib.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,10 @@ use core::{
375375
#[cfg(feature = "std")]
376376
use std::io;
377377

378-
use crate::pointer::invariant::{self, BecauseExclusive};
378+
use crate::pointer::{
379+
invariant::{self, BecauseExclusive},
380+
transmute::BecauseRead,
381+
};
379382

380383
#[cfg(any(feature = "alloc", test))]
381384
extern crate alloc;
@@ -1790,7 +1793,7 @@ pub unsafe trait TryFromBytes {
17901793
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
17911794
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
17921795
// condition will not happen.
1793-
match source.try_into_valid() {
1796+
match source.try_into_valid::<(pointer::transmute::BecauseRead, _)>() {
17941797
Ok(source) => Ok(source.as_mut()),
17951798
Err(e) => {
17961799
Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into())
@@ -2372,7 +2375,7 @@ pub unsafe trait TryFromBytes {
23722375
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
23732376
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
23742377
// condition will not happen.
2375-
match source.try_into_valid() {
2378+
match source.try_into_valid::<(BecauseRead, _)>() {
23762379
Ok(source) => Ok(source.as_mut()),
23772380
Err(e) => {
23782381
Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into())
@@ -2779,7 +2782,7 @@ fn try_ref_from_prefix_suffix<T: TryFromBytes + KnownLayout + Immutable + ?Sized
27792782
}
27802783

27812784
#[inline(always)]
2782-
fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized>(
2785+
fn try_mut_from_prefix_suffix<T: TryFromBytes + IntoBytes + KnownLayout + ?Sized>(
27832786
candidate: &mut [u8],
27842787
cast_type: CastType,
27852788
meta: Option<T::PointerMetadata>,
@@ -2794,7 +2797,7 @@ fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized
27942797
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
27952798
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
27962799
// condition will not happen.
2797-
match candidate.try_into_valid() {
2800+
match candidate.try_into_valid::<(pointer::transmute::BecauseRead, _)>() {
27982801
Ok(valid) => Ok((valid.as_mut(), prefix_suffix.as_mut())),
27992802
Err(e) => Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into()),
28002803
}
@@ -3508,7 +3511,7 @@ pub unsafe trait FromBytes: FromZeros {
35083511
{
35093512
static_assert_dst_is_not_zst!(Self);
35103513
match Ptr::from_ref(source).try_cast_into_no_leftover::<_, BecauseImmutable>(None) {
3511-
Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_ref()),
3514+
Ok(ptr) => Ok(ptr.transmute().as_ref()),
35123515
Err(err) => Err(err.map_src(|src| src.as_ref())),
35133516
}
35143517
}
@@ -3744,7 +3747,7 @@ pub unsafe trait FromBytes: FromZeros {
37443747
{
37453748
static_assert_dst_is_not_zst!(Self);
37463749
match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) {
3747-
Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_mut()),
3750+
Ok(ptr) => Ok(ptr.bikeshed_recall_valid::<(BecauseRead, BecauseExclusive)>().as_mut()),
37483751
Err(err) => Err(err.map_src(|src| src.as_mut())),
37493752
}
37503753
}
@@ -3983,7 +3986,7 @@ pub unsafe trait FromBytes: FromZeros {
39833986
let source = Ptr::from_ref(source);
39843987
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
39853988
match maybe_slf {
3986-
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_ref()),
3989+
Ok(slf) => Ok(slf.transmute().as_ref()),
39873990
Err(err) => Err(err.map_src(|s| s.as_ref())),
39883991
}
39893992
}
@@ -4214,7 +4217,7 @@ pub unsafe trait FromBytes: FromZeros {
42144217
let source = Ptr::from_mut(source);
42154218
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
42164219
match maybe_slf {
4217-
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_mut()),
4220+
Ok(slf) => Ok(slf.bikeshed_recall_valid::<(BecauseRead, BecauseExclusive)>().as_mut()),
42184221
Err(err) => Err(err.map_src(|s| s.as_mut())),
42194222
}
42204223
}
@@ -4590,7 +4593,7 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
45904593
let (slf, prefix_suffix) = Ptr::from_ref(source)
45914594
.try_cast_into::<_, BecauseImmutable>(cast_type, meta)
45924595
.map_err(|err| err.map_src(|s| s.as_ref()))?;
4593-
Ok((slf.bikeshed_recall_valid().as_ref(), prefix_suffix.as_ref()))
4596+
Ok((slf.transmute().as_ref(), prefix_suffix.as_ref()))
45944597
}
45954598

45964599
/// Interprets the given affix of the given bytes as a `&mut Self` without
@@ -4602,15 +4605,18 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
46024605
/// If there are insufficient bytes, or if that affix of `source` is not
46034606
/// appropriately aligned, this returns `Err`.
46044607
#[inline(always)]
4605-
fn mut_from_prefix_suffix<T: FromBytes + KnownLayout + ?Sized>(
4608+
fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
46064609
source: &mut [u8],
46074610
meta: Option<T::PointerMetadata>,
46084611
cast_type: CastType,
46094612
) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> {
46104613
let (slf, prefix_suffix) = Ptr::from_mut(source)
46114614
.try_cast_into::<_, BecauseExclusive>(cast_type, meta)
46124615
.map_err(|err| err.map_src(|s| s.as_mut()))?;
4613-
Ok((slf.bikeshed_recall_valid().as_mut(), prefix_suffix.as_mut()))
4616+
Ok((
4617+
slf.transmute::<invariant::Valid<T>, _, (BecauseRead, _), _>().as_mut(),
4618+
prefix_suffix.as_mut(),
4619+
))
46144620
}
46154621

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

src/pointer/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod inner;
1212
#[doc(hidden)]
1313
pub mod invariant;
1414
mod ptr;
15+
pub(crate) mod transmute;
1516

1617
#[doc(hidden)]
1718
pub use invariant::{BecauseExclusive, BecauseImmutable, Read};

src/pointer/ptr.rs

Lines changed: 139 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ mod _external {
171171
/// Methods for converting to and from `Ptr` and Rust's safe reference types.
172172
mod _conversions {
173173
use super::*;
174-
use crate::util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance};
174+
use crate::{
175+
pointer::transmute::{CastFrom, TransmuteFromAlignment, TransmuteFromPtr},
176+
util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance},
177+
};
175178

176179
/// `&'a T` → `Ptr<'a, T>`
177180
impl<'a, T: ?Sized> Ptr<'a, Valid<T>, (Shared, Aligned)> {
@@ -337,6 +340,54 @@ mod _conversions {
337340
}
338341
}
339342

343+
/// `Ptr<'a, T>` → `Ptr<'a, U>`
344+
impl<'a, V, I> Ptr<'a, V, I>
345+
where
346+
V: Validity,
347+
I: Invariants,
348+
{
349+
/// # Safety
350+
/// - TODO: `UnsafeCell` agreement
351+
/// - The caller promises that the returned `Ptr` satisfies alignment
352+
/// `A`
353+
/// - The caller promises that the returned `Ptr` satisfies validity `V`
354+
pub(crate) unsafe fn transmute_unchecked<U, A>(self) -> Ptr<'a, U, (I::Aliasing, A)>
355+
where
356+
A: Alignment,
357+
U: Validity,
358+
U::Inner: CastFrom<V::Inner>,
359+
{
360+
// SAFETY:
361+
// - By invariant on `CastFrom::cast_from`:
362+
// - This cast preserves address and referent size, and thus the
363+
// returned pointer addresses the same bytes as `p`
364+
// - This cast preserves provenance
365+
// - TODO: `UnsafeCell` agreement
366+
let ptr =
367+
unsafe { self.cast_unsized_unchecked::<U::Inner, _>(|p| U::Inner::cast_from(p)) };
368+
// SAFETY: The caller promises that alignment is satisfied.
369+
let ptr = unsafe { ptr.assume_alignment() };
370+
// SAFETY: The caller promises that validity is satisfied.
371+
let ptr = unsafe { ptr.assume_validity::<U>() };
372+
ptr.unify_validity()
373+
}
374+
375+
pub(crate) fn transmute<U, A, RT, RA>(self) -> Ptr<'a, U, (I::Aliasing, A)>
376+
where
377+
A: Alignment,
378+
U: TransmuteFromPtr<V, I::Aliasing, RT>,
379+
U::Inner: TransmuteFromAlignment<U::Inner, I::Alignment, A, RA> + CastFrom<V::Inner>,
380+
{
381+
// SAFETY:
382+
// - TODO: `UnsafeCell` agreement
383+
// - By invariant on `TransmuteFromPtr`, it is sound to treat the
384+
// resulting pointer as having alignment `A`
385+
// - By invariant on `TransmuteFromPtr`, it is sound to treat the
386+
// resulting pointer as having validity `V`
387+
unsafe { self.transmute_unchecked() }
388+
}
389+
}
390+
340391
/// `Ptr<'a, T = Wrapper<U>>` → `Ptr<'a, U>`
341392
impl<'a, V, I> Ptr<'a, V, I>
342393
where
@@ -428,7 +479,10 @@ mod _conversions {
428479
/// State transitions between invariants.
429480
mod _transitions {
430481
use super::*;
431-
use crate::{AlignmentError, TryFromBytes, ValidityError};
482+
use crate::{
483+
pointer::transmute::{CastFrom, TransmuteFromPtr, TryTransmuteFromPtr},
484+
AlignmentError, TryFromBytes, ValidityError,
485+
};
432486

433487
impl<'a, V, I> Ptr<'a, V, I>
434488
where
@@ -649,6 +703,22 @@ mod _transitions {
649703
}
650704
}
651705

706+
impl<'a, V, I> Ptr<'a, V, I>
707+
where
708+
V: Validity,
709+
I: Invariants,
710+
I::Aliasing: Reference,
711+
{
712+
/// Forgets that `self` is an `Exclusive` pointer, downgrading it to a
713+
/// `Shared` pointer.
714+
#[doc(hidden)]
715+
#[must_use]
716+
#[inline]
717+
pub const fn forget_exclusive(self) -> Ptr<'a, V, I::WithAliasing<Shared>> {
718+
unsafe { self.assume_invariants() }
719+
}
720+
}
721+
652722
impl<'a, T, I> Ptr<'a, Initialized<T>, I>
653723
where
654724
T: ?Sized,
@@ -660,14 +730,12 @@ mod _transitions {
660730
#[inline]
661731
// TODO(#859): Reconsider the name of this method before making it
662732
// public.
663-
pub const fn bikeshed_recall_valid(self) -> Ptr<'a, Valid<T>, I>
733+
pub fn bikeshed_recall_valid<R>(self) -> Ptr<'a, Valid<T>, I>
664734
where
665-
T: crate::FromBytes,
735+
Valid<T>: TransmuteFromPtr<Initialized<T>, I::Aliasing, R>,
736+
<Valid<T> as Validity>::Inner: CastFrom<T>,
666737
{
667-
// SAFETY: The bound `T: FromBytes` ensures that any initialized
668-
// sequence of bytes is bit-valid for `T`. `V = Initialized<T>`
669-
// ensures that all of the referent bytes are initialized.
670-
unsafe { self.assume_valid() }
738+
self.transmute().unify_invariants()
671739
}
672740

673741
/// Checks that `self`'s referent is validly initialized for `T`,
@@ -687,7 +755,12 @@ mod _transitions {
687755
mut self,
688756
) -> Result<Ptr<'a, Valid<T>, I>, ValidityError<Self, T>>
689757
where
690-
T: TryFromBytes + Read<I::Aliasing, R>,
758+
T: TryFromBytes, // + Read<I::Aliasing, R>,
759+
Valid<T>: TryTransmuteFromPtr<Initialized<T>, I::Aliasing, R>,
760+
// NOTE: This bound ought to be implied, but leaving it out causes
761+
// Rust to infinite loop during trait solving.
762+
<Valid<T> as Validity>::Inner:
763+
crate::pointer::transmute::CastFrom<<Initialized<T> as Validity>::Inner>,
691764
I::Aliasing: Reference,
692765
{
693766
// This call may panic. If that happens, it doesn't cause any soundness
@@ -702,12 +775,63 @@ mod _transitions {
702775
}
703776
}
704777
}
778+
779+
impl<'a, V, I> Ptr<'a, V, I>
780+
where
781+
V: Validity,
782+
I: Invariants,
783+
{
784+
/// Attempts to transmute a `Ptr<T>` into a `Ptr<U>`.
785+
///
786+
/// # Panics
787+
///
788+
/// This method will panic if
789+
/// [`U::is_bit_valid`][TryFromBytes::is_bit_valid] panics.
790+
///
791+
/// # Safety
792+
///
793+
/// On success, the returned `Ptr` addresses the same bytes as `self`.
794+
///
795+
/// On error, unsafe code may rely on this method's returned
796+
/// `ValidityError` containing `self`.
797+
#[inline]
798+
pub(crate) fn try_transmute<U, R, RR>(
799+
mut self,
800+
) -> Result<Ptr<'a, U, I::WithAlignment<Unknown>>, ValidityError<Self, U::Inner>>
801+
where
802+
U: Validity + TryTransmuteFromPtr<V, I::Aliasing, R>,
803+
U::Inner: TryFromBytes + CastFrom<V::Inner>,
804+
Initialized<U::Inner>: TransmuteFromPtr<V, I::Aliasing, RR>,
805+
// TODO: The `Sized` bound here is only required in order to call
806+
// `.bikeshed_try_into_aligned`. There are other ways of getting the
807+
// alignment of a type, and we could use these if we need to drop
808+
// this bound.
809+
<Initialized<U::Inner> as Validity>::Inner: Sized + CastFrom<V::Inner>,
810+
I::Aliasing: Reference,
811+
{
812+
let is_bit_valid = {
813+
let ptr = self.reborrow();
814+
let ptr = ptr.transmute::<Initialized<U::Inner>, Unknown, _, _>();
815+
// This call may panic. If that happens, it doesn't cause any
816+
// soundness issues, as we have not generated any invalid state
817+
// which we need to fix before returning.
818+
<U::Inner as TryFromBytes>::is_bit_valid(ptr)
819+
};
820+
821+
if is_bit_valid {
822+
let ptr = unsafe { self.transmute_unchecked() };
823+
Ok(ptr.unify_invariants())
824+
} else {
825+
Err(ValidityError::new(self))
826+
}
827+
}
828+
}
705829
}
706830

707831
/// Casts of the referent type.
708832
mod _casts {
709833
use super::*;
710-
use crate::{CastError, SizeError};
834+
use crate::{pointer::transmute::BecauseRead, CastError, SizeError};
711835

712836
impl<'a, V, I> Ptr<'a, V, I>
713837
where
@@ -866,7 +990,7 @@ mod _casts {
866990
})
867991
};
868992

869-
ptr.bikeshed_recall_aligned().bikeshed_recall_valid()
993+
ptr.bikeshed_recall_aligned().bikeshed_recall_valid::<(BecauseRead, _)>()
870994
}
871995
}
872996

@@ -1125,6 +1249,7 @@ mod tests {
11251249

11261250
mod test_ptr_try_cast_into_soundness {
11271251
use super::*;
1252+
use crate::IntoBytes;
11281253

11291254
// This test is designed so that if `Ptr::try_cast_into_xxx` are
11301255
// buggy, it will manifest as unsoundness that Miri can detect.
@@ -1164,7 +1289,9 @@ mod tests {
11641289
};
11651290

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

0 commit comments

Comments
 (0)