@@ -198,6 +198,199 @@ safety_comment! {
198
198
assert_unaligned!( mem:: MaybeUninit <( ) >, MaybeUninit <u8 >) ;
199
199
}
200
200
201
+ /// A value which might or might not constitute a valid instance of `T`.
202
+ ///
203
+ /// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
204
+ /// as `T`. Unlike `T`, it may contain any bit pattern, except that
205
+ /// uninitialized bytes may only appear in `MaybeValid<T>` at byte offsets where
206
+ /// they may appear in `T`. This is a dynamic property: if, at a particular byte
207
+ /// offset, a valid enum discriminant is set, the subsequent bytes may only have
208
+ /// uninitialized bytes as specified by the corresponding enum variant.
209
+ ///
210
+ /// Formally, given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
211
+ /// size_of_val(m))`:
212
+ /// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
213
+ /// initialized, then the byte at offset `b` within `m` is guaranteed to be
214
+ /// initialized.
215
+ /// - Let `c` be the contents of the byte range `[0, b)` in `m`. Let `TT` be the
216
+ /// subset of valid instances of `T` which contain `c` in the offset range
217
+ /// `[0, b)`. If, for all instances of `t: T` in `TT`, the byte at offset `b`
218
+ /// in `t` is initialized, then the byte at offset `b` in `m` is guaranteed to
219
+ /// be initialized.
220
+ ///
221
+ /// Pragmatically, this means that if `m` is guaranteed to contain an enum
222
+ /// type at a particular offset, and the enum discriminant stored in `m`
223
+ /// corresponds to a valid variant of that enum type, then it is guaranteed
224
+ /// that the appropriate bytes of `m` are initialized as defined by that
225
+ /// variant's bit validity (although note that the variant may contain another
226
+ /// enum type, in which case the same rules apply depending on the state of
227
+ /// its discriminant, and so on recursively).
228
+ ///
229
+ /// # Safety
230
+ ///
231
+ /// Unsafe code may assume that an instance of `MaybeValid` satisfies the
232
+ /// constraints described above. Unsafe code may produce a `MaybeValid` or
233
+ /// modify the bytes of an existing `MaybeValid` so long as these constraints
234
+ /// are upheld. It is unsound to produce a `MaybeValid` which fails to uphold
235
+ /// these constraints.
236
+ #[ repr( transparent) ]
237
+ pub struct MaybeValid < T : ?Sized + KnownLayout > {
238
+ inner : MaybeUninit < T > ,
239
+ }
240
+
241
+ safety_comment ! {
242
+ /// SAFETY:
243
+ /// - `AsBytes`: `MaybeValid` requires that, if a byte in `T` is always
244
+ /// initialized, the equivalent byte in `MaybeValid<T>` must be
245
+ /// initialized. `T: AsBytes` implies that all bytes in `T` must always be
246
+ /// initialized, and so all bytes in `MaybeValid<T>` must always be
247
+ /// initialized, and so `MaybeValid<T>` satisfies `AsBytes`.
248
+ /// - `Unaligned`: `MaybeValid<T>` has the same alignment as `T`.
249
+ /// - `KnownLayout`: Since `MaybeUninit<T>` is a `repr(transparent)` wrapper
250
+ /// around `T::MaybeUninit`:
251
+ /// - They have the same prefix size, alignment, and trailing slice
252
+ /// element size
253
+ /// - It is valid to perform an `as` cast in either direction, and this
254
+ /// operation preserves referent size
255
+ ///
256
+ /// TODO(#5): Implement `FromZeroes` and `FromBytes` for `MaybeValid<T>` and
257
+ /// `MaybeValid<[T]>`.
258
+ unsafe_impl!( T : ?Sized + KnownLayout + AsBytes => AsBytes for MaybeValid <T >) ;
259
+ unsafe_impl!( T : ?Sized + KnownLayout + Unaligned => Unaligned for MaybeValid <T >) ;
260
+ unsafe_impl_known_layout!( T : ?Sized + KnownLayout => #[ repr( MaybeUninit <T >) ] MaybeValid <T >) ;
261
+ }
262
+
263
+ impl < T : KnownLayout < MaybeUninit = mem:: MaybeUninit < T > > > Default for MaybeValid < T > {
264
+ fn default ( ) -> MaybeValid < T > {
265
+ // SAFETY: All of the bytes of `inner` are initialized to 0, and so the
266
+ // safety invariant on `MaybeValid` is upheld.
267
+ MaybeValid { inner : MaybeUninit :: zeroed ( ) }
268
+ }
269
+ }
270
+
271
+ impl < T : KnownLayout + ?Sized > MaybeValid < T > {
272
+ /// Converts this `&MaybeValid<T>` to a `&T`.
273
+ ///
274
+ /// # Safety
275
+ ///
276
+ /// `self` must contain a valid `T`.
277
+ pub unsafe fn assume_valid_ref ( & self ) -> & T {
278
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
279
+ // Since `Self` is `repr(transparent)`, it has the same layout as
280
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
281
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
282
+ // `T`.
283
+ unsafe { self . inner . assume_init_ref ( ) }
284
+ }
285
+
286
+ /// Converts this `&mut MaybeValid<T>` to a `&mut T`.
287
+ ///
288
+ /// # Safety
289
+ ///
290
+ /// `self` must contain a valid `T`.
291
+ pub unsafe fn assume_valid_mut ( & mut self ) -> & mut T {
292
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
293
+ // Since `Self` is `repr(transparent)`, it has the same layout as
294
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
295
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
296
+ // `T`.
297
+ unsafe { self . inner . assume_init_mut ( ) }
298
+ }
299
+
300
+ /// Gets a view of this `&T` as a `&MaybeValid<T>`.
301
+ ///
302
+ /// There is no mutable equivalent to this function, as producing a `&mut
303
+ /// MaybeValid<T>` from a `&mut T` would allow safe code to write invalid
304
+ /// values which would be accessible through `&mut T`.
305
+ pub fn from_ref ( r : & T ) -> & MaybeValid < T > {
306
+ let m: * const MaybeUninit < T > = MaybeUninit :: from_ref ( r) ;
307
+ #[ allow( clippy:: as_conversions) ]
308
+ let ptr = m as * const MaybeValid < T > ;
309
+ // SAFETY: Since `Self` is `repr(transparent)`, it has the same layout
310
+ // as `MaybeUninit<T>`, so the size and alignment here are valid.
311
+ //
312
+ // `MaybeValid<T>`'s bit validity constraints are weaker than those of
313
+ // `T`, so this is guaranteed not to produce an invalid `MaybeValid<T>`.
314
+ // If it were possible to write a different value for `MaybeValid<T>`
315
+ // through the returned reference, it could result in an invalid value
316
+ // being exposed via the `&T`. Luckily, the only way for mutation to
317
+ // happen is if `T` contains an `UnsafeCell` and the caller uses it to
318
+ // perform interior mutation. Importantly, `T` containing an
319
+ // `UnsafeCell` does not permit interior mutation through
320
+ // `MaybeValid<T>`, so it doesn't permit writing uninitialized or
321
+ // otherwise invalid values which would be visible through the original
322
+ // `&T`.
323
+ unsafe { & * ptr }
324
+ }
325
+ }
326
+
327
+ impl < T : KnownLayout < MaybeUninit = mem:: MaybeUninit < T > > > MaybeValid < T > {
328
+ /// Converts this `MaybeValid<T>` to a `T`.
329
+ ///
330
+ /// # Safety
331
+ ///
332
+ /// `self` must contain a valid `T`.
333
+ pub const unsafe fn assume_valid ( self ) -> T {
334
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
335
+ // Since `Self` is `repr(transparent)`, it has the same layout as
336
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
337
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
338
+ // `T`.
339
+ unsafe { self . inner . assume_init ( ) }
340
+ }
341
+ }
342
+
343
+ impl < T : KnownLayout < MaybeUninit = mem:: MaybeUninit < T > > > MaybeValid < [ T ] > {
344
+ /// Converts a `MaybeValid<[T]>` to a `[MaybeValid<T>]`.
345
+ ///
346
+ /// `MaybeValid<T>` has the same layout as `T`, so these layouts are
347
+ /// equivalent.
348
+ pub const fn as_slice_of_maybe_valids ( & self ) -> & [ MaybeValid < T > ] {
349
+ let inner: & [ <T as KnownLayout >:: MaybeUninit ] = & self . inner . inner ;
350
+ let inner_ptr: * const [ <T as KnownLayout >:: MaybeUninit ] = inner;
351
+ // Note: this Clippy warning is only emitted on our MSRV (1.61), but not
352
+ // on later versions of Clippy. Thus, we consider it spurious.
353
+ #[ allow( clippy:: as_conversions) ]
354
+ let ret_ptr = inner_ptr as * const [ MaybeValid < T > ] ;
355
+ // SAFETY: Since `inner` is a `&[MaybeUninit<T>]`, and `MaybeValid<T>`
356
+ // is a `repr(transparent)` struct around `MaybeUninit<T>`, `inner` has
357
+ // the same layout as `&[MaybeValid<T>]`.
358
+ unsafe { & * ret_ptr }
359
+ }
360
+ }
361
+
362
+ impl < const N : usize , T : KnownLayout > MaybeValid < [ T ; N ] > {
363
+ /// Converts a `MaybeValid<[T; N]>` to a `MaybeValid<[T]>`.
364
+ // TODO(#64): Make this `const` once our MSRV is >= 1.64.0 (when
365
+ // `slice_from_raw_parts` was stabilized as `const`).
366
+ pub fn as_slice ( & self ) -> & MaybeValid < [ T ] > {
367
+ let base: * const MaybeValid < [ T ; N ] > = self ;
368
+ let slice_of_t: * const [ T ] = ptr:: slice_from_raw_parts ( base. cast :: < T > ( ) , N ) ;
369
+ // Note: this Clippy warning is only emitted on our MSRV (1.61), but not
370
+ // on later versions of Clippy. Thus, we consider it spurious.
371
+ #[ allow( clippy:: as_conversions) ]
372
+ let mv_of_slice = slice_of_t as * const MaybeValid < [ T ] > ;
373
+ // SAFETY: `MaybeValid<T>` is a `repr(transparent)` wrapper around
374
+ // `MaybeUninit<T>`, which in turn has the same layout as `T`. Thus, the
375
+ // trailing slices of `[T]` and of `MaybeValid<[T]>` both have element
376
+ // type `T`. Since the number of elements is preserved during an `as`
377
+ // cast of slice/DST pointers, the resulting `*const MaybeValid<[T]>`
378
+ // has the same number of elements - and thus the same length - as the
379
+ // original `*const [T]`.
380
+ //
381
+ // Thanks to their layouts, `MaybeValid<[T; N]>` and `MaybeValid<[T]>`
382
+ // have the same alignment, so `mv_of_slice` is guaranteed to be
383
+ // aligned.
384
+ unsafe { & * mv_of_slice }
385
+ }
386
+ }
387
+
388
+ impl < T : ?Sized + KnownLayout > Debug for MaybeValid < T > {
389
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
390
+ f. pad ( core:: any:: type_name :: < Self > ( ) )
391
+ }
392
+ }
393
+
201
394
/// A type with no alignment requirement.
202
395
///
203
396
/// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`
0 commit comments