Skip to content

Commit 231e3e3

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 062b71a commit 231e3e3

File tree

6 files changed

+119
-70
lines changed

6 files changed

+119
-70
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/ptr.rs

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,22 @@ pub mod invariant {
187187
/// Aliasing>::Variance<'a, T>` to inherit this variance.
188188
#[doc(hidden)]
189189
type Variance<'a, T: 'a + ?Sized>;
190+
191+
#[doc(hidden)]
192+
type MappedTo<M: AliasingMapping>: Aliasing;
190193
}
191194

192195
/// The alignment invariant of a [`Ptr`][super::Ptr].
193-
pub trait Alignment: Sealed {}
196+
pub trait Alignment: Sealed {
197+
#[doc(hidden)]
198+
type MappedTo<M: AlignmentMapping>: Alignment;
199+
}
194200

195201
/// The validity invariant of a [`Ptr`][super::Ptr].
196-
pub trait Validity: Sealed {}
202+
pub trait Validity: Sealed {
203+
#[doc(hidden)]
204+
type MappedTo<M: ValidityMapping>: Validity;
205+
}
197206

198207
/// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`].
199208
///
@@ -217,9 +226,15 @@ pub mod invariant {
217226
//
218227
// [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance
219228
type Variance<'a, T: 'a + ?Sized> = fn(&'a T) -> &'a T;
229+
230+
type MappedTo<M: AliasingMapping> = M::FromAny;
231+
}
232+
impl Alignment for Any {
233+
type MappedTo<M: AlignmentMapping> = M::FromAny;
234+
}
235+
impl Validity for Any {
236+
type MappedTo<M: ValidityMapping> = M::FromAny;
220237
}
221-
impl Alignment for Any {}
222-
impl Validity for Any {}
223238

224239
/// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`.
225240
///
@@ -234,6 +249,7 @@ pub mod invariant {
234249
impl Aliasing for Shared {
235250
const IS_EXCLUSIVE: bool = false;
236251
type Variance<'a, T: 'a + ?Sized> = &'a T;
252+
type MappedTo<M: AliasingMapping> = M::FromShared;
237253
}
238254
impl Reference for Shared {}
239255

@@ -246,13 +262,16 @@ pub mod invariant {
246262
impl Aliasing for Exclusive {
247263
const IS_EXCLUSIVE: bool = true;
248264
type Variance<'a, T: 'a + ?Sized> = &'a mut T;
265+
type MappedTo<M: AliasingMapping> = M::FromExclusive;
249266
}
250267
impl Reference for Exclusive {}
251268

252269
/// The referent is aligned: for `Ptr<T>`, the referent's address is a
253270
/// multiple of the `T`'s alignment.
254271
pub enum Aligned {}
255-
impl Alignment for Aligned {}
272+
impl Alignment for Aligned {
273+
type MappedTo<M: AlignmentMapping> = M::FromAligned;
274+
}
256275

257276
/// The byte ranges initialized in `T` are also initialized in the referent.
258277
///
@@ -281,17 +300,23 @@ pub mod invariant {
281300
/// may contain another enum type, in which case the same rules apply
282301
/// depending on the state of its discriminant, and so on recursively).
283302
pub enum AsInitialized {}
284-
impl Validity for AsInitialized {}
303+
impl Validity for AsInitialized {
304+
type MappedTo<M: ValidityMapping> = M::FromAsInitialized;
305+
}
285306

286307
/// The byte ranges in the referent are fully initialized. In other words,
287308
/// if the referent is `N` bytes long, then it contains a bit-valid `[u8;
288309
/// N]`.
289310
pub enum Initialized {}
290-
impl Validity for Initialized {}
311+
impl Validity for Initialized {
312+
type MappedTo<M: ValidityMapping> = M::FromInitialized;
313+
}
291314

292315
/// The referent is bit-valid for `T`.
293316
pub enum Valid {}
294-
impl Validity for Valid {}
317+
impl Validity for Valid {
318+
type MappedTo<M: ValidityMapping> = M::FromValid;
319+
}
295320

296321
use sealed::Sealed;
297322
mod sealed {
@@ -312,6 +337,79 @@ pub mod invariant {
312337

313338
impl<A: Sealed, AA: Sealed, V: Sealed> Sealed for (A, AA, V) {}
314339
}
340+
341+
pub use mapping::*;
342+
mod mapping {
343+
use super::*;
344+
345+
pub trait AliasingMapping {
346+
type FromAny: Aliasing;
347+
type FromShared: Aliasing;
348+
type FromExclusive: Aliasing;
349+
}
350+
351+
pub trait AlignmentMapping {
352+
type FromAny: Alignment;
353+
type FromAligned: Alignment;
354+
}
355+
356+
pub trait ValidityMapping {
357+
type FromAny: Validity;
358+
type FromAsInitialized: Validity;
359+
type FromInitialized: Validity;
360+
type FromValid: Validity;
361+
}
362+
363+
#[allow(type_alias_bounds)]
364+
pub type MappedAliasing<I: Aliasing, M: AliasingMapping> = I::MappedTo<M>;
365+
366+
#[allow(type_alias_bounds)]
367+
pub type MappedAlignment<I: Alignment, M: AlignmentMapping> = I::MappedTo<M>;
368+
369+
#[allow(type_alias_bounds)]
370+
pub type MappedValidity<I: Validity, M: ValidityMapping> = I::MappedTo<M>;
371+
372+
impl<FromAny: Aliasing, FromShared: Aliasing, FromExclusive: Aliasing> AliasingMapping
373+
for ((Any, FromAny), (Shared, FromShared), (Exclusive, FromExclusive))
374+
{
375+
type FromAny = FromAny;
376+
type FromShared = FromShared;
377+
type FromExclusive = FromExclusive;
378+
}
379+
380+
impl<FromAny: Alignment, FromAligned: Alignment> AlignmentMapping
381+
for ((Any, FromAny), (Shared, FromAligned))
382+
{
383+
type FromAny = FromAny;
384+
type FromAligned = FromAligned;
385+
}
386+
387+
impl<
388+
FromAny: Validity,
389+
FromAsInitialized: Validity,
390+
FromInitialized: Validity,
391+
FromValid: Validity,
392+
> ValidityMapping
393+
for (
394+
(Any, FromAny),
395+
(AsInitialized, FromAsInitialized),
396+
(Initialized, FromInitialized),
397+
(Valid, FromValid),
398+
)
399+
{
400+
type FromAny = FromAny;
401+
type FromAsInitialized = FromAsInitialized;
402+
type FromInitialized = FromInitialized;
403+
type FromValid = FromValid;
404+
}
405+
406+
impl<FromInitialized: Validity> ValidityMapping for (Initialized, FromInitialized) {
407+
type FromAny = Any;
408+
type FromAsInitialized = Any;
409+
type FromInitialized = FromInitialized;
410+
type FromValid = Any;
411+
}
412+
}
315413
}
316414

317415
pub(crate) use invariant::*;
@@ -911,7 +1009,8 @@ mod _casts {
9111009
pub unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
9121010
self,
9131011
cast: F,
914-
) -> Ptr<'a, U, (I::Aliasing, Any, Any)> {
1012+
) -> Ptr<'a, U, (I::Aliasing, Any, MappedValidity<I::Validity, (Initialized, Initialized)>)>
1013+
{
9151014
let ptr = cast(self.as_inner().as_non_null().as_ptr());
9161015

9171016
// SAFETY: Caller promises that `cast` returns a pointer whose
@@ -967,7 +1066,13 @@ mod _casts {
9671066
// not happen.
9681067
// 7. `ptr`, trivially, conforms to the alignment invariant of
9691068
// `Any`.
970-
// 8. `ptr`, trivially, conforms to the validity invariant of `Any`.
1069+
// 8. If `I::Validity = Any`, `AsInitialized`, or `Valid`, the
1070+
// output validity invariant is `Any`. `ptr` trivially conforms
1071+
// to this invariant. If `I::Validity = Initialized`, the output
1072+
// validity invariant is `Initialized`. Regardless of what subset
1073+
// of `self`'s referent is referred to by `ptr`, if all of
1074+
// `self`'s referent is initialized, then the same holds of
1075+
// `ptr`'s referent.
9711076
unsafe { Ptr::new(ptr) }
9721077
}
9731078
}
@@ -1010,14 +1115,7 @@ mod _casts {
10101115
})
10111116
};
10121117

1013-
let ptr = ptr.bikeshed_recall_aligned();
1014-
1015-
// SAFETY: `ptr`'s referent begins as `Initialized`, denoting that
1016-
// all bytes of the referent are initialized bytes. The referent
1017-
// type is then casted to `[u8]`, whose only validity invariant is
1018-
// that its bytes are initialized. This validity invariant is
1019-
// satisfied by the `Initialized` invariant on the starting `ptr`.
1020-
unsafe { ptr.assume_validity::<Valid>() }
1118+
ptr.bikeshed_recall_aligned().bikeshed_recall_valid()
10211119
}
10221120
}
10231121

@@ -1250,12 +1348,7 @@ mod _project {
12501348

12511349
// SAFETY: This method has the same safety preconditions as
12521350
// `cast_unsized`.
1253-
let ptr = unsafe { self.cast_unsized(projector) };
1254-
1255-
// SAFETY: If all of the bytes of `self` are initialized (as
1256-
// promised by `I: Invariants<Validity = Initialized>`), then any
1257-
// subset of those bytes are also all initialized.
1258-
unsafe { ptr.assume_validity::<Initialized>() }
1351+
unsafe { self.cast_unsized(projector) }
12591352
}
12601353
}
12611354

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)