Skip to content

Commit 5d99011

Browse files
committed
[pointer] Store validity in the referent type
Previously, validity was stored as part of the `I: Invariants` type parameter on a `Ptr<T, I>`. In this commit, we migrate the validity to being stored as the `V` in `Ptr<V, I>`; `I` now only stores aliasing and alignment. Bit validity is subtle, in that the pair of referent type and validity invariant define a set of bit patterns which may appear in the `Ptr`'s referent. By encoding the validity in the referent type instead of in the `I` parameter, we ensure that the validity of the referent is captured entirely by a single type parameter rather than by a pair of two separate type parameters. This makes future changes (e.g. #1359) easier to model. This idea was originally proposed in #1866 (comment) Makes progress on #1866 gherrit-pr-id: Ia73ca4e5dfb3b20f71ed72ffda24dd7450c427ba
1 parent 17e7e4d commit 5d99011

File tree

6 files changed

+275
-232
lines changed

6 files changed

+275
-232
lines changed

src/pointer/invariant.rs

+93-62
Original file line numberDiff line numberDiff line change
@@ -10,48 +10,42 @@
1010

1111
//! The parameterized invariants of a [`Ptr`][super::Ptr].
1212
//!
13-
//! Invariants are encoded as ([`Aliasing`], [`Alignment`], [`Validity`])
14-
//! triples implementing the [`Invariants`] trait.
13+
//! A `Ptr<V, I>` has the following invariants:
14+
//! - [`V: Validity`][validity-trait] encodes the bit validity of `Ptr`'s
15+
//! referent, which is of type [`V::Inner`][validity-inner]
16+
//! - [`I: Invariants`][invariants-trait], where
17+
//! [`I::Aliasing`][invariants-aliasing] and
18+
//! [`I::Alignment`][invariants-alignment] encode the `Ptr`'s aliasing and
19+
//! alignment invariants respectively
20+
//!
21+
//! [validity-trait]: Validity
22+
//! [validity-inner]: Validity::Inner
23+
//! [invariants-trait]: Invariants
24+
//! [invariants-aliasing]: Invariants::Aliasing
25+
//! [invariants-alignment]: Invariants::Alignment
26+
27+
use core::marker::PhantomData;
1528

16-
/// The invariants of a [`Ptr`][super::Ptr].
29+
/// The aliasing and alignment invariants of a [`Ptr`][super::Ptr].
1730
pub trait Invariants: Sealed {
1831
type Aliasing: Aliasing;
1932
type Alignment: Alignment;
20-
type Validity: Validity;
2133

2234
/// Invariants identical to `Self` except with a different aliasing
2335
/// invariant.
24-
type WithAliasing<A: Aliasing>: Invariants<
25-
Aliasing = A,
26-
Alignment = Self::Alignment,
27-
Validity = Self::Validity,
28-
>;
36+
type WithAliasing<A: Aliasing>: Invariants<Aliasing = A, Alignment = Self::Alignment>;
2937

3038
/// Invariants identical to `Self` except with a different alignment
3139
/// invariant.
32-
type WithAlignment<A: Alignment>: Invariants<
33-
Aliasing = Self::Aliasing,
34-
Alignment = A,
35-
Validity = Self::Validity,
36-
>;
37-
38-
/// Invariants identical to `Self` except with a different validity
39-
/// invariant.
40-
type WithValidity<V: Validity>: Invariants<
41-
Aliasing = Self::Aliasing,
42-
Alignment = Self::Alignment,
43-
Validity = V,
44-
>;
40+
type WithAlignment<A: Alignment>: Invariants<Aliasing = Self::Aliasing, Alignment = A>;
4541
}
4642

47-
impl<A: Aliasing, AA: Alignment, V: Validity> Invariants for (A, AA, V) {
43+
impl<A: Aliasing, AA: Alignment> Invariants for (A, AA) {
4844
type Aliasing = A;
4945
type Alignment = AA;
50-
type Validity = V;
5146

52-
type WithAliasing<AB: Aliasing> = (AB, AA, V);
53-
type WithAlignment<AB: Alignment> = (A, AB, V);
54-
type WithValidity<VB: Validity> = (A, AA, VB);
47+
type WithAliasing<AB: Aliasing> = (AB, AA);
48+
type WithAlignment<AB: Alignment> = (A, AB);
5549
}
5650

5751
/// The aliasing invariant of a [`Ptr`][super::Ptr].
@@ -79,7 +73,24 @@ pub trait Alignment: Sealed {
7973
}
8074

8175
/// The validity invariant of a [`Ptr`][super::Ptr].
76+
///
77+
/// A `V: Validity` defines both the referent type of a `Ptr<V>`
78+
/// ([`V::Inner`](Validity::Inner)) and the bit validity of the referent value.
79+
/// Bit validity specifies a set, `S`, of possible values which may exist at the
80+
/// `Ptr`'s referent. Code operating on a `Ptr` may assume that bit validity
81+
/// holds - namely, that it will only observe referent values in `S`. It must
82+
/// also uphold bit validity - namely, it must only write values in `S` to the
83+
/// referent.
84+
///
85+
/// The specific definition of `S` for a given validity type (i.e., `V:
86+
/// Validity`) is documented on that type.
87+
///
88+
/// The available validities are [`Uninit`], [`AsInitialized`], [`Initialized`],
89+
/// and [`Valid`].
8290
pub trait Validity: Sealed {
91+
type Inner: ?Sized;
92+
type WithInner<T: ?Sized>: Validity<Inner = T>;
93+
8394
#[doc(hidden)]
8495
type MappedTo<M: ValidityMapping>: Validity;
8596
}
@@ -98,8 +109,19 @@ pub enum Unknown {}
98109
impl Alignment for Unknown {
99110
type MappedTo<M: AlignmentMapping> = M::FromUnknown;
100111
}
101-
impl Validity for Unknown {
102-
type MappedTo<M: ValidityMapping> = M::FromUnknown;
112+
113+
/// A validity which permits arbitrary bytes - including uninitialized bytes -
114+
/// at any byte offset.
115+
///
116+
/// The referent of a `Ptr<Uninit<T>>` may contain arbitrary bytes - including
117+
/// uninitialized bytes - at any byte offset.
118+
pub struct Uninit<T: ?Sized = ()>(PhantomData<T>);
119+
120+
impl<T: ?Sized> Validity for Uninit<T> {
121+
type Inner = T;
122+
type WithInner<U: ?Sized> = Uninit<U>;
123+
124+
type MappedTo<M: ValidityMapping> = M::FromUninit<T>;
103125
}
104126

105127
/// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`.
@@ -138,11 +160,11 @@ impl Alignment for Aligned {
138160

139161
/// The byte ranges initialized in `T` are also initialized in the referent.
140162
///
141-
/// Formally: uninitialized bytes may only be present in `Ptr<T>`'s referent
142-
/// where they are guaranteed to be present in `T`. This is a dynamic property:
143-
/// if, at a particular byte offset, a valid enum discriminant is set, the
144-
/// subsequent bytes may only have uninitialized bytes as specificed by the
145-
/// corresponding enum.
163+
/// Formally: uninitialized bytes may only be present in
164+
/// `Ptr<AsInitialized<T>>`'s referent where they are guaranteed to be present
165+
/// in `T`. This is a dynamic property: if, at a particular byte offset, a valid
166+
/// enum discriminant is set, the subsequent bytes may only have uninitialized
167+
/// bytes as specificed by the corresponding enum.
146168
///
147169
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`, in
148170
/// the range `[0, len)`:
@@ -162,22 +184,28 @@ impl Alignment for Aligned {
162184
/// variant's bit validity (although note that the variant may contain another
163185
/// enum type, in which case the same rules apply depending on the state of
164186
/// its discriminant, and so on recursively).
165-
pub enum AsInitialized {}
166-
impl Validity for AsInitialized {
167-
type MappedTo<M: ValidityMapping> = M::FromAsInitialized;
187+
pub struct AsInitialized<T: ?Sized = ()>(PhantomData<T>);
188+
impl<T: ?Sized> Validity for AsInitialized<T> {
189+
type Inner = T;
190+
type WithInner<U: ?Sized> = AsInitialized<U>;
191+
type MappedTo<M: ValidityMapping> = M::FromAsInitialized<T>;
168192
}
169193

170194
/// The byte ranges in the referent are fully initialized. In other words, if
171195
/// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`.
172-
pub enum Initialized {}
173-
impl Validity for Initialized {
174-
type MappedTo<M: ValidityMapping> = M::FromInitialized;
196+
pub struct Initialized<T: ?Sized = ()>(PhantomData<T>);
197+
impl<T: ?Sized> Validity for Initialized<T> {
198+
type Inner = T;
199+
type WithInner<U: ?Sized> = Initialized<U>;
200+
type MappedTo<M: ValidityMapping> = M::FromInitialized<T>;
175201
}
176202

177203
/// The referent is bit-valid for `T`.
178-
pub enum Valid {}
179-
impl Validity for Valid {
180-
type MappedTo<M: ValidityMapping> = M::FromValid;
204+
pub struct Valid<T: ?Sized = ()>(PhantomData<T>);
205+
impl<T: ?Sized> Validity for Valid<T> {
206+
type Inner = T;
207+
type WithInner<U: ?Sized> = Valid<U>;
208+
type MappedTo<M: ValidityMapping> = M::FromValid<T>;
181209
}
182210

183211
/// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations.
@@ -224,16 +252,18 @@ mod sealed {
224252

225253
impl Sealed for Unknown {}
226254

255+
impl<T: ?Sized> Sealed for Uninit<T> {}
256+
227257
impl Sealed for Shared {}
228258
impl Sealed for Exclusive {}
229259

230260
impl Sealed for Aligned {}
231261

232-
impl Sealed for AsInitialized {}
233-
impl Sealed for Initialized {}
234-
impl Sealed for Valid {}
262+
impl<T: ?Sized> Sealed for AsInitialized<T> {}
263+
impl<T: ?Sized> Sealed for Initialized<T> {}
264+
impl<T: ?Sized> Sealed for Valid<T> {}
235265

236-
impl<A: Sealed, AA: Sealed, V: Sealed> Sealed for (A, AA, V) {}
266+
impl<A: Sealed, AA: Sealed> Sealed for (A, AA) {}
237267
}
238268

239269
pub use mapping::*;
@@ -268,10 +298,10 @@ mod mapping {
268298
/// Mappings are used by [`Ptr`](crate::Ptr) conversion methods to preserve
269299
/// or modify invariants as required by each method's semantics.
270300
pub trait ValidityMapping {
271-
type FromUnknown: Validity;
272-
type FromAsInitialized: Validity;
273-
type FromInitialized: Validity;
274-
type FromValid: Validity;
301+
type FromUninit<T: ?Sized>: Validity;
302+
type FromAsInitialized<T: ?Sized>: Validity;
303+
type FromInitialized<T: ?Sized>: Validity;
304+
type FromValid<T: ?Sized>: Validity;
275305
}
276306

277307
/// The application of the [`AlignmentMapping`] `M` to the [`Alignment`] `A`.
@@ -280,7 +310,8 @@ mod mapping {
280310

281311
/// The application of the [`ValidityMapping`] `M` to the [`Validity`] `A`.
282312
#[allow(type_alias_bounds)]
283-
pub type MappedValidity<V: Validity, M: ValidityMapping> = V::MappedTo<M>;
313+
pub type MappedValidity<V: Validity, U: ?Sized, M: ValidityMapping> =
314+
<V::MappedTo<M> as Validity>::WithInner<U>;
284315

285316
impl<FromUnknown: Alignment, FromAligned: Alignment> AlignmentMapping
286317
for ((Unknown, FromUnknown), (Shared, FromAligned))
@@ -290,28 +321,28 @@ mod mapping {
290321
}
291322

292323
impl<
293-
FromUnknown: Validity,
324+
FromUninit: Validity,
294325
FromAsInitialized: Validity,
295326
FromInitialized: Validity,
296327
FromValid: Validity,
297328
> ValidityMapping
298329
for (
299-
(Unknown, FromUnknown),
330+
(Unknown, FromUninit),
300331
(AsInitialized, FromAsInitialized),
301332
(Initialized, FromInitialized),
302333
(Valid, FromValid),
303334
)
304335
{
305-
type FromUnknown = FromUnknown;
306-
type FromAsInitialized = FromAsInitialized;
307-
type FromInitialized = FromInitialized;
308-
type FromValid = FromValid;
336+
type FromUninit<T: ?Sized> = FromUninit::WithInner<T>;
337+
type FromAsInitialized<T: ?Sized> = FromAsInitialized::WithInner<T>;
338+
type FromInitialized<T: ?Sized> = FromInitialized::WithInner<T>;
339+
type FromValid<T: ?Sized> = FromValid::WithInner<T>;
309340
}
310341

311342
impl<FromInitialized: Validity> ValidityMapping for (Initialized, FromInitialized) {
312-
type FromUnknown = Unknown;
313-
type FromAsInitialized = Unknown;
314-
type FromInitialized = FromInitialized;
315-
type FromValid = Unknown;
343+
type FromUninit<T: ?Sized> = Uninit<T>;
344+
type FromAsInitialized<T: ?Sized> = Uninit<T>;
345+
type FromInitialized<T: ?Sized> = FromInitialized::WithInner<T>;
346+
type FromValid<T: ?Sized> = Uninit<T>;
316347
}
317348
}

src/pointer/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ use crate::Unaligned;
2525
///
2626
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
2727
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unknown> =
28-
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
28+
Ptr<'a, invariant::Initialized<T>, (Aliasing, Alignment)>;
2929

3030
/// A semi-user-facing wrapper type representing a maybe-aligned reference, for
3131
/// use in [`TryFromBytes::is_bit_valid`].
3232
///
3333
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
3434
pub type MaybeAligned<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unknown> =
35-
Ptr<'a, T, (Aliasing, Alignment, invariant::Valid)>;
35+
Ptr<'a, invariant::Valid<T>, (Aliasing, Alignment)>;
3636

3737
// These methods are defined on the type alias, `MaybeAligned`, so as to bring
3838
// them to the forefront of the rendered rustdoc for that type alias.
@@ -77,10 +77,10 @@ where
7777
}
7878

7979
/// Checks if the referent is zeroed.
80-
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool
80+
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, invariant::Initialized<T>, I>) -> bool
8181
where
8282
T: crate::Immutable + crate::KnownLayout,
83-
I: invariant::Invariants<Validity = invariant::Initialized>,
83+
I: invariant::Invariants,
8484
I::Aliasing: invariant::Reference,
8585
{
8686
ptr.as_bytes::<BecauseImmutable>().as_ref().iter().all(|&byte| byte == 0)

0 commit comments

Comments
 (0)