Skip to content

Commit 85a12e2

Browse files
authored
[pointer] Simplify AliasingSafe, rename to Read (#1908)
`AliasingSafe` is really about whether a pointer permits unsynchronized reads - either because the referent contains no `UnsafeCell`s or because the aliasing mode is `Exclusive`. Previously, `AliasingSafe` was not named consistent with this meaning, and was a function of a *pair* of types rather than of a single type. This commit fixes both oversights. While we're here, we also add `Read` bounds in some places, allowing us to simplify many safety comments.
1 parent f80a65d commit 85a12e2

File tree

13 files changed

+138
-157
lines changed

13 files changed

+138
-157
lines changed

src/impls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,7 @@ mod simd {
950950
#[cfg(test)]
951951
mod tests {
952952
use super::*;
953+
use crate::pointer::invariant;
953954

954955
#[test]
955956
fn test_impls() {

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ use core::{
360360
slice,
361361
};
362362

363-
use crate::pointer::{invariant, BecauseExclusive};
363+
use crate::pointer::invariant::{self, BecauseExclusive};
364364

365365
#[cfg(any(feature = "alloc", test))]
366366
extern crate alloc;
@@ -372,7 +372,7 @@ use core::alloc::Layout;
372372

373373
// Used by `TryFromBytes::is_bit_valid`.
374374
#[doc(hidden)]
375-
pub use crate::pointer::{BecauseImmutable, Maybe, MaybeAligned, Ptr};
375+
pub use crate::pointer::{invariant::BecauseImmutable, Maybe, MaybeAligned, Ptr};
376376
// Used by `KnownLayout`.
377377
#[doc(hidden)]
378378
pub use crate::layout::*;

src/pointer/invariant.rs

Lines changed: 37 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -170,78 +170,41 @@ impl Validity for Initialized {}
170170
pub enum Valid {}
171171
impl Validity for Valid {}
172172

173-
pub mod aliasing_safety {
174-
use super::*;
175-
use crate::Immutable;
176-
177-
/// Pointer conversions which do not violate aliasing.
178-
///
179-
/// `U: AliasingSafe<T, A, R>` implies that a pointer conversion from `T` to
180-
/// `U` does not violate the aliasing invariant, `A`. This can be because
181-
/// `A` is [`Exclusive`] or because neither `T` nor `U` permit interior
182-
/// mutability.
183-
///
184-
/// # Safety
185-
///
186-
/// `U: AliasingSafe<T, A, R>` if either of the following conditions holds:
187-
/// - `A` is [`Exclusive`]
188-
/// - `T` and `U` both implement [`Immutable`]
189-
#[doc(hidden)]
190-
pub unsafe trait AliasingSafe<T: ?Sized, A: Aliasing, R: AliasingSafeReason> {}
191-
192-
#[doc(hidden)]
193-
pub trait AliasingSafeReason: sealed::Sealed {}
194-
impl<R: AliasingSafeReason> AliasingSafeReason for (R,) {}
195-
196-
/// The conversion is safe because only one live `Ptr` or reference may exist to
197-
/// the referent bytes at a time.
198-
#[derive(Copy, Clone, Debug)]
199-
#[doc(hidden)]
200-
pub enum BecauseExclusive {}
201-
impl AliasingSafeReason for BecauseExclusive {}
202-
203-
/// The conversion is safe because no live `Ptr`s or references permit mutation.
204-
#[derive(Copy, Clone, Debug)]
205-
#[doc(hidden)]
206-
pub enum BecauseImmutable {}
207-
impl AliasingSafeReason for BecauseImmutable {}
208-
209-
/// SAFETY: `T: AliasingSafe<Exclusive, BecauseExclusive>` because for all
210-
/// `Ptr<'a, T, I>` such that `I::Aliasing = Exclusive`, there cannot exist
211-
/// other live references to the memory referenced by `Ptr`.
212-
unsafe impl<T: ?Sized, U: ?Sized> AliasingSafe<T, Exclusive, BecauseExclusive> for U {}
213-
214-
/// SAFETY: `U: AliasingSafe<T, A, BecauseNoCell>` because for all `Ptr<'a, T,
215-
/// I>` and `Ptr<'a, U, I>` such that `I::Aliasing = A`, all live references and
216-
/// live `Ptr`s agree, by invariant on `Immutable`, that the referenced bytes
217-
/// contain no `UnsafeCell`s, and thus do not permit mutation except via
218-
/// exclusive aliasing.
219-
unsafe impl<A, T: ?Sized, U: ?Sized> AliasingSafe<T, A, BecauseImmutable> for U
220-
where
221-
A: Aliasing,
222-
T: Immutable,
223-
U: Immutable,
224-
{
225-
}
226-
227-
/// This ensures that `U: AliasingSafe<T, A>` implies `T: AliasingSafe<U, A>` in
228-
/// a manner legible to rustc, which in turn means we can write simpler bounds in
229-
/// some places.
230-
///
231-
/// SAFETY: Per `U: AliasingSafe<T, A, R>`, either:
232-
/// - `A` is `Exclusive`
233-
/// - `T` and `U` both implement `Immutable`
234-
///
235-
/// Neither property depends on which of `T` and `U` are in the `Self` position
236-
/// vs the first type parameter position.
237-
unsafe impl<A, T: ?Sized, U: ?Sized, R> AliasingSafe<U, A, (R,)> for T
238-
where
239-
A: Aliasing,
240-
R: AliasingSafeReason,
241-
U: AliasingSafe<T, A, R>,
242-
{
243-
}
244-
}
173+
/// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations.
174+
///
175+
/// `T: Read<A, R>` implies that a pointer to `T` with aliasing `A` permits
176+
/// unsynchronized read oeprations. This can be because `A` is [`Exclusive`] or
177+
/// because `T` does not permit interior mutation.
178+
///
179+
/// # Safety
180+
///
181+
/// `T: Read<A, R>` if either of the following conditions holds:
182+
/// - `A` is [`Exclusive`]
183+
/// - `T` implements [`Immutable`](crate::Immutable)
184+
///
185+
/// As a consequence, if `T: Read<A, R>`, then any `Ptr<T, (A, ...)>` is
186+
/// permitted to perform unsynchronized reads from its referent.
187+
pub trait Read<A: Aliasing, R: ReadReason> {}
188+
189+
impl<A: Reference, T: ?Sized + crate::Immutable> Read<A, BecauseImmutable> for T {}
190+
impl<T: ?Sized> Read<Exclusive, BecauseExclusive> for T {}
191+
192+
/// Used to disambiguate [`Read`] impls.
193+
pub trait ReadReason: Sealed {}
194+
195+
/// Unsynchronized reads are permitted because only one live [`Ptr`](crate::Ptr)
196+
/// or reference may exist to the referent bytes at a time.
197+
#[derive(Copy, Clone, Debug)]
198+
#[doc(hidden)]
199+
pub enum BecauseExclusive {}
200+
impl ReadReason for BecauseExclusive {}
201+
202+
/// Unsynchronized reads are permitted because no live [`Ptr`](crate::Ptr)s or
203+
/// references permit interior mutation.
204+
#[derive(Copy, Clone, Debug)]
205+
#[doc(hidden)]
206+
pub enum BecauseImmutable {}
207+
impl ReadReason for BecauseImmutable {}
245208

246209
use sealed::Sealed;
247210
mod sealed {
@@ -262,7 +225,6 @@ mod sealed {
262225

263226
impl<A: Sealed, AA: Sealed, V: Sealed> Sealed for (A, AA, V) {}
264227

265-
impl Sealed for super::aliasing_safety::BecauseExclusive {}
266-
impl Sealed for super::aliasing_safety::BecauseImmutable {}
267-
impl<S: Sealed> Sealed for (S,) {}
228+
impl Sealed for BecauseImmutable {}
229+
impl Sealed for BecauseExclusive {}
268230
}

src/pointer/mod.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ pub mod invariant;
1414
mod ptr;
1515

1616
#[doc(hidden)]
17-
pub use invariant::aliasing_safety::{
18-
AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable,
19-
};
17+
pub use invariant::{BecauseExclusive, BecauseImmutable, Read, ReadReason};
2018
#[doc(hidden)]
2119
pub use ptr::Ptr;
2220

@@ -50,11 +48,11 @@ where
5048
pub fn read_unaligned<R>(self) -> T
5149
where
5250
T: Copy,
53-
R: AliasingSafeReason,
54-
T: AliasingSafe<T, Aliasing, R>,
51+
R: invariant::ReadReason,
52+
T: invariant::Read<Aliasing, R>,
5553
{
5654
// SAFETY: By invariant on `MaybeAligned`, `raw` contains
57-
// validly-initialized data for `T`. By `T: AliasingSafe`, we are
55+
// validly-initialized data for `T`. By `T: Read<Aliasing>`, we are
5856
// permitted to perform a read of `self`'s referent.
5957
unsafe { self.as_inner().read_unaligned() }
6058
}

src/pointer/ptr.rs

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ mod _conversions {
377377
// byte ranges. Since `p` and the returned pointer address the
378378
// same byte range, they refer to `UnsafeCell`s at the same byte
379379
// ranges.
380-
let c = unsafe { self.cast_unsized(|p| T::cast_into_inner(p)) };
380+
let c = unsafe { self.cast_unsized_unchecked(|p| T::cast_into_inner(p)) };
381381
// SAFETY: By invariant on `TransparentWrapper`, since `self`
382382
// satisfies the alignment invariant `I::Alignment`, `c` (of type
383383
// `T::Inner`) satisfies the given "applied" alignment invariant.
@@ -411,7 +411,7 @@ mod _conversions {
411411
// `UnsafeCell`s at the same locations as `p`.
412412
let ptr = unsafe {
413413
#[allow(clippy::as_conversions)]
414-
self.cast_unsized(|p: *mut T| p as *mut crate::Unalign<T>)
414+
self.cast_unsized_unchecked(|p: *mut T| p as *mut crate::Unalign<T>)
415415
};
416416
// SAFETY: `Unalign<T>` promises to have the same bit validity as
417417
// `T`.
@@ -651,13 +651,14 @@ mod _transitions {
651651
/// On error, unsafe code may rely on this method's returned
652652
/// `ValidityError` containing `self`.
653653
#[inline]
654-
pub(crate) fn try_into_valid(
654+
pub(crate) fn try_into_valid<R>(
655655
mut self,
656656
) -> Result<Ptr<'a, T, I::WithValidity<Valid>>, ValidityError<Self, T>>
657657
where
658-
T: TryFromBytes,
658+
T: TryFromBytes + Read<I::Aliasing, R>,
659659
I::Aliasing: Reference,
660660
I: Invariants<Validity = Initialized>,
661+
R: crate::pointer::ReadReason,
661662
{
662663
// This call may panic. If that happens, it doesn't cause any soundness
663664
// issues, as we have not generated any invalid state which we need to
@@ -685,14 +686,19 @@ mod _transitions {
685686
/// Casts of the referent type.
686687
mod _casts {
687688
use super::*;
688-
use crate::{pointer::invariant::aliasing_safety::*, CastError, SizeError};
689+
use crate::{CastError, SizeError};
689690

690691
impl<'a, T, I> Ptr<'a, T, I>
691692
where
692693
T: 'a + ?Sized,
693694
I: Invariants,
694695
{
695-
/// Casts to a different (unsized) target type.
696+
/// Casts to a different (unsized) target type without checking interior
697+
/// mutability.
698+
///
699+
/// Callers should prefer [`cast_unsized`] where possible.
700+
///
701+
/// [`cast_unsized`]: Ptr::cast_unsized
696702
///
697703
/// # Safety
698704
///
@@ -705,7 +711,7 @@ mod _casts {
705711
/// exist in `*p`
706712
#[doc(hidden)]
707713
#[inline]
708-
pub unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
714+
pub unsafe fn cast_unsized_unchecked<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
709715
self,
710716
cast: F,
711717
) -> Ptr<'a, U, (I::Aliasing, Any, Any)> {
@@ -767,6 +773,34 @@ mod _casts {
767773
// 8. `ptr`, trivially, conforms to the validity invariant of `Any`.
768774
unsafe { Ptr::new(ptr) }
769775
}
776+
777+
/// Casts to a different (unsized) target type.
778+
///
779+
/// # Safety
780+
///
781+
/// The caller promises that `u = cast(p)` is a pointer cast with the
782+
/// following properties:
783+
/// - `u` addresses a subset of the bytes addressed by `p`
784+
/// - `u` has the same provenance as `p`
785+
#[doc(hidden)]
786+
#[inline]
787+
pub unsafe fn cast_unsized<U, F, R, S>(self, cast: F) -> Ptr<'a, U, (I::Aliasing, Any, Any)>
788+
where
789+
T: Read<I::Aliasing, R>,
790+
U: 'a + ?Sized + Read<I::Aliasing, S>,
791+
R: ReadReason,
792+
S: ReadReason,
793+
F: FnOnce(*mut T) -> *mut U,
794+
{
795+
// SAFETY: Because `T` and `U` both implement `Read<I::Aliasing, _>`,
796+
// either:
797+
// - `I::Aliasing` is `Exclusive`
798+
// - `T` and `U` are both `Immutable`, in which case they trivially
799+
// contain `UnsafeCell`s at identical locations
800+
//
801+
// The caller promises all other safety preconditions.
802+
unsafe { self.cast_unsized_unchecked(cast) }
803+
}
770804
}
771805

772806
impl<'a, T, I> Ptr<'a, T, I>
@@ -778,8 +812,9 @@ mod _casts {
778812
#[allow(clippy::wrong_self_convention)]
779813
pub(crate) fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)>
780814
where
781-
[u8]: AliasingSafe<T, I::Aliasing, R>,
782-
R: AliasingSafeReason,
815+
R: ReadReason,
816+
T: Read<I::Aliasing, R>,
817+
I::Aliasing: Reference,
783818
{
784819
let bytes = match T::size_of_val_raw(self.as_inner().as_non_null()) {
785820
Some(bytes) => bytes,
@@ -796,10 +831,6 @@ mod _casts {
796831
// pointer's address, and `bytes` is the length of `p`, so the
797832
// returned pointer addresses the same bytes as `p`
798833
// - `slice_from_raw_parts_mut` and `.cast` both preserve provenance
799-
// - Because `[u8]: AliasingSafe<T, I::Aliasing, _>`, either:
800-
// - `I::Aliasing` is `Exclusive`
801-
// - `T` and `[u8]` are both `Immutable`, in which case they
802-
// trivially contain `UnsafeCell`s at identical locations
803834
let ptr: Ptr<'a, [u8], _> = unsafe {
804835
self.cast_unsized(|p: *mut T| {
805836
#[allow(clippy::as_conversions)]
@@ -878,9 +909,9 @@ mod _casts {
878909
CastError<Self, U>,
879910
>
880911
where
881-
R: AliasingSafeReason,
912+
R: ReadReason,
882913
I::Aliasing: Reference,
883-
U: 'a + ?Sized + KnownLayout + AliasingSafe<[u8], I::Aliasing, R>,
914+
U: 'a + ?Sized + KnownLayout + Read<I::Aliasing, R>,
884915
{
885916
let (inner, remainder) =
886917
self.as_inner().try_cast_into(cast_type, meta).map_err(|err| {
@@ -893,12 +924,13 @@ mod _casts {
893924
})?;
894925

895926
// SAFETY:
896-
// 0. Since `U: AliasingSafe<[u8], I::Aliasing, _>`, either:
927+
// 0. Since `U: Read<I::Aliasing, _>`, either:
897928
// - `I::Aliasing` is `Exclusive`, in which case both `src` and
898929
// `ptr` conform to `Exclusive`
899-
// - `I::Aliasing` is `Shared` or `Any` and both `U` and `[u8]`
900-
// are `Immutable`. In this case, neither pointer permits
901-
// mutation, and so `Shared` aliasing is satisfied.
930+
// - `I::Aliasing` is `Shared` or `Any` and `U` is `Immutable`
931+
// (we already know that `[u8]: Immutable`). In this case,
932+
// neither `U` nor `[u8]` permit mutation, and so `Shared`
933+
// aliasing is satisfied.
902934
// 1. `ptr` conforms to the alignment invariant of `Aligned` because
903935
// it is derived from `try_cast_into`, which promises that the
904936
// object described by `target` is validly aligned for `U`.
@@ -939,8 +971,8 @@ mod _casts {
939971
) -> Result<Ptr<'a, U, (I::Aliasing, Aligned, Initialized)>, CastError<Self, U>>
940972
where
941973
I::Aliasing: Reference,
942-
U: 'a + ?Sized + KnownLayout + AliasingSafe<[u8], I::Aliasing, R>,
943-
R: AliasingSafeReason,
974+
U: 'a + ?Sized + KnownLayout + Read<I::Aliasing, R>,
975+
R: ReadReason,
944976
{
945977
match self.try_cast_into(CastType::Prefix, meta) {
946978
Ok((slf, remainder)) => {
@@ -984,11 +1016,8 @@ mod _casts {
9841016
#[must_use]
9851017
#[inline(always)]
9861018
pub fn get_mut(self) -> Ptr<'a, T, I> {
987-
// SAFETY:
988-
// - The closure uses an `as` cast, which preserves address range
989-
// and provenance.
990-
// - We require `I: Invariants<Aliasing = Exclusive>`, so we are not
991-
// required to uphold `UnsafeCell` equality.
1019+
// SAFETY: The closure uses an `as` cast, which preserves address
1020+
// range and provenance.
9921021
#[allow(clippy::as_conversions)]
9931022
let ptr = unsafe { self.cast_unsized(|p| p as *mut T) };
9941023

@@ -1034,7 +1063,8 @@ mod _project {
10341063
///
10351064
/// # Safety
10361065
///
1037-
/// `project` has the same safety preconditions as `cast_unsized`.
1066+
/// `project` has the same safety preconditions as
1067+
/// `cast_unsized_unchecked`.
10381068
#[doc(hidden)]
10391069
#[inline]
10401070
pub unsafe fn project<U: 'a + ?Sized>(
@@ -1046,8 +1076,8 @@ mod _project {
10461076
// `Initialized` pointer, we could remove this method entirely.
10471077

10481078
// SAFETY: This method has the same safety preconditions as
1049-
// `cast_unsized`.
1050-
let ptr = unsafe { self.cast_unsized(projector) };
1079+
// `cast_unsized_unchecked`.
1080+
let ptr = unsafe { self.cast_unsized_unchecked(projector) };
10511081

10521082
// SAFETY: If all of the bytes of `self` are initialized (as
10531083
// promised by `I: Invariants<Validity = Initialized>`), then any

0 commit comments

Comments
 (0)