Skip to content

Commit 2480958

Browse files
committed
[pointer] Support generic invariant mapping
This commit adds a framework which supports encoding in the type system any `I -> I` mapping where `I` is any `Invariant` type. This permits us to make `cast_unsized`'s return value smarter, and as a result, allows us to remove a lot of `unsafe` code. Makes progress on #1122
1 parent 9e69c15 commit 2480958

File tree

7 files changed

+141
-92
lines changed

7 files changed

+141
-92
lines changed

src/impls.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,15 +290,6 @@ safety_comment! {
290290
/// because `NonZeroXxx` and `xxx` have the same size. [1] Neither `r`
291291
/// nor `t` refer to any `UnsafeCell`s because neither `NonZeroXxx` [2]
292292
/// nor `xxx` do.
293-
/// - Since the closure takes a `&xxx` argument, given a `Maybe<'a,
294-
/// NonZeroXxx>` which satisfies the preconditions of
295-
/// `TryFromBytes::<NonZeroXxx>::is_bit_valid`, it must be guaranteed
296-
/// that the memory referenced by that `MabyeValid` always contains a
297-
/// valid `xxx`. Since `NonZeroXxx`'s bytes are always initialized [1],
298-
/// `is_bit_valid`'s precondition requires that the same is true of its
299-
/// argument. Since `xxx`'s only bit validity invariant is that its
300-
/// bytes must be initialized, this memory is guaranteed to contain a
301-
/// valid `xxx`.
302293
/// - The impl must only return `true` for its argument if the original
303294
/// `Maybe<NonZeroXxx>` refers to a valid `NonZeroXxx`. The only
304295
/// `xxx` which is not also a valid `NonZeroXxx` is 0. [1]

src/pointer/invariant.rs

Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,22 @@ pub trait Aliasing: Sealed {
6565
/// Aliasing>::Variance<'a, T>` to inherit this variance.
6666
#[doc(hidden)]
6767
type Variance<'a, T: 'a + ?Sized>;
68+
69+
#[doc(hidden)]
70+
type MappedTo<M: AliasingMapping>: Aliasing;
6871
}
6972

7073
/// The alignment invariant of a [`Ptr`][super::Ptr].
71-
pub trait Alignment: Sealed {}
74+
pub trait Alignment: Sealed {
75+
#[doc(hidden)]
76+
type MappedTo<M: AlignmentMapping>: Alignment;
77+
}
7278

7379
/// The validity invariant of a [`Ptr`][super::Ptr].
74-
pub trait Validity: Sealed {}
80+
pub trait Validity: Sealed {
81+
#[doc(hidden)]
82+
type MappedTo<M: ValidityMapping>: Validity;
83+
}
7584

7685
/// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`].
7786
///
@@ -96,9 +105,15 @@ impl Aliasing for Any {
96105
//
97106
// [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance
98107
type Variance<'a, T: 'a + ?Sized> = fn(&'a T) -> &'a T;
108+
109+
type MappedTo<M: AliasingMapping> = M::FromAny;
110+
}
111+
impl Alignment for Any {
112+
type MappedTo<M: AlignmentMapping> = M::FromAny;
113+
}
114+
impl Validity for Any {
115+
type MappedTo<M: ValidityMapping> = M::FromAny;
99116
}
100-
impl Alignment for Any {}
101-
impl Validity for Any {}
102117

103118
/// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`.
104119
///
@@ -112,6 +127,7 @@ pub enum Shared {}
112127
impl Aliasing for Shared {
113128
const IS_EXCLUSIVE: bool = false;
114129
type Variance<'a, T: 'a + ?Sized> = &'a T;
130+
type MappedTo<M: AliasingMapping> = M::FromShared;
115131
}
116132
impl Reference for Shared {}
117133

@@ -124,51 +140,60 @@ pub enum Exclusive {}
124140
impl Aliasing for Exclusive {
125141
const IS_EXCLUSIVE: bool = true;
126142
type Variance<'a, T: 'a + ?Sized> = &'a mut T;
143+
type MappedTo<M: AliasingMapping> = M::FromExclusive;
127144
}
128145
impl Reference for Exclusive {}
129146

130-
/// The referent is aligned: for `Ptr<T>`, the referent's address is a multiple
131-
/// of the `T`'s alignment.
147+
/// The referent is aligned: for `Ptr<T>`, the referent's address is a
148+
/// multiple of the `T`'s alignment.
132149
pub enum Aligned {}
133-
impl Alignment for Aligned {}
150+
impl Alignment for Aligned {
151+
type MappedTo<M: AlignmentMapping> = M::FromAligned;
152+
}
134153

135154
/// The byte ranges initialized in `T` are also initialized in the referent.
136155
///
137156
/// Formally: uninitialized bytes may only be present in `Ptr<T>`'s referent
138-
/// where they are guaranteed to be present in `T`. This is a dynamic
139-
/// property: if, at a particular byte offset, a valid enum discriminant is
140-
/// set, the subsequent bytes may only have uninitialized bytes as
141-
/// specificed by the corresponding enum.
157+
/// where they are guaranteed to be present in `T`. This is a dynamic property:
158+
/// if, at a particular byte offset, a valid enum discriminant is set, the
159+
/// subsequent bytes may only have uninitialized bytes as specificed by the
160+
/// corresponding enum.
142161
///
143-
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`,
144-
/// in the range `[0, len)`:
145-
/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in
146-
/// `t` is initialized, then the byte at offset `b` within `*ptr` must be
162+
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`, in
163+
/// the range `[0, len)`:
164+
/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in `t`
165+
/// is initialized, then the byte at offset `b` within `*ptr` must be
147166
/// initialized.
148-
/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S`
149-
/// be the subset of valid instances of `T` of length `len` which contain
150-
/// `c` in the offset range `[0, b)`. If, in any instance of `t: T` in
151-
/// `S`, the byte at offset `b` in `t` is initialized, then the byte at
152-
/// offset `b` in `*ptr` must be initialized.
167+
/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S` be
168+
/// the subset of valid instances of `T` of length `len` which contain `c` in
169+
/// the offset range `[0, b)`. If, in any instance of `t: T` in `S`, the byte
170+
/// at offset `b` in `t` is initialized, then the byte at offset `b` in `*ptr`
171+
/// must be initialized.
153172
///
154-
/// Pragmatically, this means that if `*ptr` is guaranteed to contain an
155-
/// enum type at a particular offset, and the enum discriminant stored in
156-
/// `*ptr` corresponds to a valid variant of that enum type, then it is
157-
/// guaranteed that the appropriate bytes of `*ptr` are initialized as
158-
/// defined by that variant's bit validity (although note that the variant
159-
/// may contain another enum type, in which case the same rules apply
160-
/// depending on the state of its discriminant, and so on recursively).
173+
/// Pragmatically, this means that if `*ptr` is guaranteed to contain an enum
174+
/// type at a particular offset, and the enum discriminant stored in `*ptr`
175+
/// corresponds to a valid variant of that enum type, then it is guaranteed
176+
/// that the appropriate bytes of `*ptr` are initialized as defined by that
177+
/// variant's bit validity (although note that the variant may contain another
178+
/// enum type, in which case the same rules apply depending on the state of
179+
/// its discriminant, and so on recursively).
161180
pub enum AsInitialized {}
162-
impl Validity for AsInitialized {}
181+
impl Validity for AsInitialized {
182+
type MappedTo<M: ValidityMapping> = M::FromAsInitialized;
183+
}
163184

164185
/// The byte ranges in the referent are fully initialized. In other words, if
165186
/// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`.
166187
pub enum Initialized {}
167-
impl Validity for Initialized {}
188+
impl Validity for Initialized {
189+
type MappedTo<M: ValidityMapping> = M::FromInitialized;
190+
}
168191

169192
/// The referent is bit-valid for `T`.
170193
pub enum Valid {}
171-
impl Validity for Valid {}
194+
impl Validity for Valid {
195+
type MappedTo<M: ValidityMapping> = M::FromValid;
196+
}
172197

173198
pub mod aliasing_safety {
174199
use super::*;
@@ -266,3 +291,76 @@ mod sealed {
266291
impl Sealed for super::aliasing_safety::BecauseImmutable {}
267292
impl<S: Sealed> Sealed for (S,) {}
268293
}
294+
295+
pub use mapping::*;
296+
mod mapping {
297+
use super::*;
298+
299+
pub trait AliasingMapping {
300+
type FromAny: Aliasing;
301+
type FromShared: Aliasing;
302+
type FromExclusive: Aliasing;
303+
}
304+
305+
pub trait AlignmentMapping {
306+
type FromAny: Alignment;
307+
type FromAligned: Alignment;
308+
}
309+
310+
pub trait ValidityMapping {
311+
type FromAny: Validity;
312+
type FromAsInitialized: Validity;
313+
type FromInitialized: Validity;
314+
type FromValid: Validity;
315+
}
316+
317+
#[allow(type_alias_bounds)]
318+
pub type MappedAliasing<I: Aliasing, M: AliasingMapping> = I::MappedTo<M>;
319+
320+
#[allow(type_alias_bounds)]
321+
pub type MappedAlignment<I: Alignment, M: AlignmentMapping> = I::MappedTo<M>;
322+
323+
#[allow(type_alias_bounds)]
324+
pub type MappedValidity<I: Validity, M: ValidityMapping> = I::MappedTo<M>;
325+
326+
impl<FromAny: Aliasing, FromShared: Aliasing, FromExclusive: Aliasing> AliasingMapping
327+
for ((Any, FromAny), (Shared, FromShared), (Exclusive, FromExclusive))
328+
{
329+
type FromAny = FromAny;
330+
type FromShared = FromShared;
331+
type FromExclusive = FromExclusive;
332+
}
333+
334+
impl<FromAny: Alignment, FromAligned: Alignment> AlignmentMapping
335+
for ((Any, FromAny), (Shared, FromAligned))
336+
{
337+
type FromAny = FromAny;
338+
type FromAligned = FromAligned;
339+
}
340+
341+
impl<
342+
FromAny: Validity,
343+
FromAsInitialized: Validity,
344+
FromInitialized: Validity,
345+
FromValid: Validity,
346+
> ValidityMapping
347+
for (
348+
(Any, FromAny),
349+
(AsInitialized, FromAsInitialized),
350+
(Initialized, FromInitialized),
351+
(Valid, FromValid),
352+
)
353+
{
354+
type FromAny = FromAny;
355+
type FromAsInitialized = FromAsInitialized;
356+
type FromInitialized = FromInitialized;
357+
type FromValid = FromValid;
358+
}
359+
360+
impl<FromInitialized: Validity> ValidityMapping for (Initialized, FromInitialized) {
361+
type FromAny = Any;
362+
type FromAsInitialized = Any;
363+
type FromInitialized = FromInitialized;
364+
type FromValid = Any;
365+
}
366+
}

src/pointer/ptr.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,8 @@ mod _casts {
708708
pub unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
709709
self,
710710
cast: F,
711-
) -> Ptr<'a, U, (I::Aliasing, Any, Any)> {
711+
) -> Ptr<'a, U, (I::Aliasing, Any, MappedValidity<I::Validity, (Initialized, Initialized)>)>
712+
{
712713
let ptr = cast(self.as_inner().as_non_null().as_ptr());
713714

714715
// SAFETY: Caller promises that `cast` returns a pointer whose
@@ -764,7 +765,13 @@ mod _casts {
764765
// not happen.
765766
// 7. `ptr`, trivially, conforms to the alignment invariant of
766767
// `Any`.
767-
// 8. `ptr`, trivially, conforms to the validity invariant of `Any`.
768+
// 8. If `I::Validity = Any`, `AsInitialized`, or `Valid`, the
769+
// output validity invariant is `Any`. `ptr` trivially conforms
770+
// to this invariant. If `I::Validity = Initialized`, the output
771+
// validity invariant is `Initialized`. Regardless of what subset
772+
// of `self`'s referent is referred to by `ptr`, if all of
773+
// `self`'s referent is initialized, then the same holds of
774+
// `ptr`'s referent.
768775
unsafe { Ptr::new(ptr) }
769776
}
770777
}
@@ -807,14 +814,7 @@ mod _casts {
807814
})
808815
};
809816

810-
let ptr = ptr.bikeshed_recall_aligned();
811-
812-
// SAFETY: `ptr`'s referent begins as `Initialized`, denoting that
813-
// all bytes of the referent are initialized bytes. The referent
814-
// type is then casted to `[u8]`, whose only validity invariant is
815-
// that its bytes are initialized. This validity invariant is
816-
// satisfied by the `Initialized` invariant on the starting `ptr`.
817-
unsafe { ptr.assume_validity::<Valid>() }
817+
ptr.bikeshed_recall_aligned().bikeshed_recall_valid()
818818
}
819819
}
820820

@@ -1047,12 +1047,7 @@ mod _project {
10471047

10481048
// SAFETY: This method has the same safety preconditions as
10491049
// `cast_unsized`.
1050-
let ptr = unsafe { self.cast_unsized(projector) };
1051-
1052-
// SAFETY: If all of the bytes of `self` are initialized (as
1053-
// promised by `I: Invariants<Validity = Initialized>`), then any
1054-
// subset of those bytes are also all initialized.
1055-
unsafe { ptr.assume_validity::<Initialized>() }
1050+
unsafe { self.cast_unsized(projector) }
10561051
}
10571052
}
10581053

src/util/macro_util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ macro_rules! assert_align_gt_eq {
424424
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
425425
#[macro_export]
426426
macro_rules! assert_size_eq {
427-
($t:ident, $u: ident) => {{
427+
($t:ident, $u:ident) => {{
428428
// The comments here should be read in the context of this macro's
429429
// invocations in `transmute_ref!` and `transmute_mut!`.
430430
if false {

src/util/macros.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ macro_rules! safety_comment {
5252
/// referred to by `t`.
5353
/// - `r` refers to an object with `UnsafeCell`s at the same byte ranges as
5454
/// the object referred to by `t`.
55-
/// - If the provided closure takes a `&$repr` argument, then given a `Ptr<'a,
56-
/// $ty>` which satisfies the preconditions of
57-
/// `TryFromBytes::<$ty>::is_bit_valid`, it must be guaranteed that the
58-
/// memory referenced by that `Ptr` always contains a valid `$repr`.
5955
/// - The impl of `is_bit_valid` must only return `true` for its argument
6056
/// `Ptr<$repr>` if the original `Ptr<$ty>` refers to a valid `$ty`.
6157
macro_rules! unsafe_impl {
@@ -153,9 +149,7 @@ macro_rules! unsafe_impl {
153149
#[allow(clippy::as_conversions)]
154150
let candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) };
155151

156-
// SAFETY: The caller has promised that the referenced memory region
157-
// will contain a valid `$repr`.
158-
let $candidate = unsafe { candidate.assume_validity::<crate::pointer::invariant::Valid>() };
152+
let $candidate = candidate.bikeshed_recall_valid();
159153
$is_bit_valid
160154
}
161155
};
@@ -176,12 +170,6 @@ macro_rules! unsafe_impl {
176170
// `UnsafeCell`s at the same byte ranges as the source type.
177171
#[allow(clippy::as_conversions)]
178172
let $candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) };
179-
180-
// Restore the invariant that the referent bytes are initialized.
181-
// SAFETY: The above cast does not uninitialize any referent bytes;
182-
// they remain initialized.
183-
let $candidate = unsafe { $candidate.assume_validity::<crate::pointer::invariant::Initialized>() };
184-
185173
$is_bit_valid
186174
}
187175
};

zerocopy-derive/src/enum.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,6 @@ pub(crate) fn derive_is_bit_valid(
265265
}
266266
)
267267
};
268-
// SAFETY: `cast_unsized` removes the initialization
269-
// invariant from `p`, so we re-assert that all of the bytes
270-
// are initialized.
271-
let variant = unsafe { variant.assume_initialized() };
272268
<
273269
#variant_struct_ident #ty_generics as #trait_path
274270
>::is_bit_valid(variant)
@@ -325,10 +321,6 @@ pub(crate) fn derive_is_bit_valid(
325321
p as *mut ___ZerocopyTagPrimitive
326322
})
327323
};
328-
// SAFETY: `tag_ptr` is casted from `candidate`, whose referent
329-
// is `Initialized`. Since we have not written uninitialized
330-
// bytes into the referent, `tag_ptr` is also `Initialized`.
331-
let tag_ptr = unsafe { tag_ptr.assume_initialized() };
332324
tag_ptr.bikeshed_recall_valid().read_unaligned::<::zerocopy::BecauseImmutable>()
333325
};
334326

@@ -347,9 +339,6 @@ pub(crate) fn derive_is_bit_valid(
347339
p as *mut ___ZerocopyRawEnum #ty_generics
348340
})
349341
};
350-
// SAFETY: `cast_unsized` removes the initialization invariant from
351-
// `p`, so we re-assert that all of the bytes are initialized.
352-
let raw_enum = unsafe { raw_enum.assume_initialized() };
353342
// SAFETY:
354343
// - This projection returns a subfield of `this` using
355344
// `addr_of_mut!`.

0 commit comments

Comments
 (0)