Skip to content

Commit 3a43dfd

Browse files
committed
[pointer][WIP] Transmute
gherrit-pr-id: Iad14813bc6d933312bc8d7a1ddcf1aafc7126938
1 parent daf3a21 commit 3a43dfd

File tree

12 files changed

+873
-736
lines changed

12 files changed

+873
-736
lines changed

src/impls.rs

Lines changed: 200 additions & 50 deletions
Large diffs are not rendered by default.

src/lib.rs

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,17 @@ pub unsafe trait KnownLayout {
805805
// resulting size would not fit in a `usize`.
806806
meta.size_for_metadata(Self::LAYOUT)
807807
}
808+
809+
#[doc(hidden)]
810+
#[must_use]
811+
#[inline(always)]
812+
fn cast_from_raw<P: KnownLayout<PointerMetadata = Self::PointerMetadata> + ?Sized>(
813+
ptr: NonNull<P>,
814+
) -> NonNull<Self> {
815+
let data = ptr.cast::<u8>();
816+
let meta = P::pointer_to_metadata(ptr.as_ptr());
817+
Self::raw_from_ptr_len(data, meta)
818+
}
808819
}
809820

810821
/// The metadata associated with a [`KnownLayout`] type.
@@ -2843,29 +2854,43 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
28432854
// We use `from_mut` despite not mutating via `c_ptr` so that we don't need
28442855
// to add a `T: Immutable` bound.
28452856
let c_ptr = Ptr::from_mut(&mut candidate);
2846-
let c_ptr = c_ptr.transparent_wrapper_into_inner();
28472857
// SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived from
28482858
// `candidate`, which the caller promises is entirely initialized. Since
28492859
// `candidate` is a `MaybeUninit`, it has no validity requirements, and so
2850-
// no values written to `c_ptr` can violate its validity. Since `c_ptr` has
2851-
// `Exclusive` aliasing, no mutations may happen except via `c_ptr` so long
2852-
// as it is live, so we don't need to worry about the fact that `c_ptr` may
2853-
// have more restricted validity than `candidate`.
2860+
// no values written to an `Initialized` `c_ptr` can violate its validity.
2861+
// Since `c_ptr` has `Exclusive` aliasing, no mutations may happen except
2862+
// via `c_ptr` so long as it is live, so we don't need to worry about the
2863+
// fact that `c_ptr` may have more restricted validity than `candidate`.
28542864
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
2865+
let c_ptr = c_ptr.transmute();
28552866

2867+
// Since we don't have `T: KnownLayout`, we hack around that by using
2868+
// `Wrapping<T>`, which implements `KnownLayout` even if `T` doesn't.
2869+
//
28562870
// This call may panic. If that happens, it doesn't cause any soundness
2857-
// issues, as we have not generated any invalid state which we need to
2858-
// fix before returning.
2871+
// issues, as we have not generated any invalid state which we need to fix
2872+
// before returning.
28592873
//
2860-
// Note that one panic or post-monomorphization error condition is
2861-
// calling `try_into_valid` (and thus `is_bit_valid`) with a shared
2862-
// pointer when `Self: !Immutable`. Since `Self: Immutable`, this panic
2863-
// condition will not happen.
2864-
if !T::is_bit_valid(c_ptr.forget_aligned()) {
2874+
// Note that one panic or post-monomorphization error condition is calling
2875+
// `try_into_valid` (and thus `is_bit_valid`) with a shared pointer when
2876+
// `Self: !Immutable`. Since `Self: Immutable`, this panic condition will
2877+
// not happen.
2878+
if !Wrapping::<T>::is_bit_valid(c_ptr.forget_aligned()) {
28652879
return Err(ValidityError::new(source).into());
28662880
}
28672881

2868-
// SAFETY: We just validated that `candidate` contains a valid `T`.
2882+
fn _assert_same_size_and_validity<T>()
2883+
where
2884+
Wrapping<T>: pointer::TransmuteFrom<T, invariant::Valid, invariant::Valid>,
2885+
T: pointer::TransmuteFrom<Wrapping<T>, invariant::Valid, invariant::Valid>,
2886+
{
2887+
}
2888+
2889+
_assert_same_size_and_validity::<T>();
2890+
2891+
// SAFETY: We just validated that `candidate` contains a valid
2892+
// `Wrapping<T>`, which has the same size and bit validity as `T`, as
2893+
// guaranteed by the preceding type assertion.
28692894
Ok(unsafe { candidate.assume_init() })
28702895
}
28712896

@@ -4258,7 +4283,9 @@ pub unsafe trait FromBytes: FromZeros {
42584283
let source = Ptr::from_mut(source);
42594284
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
42604285
match maybe_slf {
4261-
Ok(slf) => Ok(slf.bikeshed_recall_valid().as_mut()),
4286+
Ok(slf) => Ok(slf
4287+
.bikeshed_recall_valid::<(_, (_, (BecauseExclusive, BecauseExclusive)))>()
4288+
.as_mut()),
42624289
Err(err) => Err(err.map_src(|s| s.as_mut())),
42634290
}
42644291
}
@@ -4728,7 +4755,7 @@ fn ref_from_prefix_suffix<T: FromBytes + KnownLayout + Immutable + ?Sized>(
47284755
/// If there are insufficient bytes, or if that affix of `source` is not
47294756
/// appropriately aligned, this returns `Err`.
47304757
#[inline(always)]
4731-
fn mut_from_prefix_suffix<T: FromBytes + KnownLayout + ?Sized>(
4758+
fn mut_from_prefix_suffix<T: FromBytes + IntoBytes + KnownLayout + ?Sized>(
47324759
source: &mut [u8],
47334760
meta: Option<T::PointerMetadata>,
47344761
cast_type: CastType,

src/pointer/invariant.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ pub enum Initialized {}
178178
// required to uphold).
179179
unsafe impl Validity for Initialized {}
180180

181-
/// The referent of a `Ptr<T>` is bit-valid for `T`.
181+
/// The referent of a `Ptr<T>` is valid for `T`, upholding bit validity and any
182+
/// library safety invariants.
182183
pub enum Valid {}
183184
// SAFETY: `Valid`'s validity is well-defined for all `T: ?Sized`, and is not a
184185
// function of any property of `T` other than its bit validity.

src/pointer/mod.rs

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

1617
#[doc(hidden)]
17-
pub use invariant::{BecauseExclusive, BecauseImmutable, Read};
18+
pub(crate) use transmute::*;
1819
#[doc(hidden)]
19-
pub use ptr::Ptr;
20+
pub use {
21+
invariant::{BecauseExclusive, BecauseImmutable, Read},
22+
ptr::Ptr,
23+
};
2024

2125
use crate::Unaligned;
2226

src/pointer/ptr.rs

Lines changed: 65 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ use core::{
1212
ptr::NonNull,
1313
};
1414

15-
use super::{inner::PtrInner, invariant::*};
1615
use crate::{
17-
util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance},
16+
pointer::{
17+
inner::PtrInner,
18+
invariant::*,
19+
transmute::{MutationCompatible, TransmuteFromPtr},
20+
},
1821
AlignmentError, CastError, CastType, KnownLayout, SizeError, TryFromBytes, ValidityError,
1922
};
2023

@@ -383,53 +386,31 @@ mod _conversions {
383386
}
384387
}
385388

386-
/// `Ptr<'a, T = Wrapper<U>>` → `Ptr<'a, U>`
387-
impl<'a, T, I> Ptr<'a, T, I>
388-
where
389-
T: 'a + TransparentWrapper<I, UnsafeCellVariance = Covariant> + ?Sized,
390-
I: Invariants,
391-
{
392-
/// Converts `self` to a transparent wrapper type into a `Ptr` to the
393-
/// wrapped inner type.
394-
pub(crate) fn transparent_wrapper_into_inner(
395-
self,
396-
) -> Ptr<
397-
'a,
398-
T::Inner,
399-
(
400-
I::Aliasing,
401-
<T::AlignmentVariance as AlignmentVariance<I::Alignment>>::Applied,
402-
<T::ValidityVariance as ValidityVariance<I::Validity>>::Applied,
403-
),
404-
> {
405-
// SAFETY:
406-
// - By invariant on `TransparentWrapper::cast_into_inner`:
407-
// - This cast preserves address and referent size, and thus the
408-
// returned pointer addresses the same bytes as `p`
409-
// - This cast preserves provenance
410-
// - By invariant on `TransparentWrapper<UnsafeCellVariance =
411-
// Covariant>`, `T` and `T::Inner` have `UnsafeCell`s at the same
412-
// byte ranges. Since `p` and the returned pointer address the
413-
// same byte range, they refer to `UnsafeCell`s at the same byte
414-
// ranges.
415-
// - By invariant on `TransparentWrapper`, since `self` satisfies
416-
// the validity invariant `I::Validity`, the returned pointer (of
417-
// type `T::Inner`) satisfies the given "applied" validity
418-
// invariant.
419-
let ptr = unsafe { self.transmute_unchecked(|p| T::cast_into_inner(p)) };
420-
// SAFETY: By invariant on `TransparentWrapper`, since `self`
421-
// satisfies the alignment invariant `I::Alignment`, the returned
422-
// pointer (of type `T::Inner`) satisfies the given "applied"
423-
// alignment invariant.
424-
unsafe { ptr.assume_alignment() }
425-
}
426-
}
427-
428389
/// `Ptr<'a, T>` → `Ptr<'a, U>`
429390
impl<'a, T: ?Sized, I> Ptr<'a, T, I>
430391
where
431392
I: Invariants,
432393
{
394+
pub(crate) fn transmute<U, V, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
395+
where
396+
T: KnownLayout,
397+
V: Validity,
398+
U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, R>
399+
+ KnownLayout<PointerMetadata = T::PointerMetadata>
400+
+ ?Sized,
401+
{
402+
unsafe { self.transmute_unchecked(|t: NonNull<T>| U::cast_from_raw(t)) }
403+
}
404+
405+
pub(crate) fn transmute_sized<U, V, R>(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
406+
where
407+
T: Sized,
408+
V: Validity,
409+
U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V, R>,
410+
{
411+
unsafe { self.transmute_unchecked(cast!()) }
412+
}
413+
433414
/// Casts to a different (unsized) target type without checking interior
434415
/// mutability.
435416
///
@@ -460,14 +441,9 @@ mod _conversions {
460441
) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
461442
where
462443
V: Validity,
463-
F: FnOnce(*mut T) -> *mut U,
444+
F: FnOnce(NonNull<T>) -> NonNull<U>,
464445
{
465-
let ptr = cast(self.as_inner().as_non_null().as_ptr());
466-
467-
// SAFETY: Caller promises that `cast` returns a pointer whose
468-
// address is in the range of `self.as_inner().as_non_null()`'s referent. By
469-
// invariant, none of these addresses are null.
470-
let ptr = unsafe { NonNull::new_unchecked(ptr) };
446+
let ptr = cast(self.as_inner().as_non_null());
471447

472448
// SAFETY:
473449
//
@@ -552,7 +528,7 @@ mod _conversions {
552528
// validity of the other.
553529
let ptr = unsafe {
554530
#[allow(clippy::as_conversions)]
555-
self.transmute_unchecked(|p: *mut T| p as *mut crate::Unalign<T>)
531+
self.transmute_unchecked(NonNull::cast::<crate::Unalign<T>>)
556532
};
557533
ptr.bikeshed_recall_aligned()
558534
}
@@ -561,6 +537,8 @@ mod _conversions {
561537

562538
/// State transitions between invariants.
563539
mod _transitions {
540+
use crate::pointer::transmute::TryTransmuteFromPtr;
541+
564542
use super::*;
565543

566544
impl<'a, T, I> Ptr<'a, T, I>
@@ -819,14 +797,11 @@ mod _transitions {
819797
#[inline]
820798
// TODO(#859): Reconsider the name of this method before making it
821799
// public.
822-
pub fn bikeshed_recall_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>
800+
pub fn bikeshed_recall_valid<R>(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>
823801
where
824-
T: crate::FromBytes,
802+
T: crate::FromBytes + TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid, R>,
825803
I: Invariants<Validity = Initialized>,
826804
{
827-
// TODO(#1866): Fix this unsoundness.
828-
829-
// SAFETY: This is unsound!
830805
unsafe { self.assume_valid() }
831806
}
832807

@@ -843,24 +818,24 @@ mod _transitions {
843818
/// On error, unsafe code may rely on this method's returned
844819
/// `ValidityError` containing `self`.
845820
#[inline]
846-
pub(crate) fn try_into_valid<R>(
821+
pub(crate) fn try_into_valid<R, S>(
847822
mut self,
848823
) -> Result<Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>, ValidityError<Self, T>>
849824
where
850-
T: TryFromBytes + Read<I::Aliasing, R>,
825+
T: TryFromBytes
826+
+ Read<I::Aliasing, R>
827+
+ TryTransmuteFromPtr<T, I::Aliasing, I::Validity, Valid, S>,
851828
I::Aliasing: Reference,
852829
I: Invariants<Validity = Initialized>,
853830
{
854831
// This call may panic. If that happens, it doesn't cause any soundness
855832
// issues, as we have not generated any invalid state which we need to
856833
// fix before returning.
857834
if T::is_bit_valid(self.reborrow().forget_aligned()) {
858-
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
859-
// contains a bit-valid instance of `Self`.
835+
// TODO: Complete this safety comment.
860836
//
861-
// TODO(#1866): This is unsound! The returned `Ptr` may permit
862-
// writing referents which do not satisfy the `Initialized`
863-
// validity invariant of `self`.
837+
// If `T::is_bit_valid`, code may assume that `self` contains a
838+
// bit-valid instance of `Self`.
864839
Ok(unsafe { self.assume_valid() })
865840
} else {
866841
Err(ValidityError::new(self))
@@ -902,9 +877,10 @@ mod _casts {
902877
/// - `u` has the same provenance as `p`
903878
/// - If `I::Aliasing` is [`Shared`], `UnsafeCell`s in `*u` must exist
904879
/// at ranges identical to those at which `UnsafeCell`s exist in `*p`
880+
/// TODO: UnsafeCell compatibility
905881
#[doc(hidden)]
906882
#[inline]
907-
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(*mut T) -> *mut U>(
883+
pub(crate) unsafe fn cast_unsized_unchecked<U, F: FnOnce(NonNull<T>) -> NonNull<U>>(
908884
self,
909885
cast: F,
910886
) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)>
@@ -940,20 +916,28 @@ mod _casts {
940916
/// - `u` has the same provenance as `p`
941917
#[doc(hidden)]
942918
#[inline]
943-
pub unsafe fn cast_unsized<U, F, R, S>(
919+
pub unsafe fn cast_unsized<U, F, R>(
944920
self,
945921
cast: F,
946922
) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)>
947923
where
948-
T: Read<I::Aliasing, R>,
949-
U: 'a + ?Sized + Read<I::Aliasing, S> + CastableFrom<T, I::Validity, I::Validity>,
950-
F: FnOnce(*mut T) -> *mut U,
924+
T: MutationCompatible<U, I::Aliasing, I::Validity, I::Validity, R>,
925+
U: 'a + ?Sized + CastableFrom<T, I::Validity, I::Validity>,
926+
F: FnOnce(NonNull<T>) -> NonNull<U>,
951927
{
952-
// SAFETY: Because `T` and `U` both implement `Read<I::Aliasing, _>`,
953-
// either:
954-
// - `I::Aliasing` is `Exclusive`
955-
// - `T` and `U` are both `Immutable`, in which case they trivially
956-
// contain `UnsafeCell`s at identical locations
928+
// SAFETY: Because `T: MutationCompatible<U, I::Aliasing, R>`, one
929+
// of the following holds:
930+
// - `T: Read<I::Aliasing>` and `U: Read<I::Aliasing>`, in which
931+
// case one of the following holds:
932+
// - `I::Aliasing` is `Exclusive`
933+
// - `T` and `U` are both `Immutable`
934+
// - `T` and `U` contain `UnsafeCell`s at identical locations
935+
// TODO: This should also promise UnsafeCell compatibility
936+
//
937+
// In the first case, `I::Aliasing` is `Exclusive`, and in the
938+
// second and third case, `T` and `U` contain `UnsafeCell`s at
939+
// identical locations (in the second case, this is because `T` and
940+
// `U` contain no `UnsafeCell`s at all).
957941
//
958942
// The caller promises all other safety preconditions.
959943
unsafe { self.cast_unsized_unchecked(cast) }
@@ -988,9 +972,8 @@ mod _casts {
988972
// returned pointer addresses the same bytes as `p`
989973
// - `slice_from_raw_parts_mut` and `.cast` both preserve provenance
990974
let ptr: Ptr<'a, [u8], _> = unsafe {
991-
self.cast_unsized(|p: *mut T| {
992-
#[allow(clippy::as_conversions)]
993-
core::ptr::slice_from_raw_parts_mut(p.cast::<u8>(), bytes)
975+
self.cast_unsized(|p: NonNull<T>| {
976+
core::ptr::NonNull::slice_from_raw_parts(p.cast::<u8>(), bytes)
994977
})
995978
};
996979

@@ -1214,7 +1197,7 @@ mod _casts {
12141197
// inner type `T`. A consequence of this guarantee is that it is
12151198
// possible to convert between `T` and `UnsafeCell<T>`.
12161199
#[allow(clippy::as_conversions)]
1217-
let ptr = unsafe { self.transmute_unchecked(|p| p as *mut T) };
1200+
let ptr = unsafe { self.transmute_unchecked(cast!()) };
12181201

12191202
// SAFETY: `UnsafeCell<T>` has the same alignment as `T` [1],
12201203
// and so if `self` is guaranteed to be aligned, then so is the
@@ -1321,10 +1304,12 @@ mod tests {
13211304
};
13221305

13231306
// SAFETY: The bytes in `slf` must be initialized.
1324-
unsafe fn validate_and_get_len<T: ?Sized + KnownLayout + FromBytes>(
1307+
unsafe fn validate_and_get_len<
1308+
T: ?Sized + KnownLayout + FromBytes + Immutable,
1309+
>(
13251310
slf: Ptr<'_, T, (Shared, Aligned, Initialized)>,
13261311
) -> usize {
1327-
let t = slf.bikeshed_recall_valid().as_ref();
1312+
let t = slf.bikeshed_recall_valid::<BecauseImmutable>().as_ref();
13281313

13291314
let bytes = {
13301315
let len = mem::size_of_val(t);

0 commit comments

Comments
 (0)