Skip to content

Commit 660856a

Browse files
committed
[WIP] Transmute
gherrit-pr-id: I8d5d162c1b6fe43e3dcb90a6dc5bf58a7a203bf8
1 parent 53cca63 commit 660856a

File tree

6 files changed

+712
-114
lines changed

6 files changed

+712
-114
lines changed

src/lib.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,10 @@ use core::{
367367
#[cfg(feature = "std")]
368368
use std::io;
369369

370-
use crate::pointer::invariant::{self, BecauseExclusive};
370+
use crate::pointer::{
371+
invariant::{self, BecauseExclusive},
372+
transmute::BecauseRead,
373+
};
371374

372375
#[cfg(any(feature = "alloc", test))]
373376
extern crate alloc;
@@ -1782,7 +1785,7 @@ pub unsafe trait TryFromBytes {
17821785
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
17831786
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
17841787
// condition will not happen.
1785-
match source.try_into_valid() {
1788+
match source.try_into_valid::<(pointer::transmute::BecauseRead, _)>() {
17861789
Ok(source) => Ok(source.as_mut()),
17871790
Err(e) => {
17881791
Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into())
@@ -2364,7 +2367,7 @@ pub unsafe trait TryFromBytes {
23642367
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
23652368
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
23662369
// condition will not happen.
2367-
match source.try_into_valid() {
2370+
match source.try_into_valid::<(BecauseRead, _)>() {
23682371
Ok(source) => Ok(source.as_mut()),
23692372
Err(e) => {
23702373
Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into())
@@ -2771,7 +2774,7 @@ fn try_ref_from_prefix_suffix<T: TryFromBytes + KnownLayout + Immutable + ?Sized
27712774
}
27722775

27732776
#[inline(always)]
2774-
fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized>(
2777+
fn try_mut_from_prefix_suffix<T: TryFromBytes + IntoBytes + KnownLayout + ?Sized>(
27752778
candidate: &mut [u8],
27762779
cast_type: CastType,
27772780
meta: Option<T::PointerMetadata>,
@@ -2786,7 +2789,7 @@ fn try_mut_from_prefix_suffix<T: IntoBytes + TryFromBytes + KnownLayout + ?Sized
27862789
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
27872790
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
27882791
// condition will not happen.
2789-
match candidate.try_into_valid() {
2792+
match candidate.try_into_valid::<(pointer::transmute::BecauseRead, _)>() {
27902793
Ok(valid) => Ok((valid.as_mut(), prefix_suffix.as_mut())),
27912794
Err(e) => Err(e.map_src(|src| src.as_bytes::<BecauseExclusive>().as_mut()).into()),
27922795
}
@@ -3500,7 +3503,7 @@ pub unsafe trait FromBytes: FromZeros {
35003503
{
35013504
static_assert_dst_is_not_zst!(Self);
35023505
match Ptr::from_ref(source).try_cast_into_no_leftover::<_, BecauseImmutable>(None) {
3503-
Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_ref()),
3506+
Ok(ptr) => Ok(ptr.transmute().as_ref()),
35043507
Err(err) => Err(err.map_src(|src| src.as_ref())),
35053508
}
35063509
}
@@ -3975,7 +3978,7 @@ pub unsafe trait FromBytes: FromZeros {
39753978
let source = Ptr::from_ref(source);
39763979
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
39773980
match maybe_slf {
3978-
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_ref()),
3981+
Ok(slf) => Ok(slf.transmute().as_ref()),
39793982
Err(err) => Err(err.map_src(|s| s.as_ref())),
39803983
}
39813984
}
@@ -4574,7 +4577,7 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
45744577
let (slf, prefix_suffix) = Ptr::from_ref(source)
45754578
.try_cast_into::<_, BecauseImmutable>(cast_type, meta)
45764579
.map_err(|err| err.map_src(|s| s.as_ref()))?;
4577-
Ok((slf.bikeshed_recall_valid().as_ref(), prefix_suffix.as_ref()))
4580+
Ok((slf.transmute().as_ref(), prefix_suffix.as_ref()))
45784581
}
45794582

45804583
/// Interprets the given affix of the given bytes as a `&mut Self` without
@@ -4586,15 +4589,18 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
45864589
/// If there are insufficient bytes, or if that affix of `source` is not
45874590
/// appropriately aligned, this returns `Err`.
45884591
#[inline(always)]
4589-
fn mut_from_prefix_suffix<T: FromBytes + KnownLayout + ?Sized>(
4592+
fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
45904593
source: &mut [u8],
45914594
meta: Option<T::PointerMetadata>,
45924595
cast_type: CastType,
45934596
) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> {
45944597
let (slf, prefix_suffix) = Ptr::from_mut(source)
45954598
.try_cast_into::<_, BecauseExclusive>(cast_type, meta)
45964599
.map_err(|err| err.map_src(|s| s.as_mut()))?;
4597-
Ok((slf.bikeshed_recall_valid().as_mut(), prefix_suffix.as_mut()))
4600+
Ok((
4601+
slf.transmute::<_, _, (pointer::transmute::BecauseRead, _), _>().as_mut(),
4602+
prefix_suffix.as_mut(),
4603+
))
45984604
}
45994605

46004606
/// 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: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,10 @@ mod _external {
172172
/// Methods for converting to and from `Ptr` and Rust's safe reference types.
173173
mod _conversions {
174174
use super::*;
175-
use crate::util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance};
175+
use crate::{
176+
pointer::transmute::{CastFrom, TransmuteFromAlignment, TransmuteFromPtr},
177+
util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance},
178+
};
176179

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

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

434489
impl<'a, V, I> Ptr<'a, V, I>
435490
where
@@ -650,6 +705,22 @@ mod _transitions {
650705
}
651706
}
652707

708+
impl<'a, V, I> Ptr<'a, V, I>
709+
where
710+
V: Validity,
711+
I: Invariants,
712+
I::Aliasing: Reference,
713+
{
714+
/// Forgets that `self` is an `Exclusive` pointer, downgrading it to a
715+
/// `Shared` pointer.
716+
#[doc(hidden)]
717+
#[must_use]
718+
#[inline]
719+
pub const fn forget_exclusive(self) -> Ptr<'a, V, I::WithAliasing<Shared>> {
720+
unsafe { self.assume_invariants() }
721+
}
722+
}
723+
653724
impl<'a, T, I> Ptr<'a, Initialized<T>, I>
654725
where
655726
T: ?Sized,
@@ -661,14 +732,11 @@ mod _transitions {
661732
#[inline]
662733
// TODO(#859): Reconsider the name of this method before making it
663734
// public.
664-
pub const fn bikeshed_recall_valid(self) -> Ptr<'a, Valid<T>, I>
735+
pub fn bikeshed_recall_valid(self) -> Ptr<'a, Valid<T>, I>
665736
where
666-
T: crate::FromBytes,
737+
T: crate::FromBytes + crate::IntoBytes,
667738
{
668-
// SAFETY: The bound `T: FromBytes` ensures that any initialized
669-
// sequence of bytes is bit-valid for `T`. `V = Initialized<T>`
670-
// ensures that all of the referent bytes are initialized.
671-
unsafe { self.assume_valid() }
739+
self.transmute().unify_invariants()
672740
}
673741

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

708839
/// Casts of the referent type.

0 commit comments

Comments
 (0)