@@ -1570,6 +1570,235 @@ safety_comment! {
1570
1570
assert_unaligned!( mem:: MaybeUninit <( ) >, MaybeUninit <u8 >) ;
1571
1571
}
1572
1572
1573
+ /// A value which might or might not constitute a valid instance of `T`.
1574
+ ///
1575
+ /// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
1576
+ /// as `T`. Unlike `T`, it may contain any bit pattern, except that
1577
+ /// uninitialized bytes may only appear in `MaybeValid<T>` at byte offsets where
1578
+ /// they may appear in `T`. This is a dynamic property: if, at a particular byte
1579
+ /// offset, a valid enum discriminant is set, the subsequent bytes may only have
1580
+ /// uninitialized bytes as specified by the corresponding enum variant.
1581
+ ///
1582
+ /// Formally, given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
1583
+ /// size_of_val(m))`:
1584
+ /// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
1585
+ /// initialized, then the byte at offset `b` within `m` is guaranteed to be
1586
+ /// initialized.
1587
+ /// - Let `c` be the contents of the byte range `[0, b)` in `m`. Let `TT` be the
1588
+ /// subset of valid instances of `T` which contain `c` in the offset range
1589
+ /// `[0, b)`. If, for all instances of `t: T` in `TT`, the byte at offset `b`
1590
+ /// in `t` is initialized, then the byte at offset `b` in `m` is guaranteed to
1591
+ /// be initialized.
1592
+ ///
1593
+ /// Pragmatically, this means that if `m` is guaranteed to contain an enum
1594
+ /// type at a particular offset, and the enum discriminant stored in `m`
1595
+ /// corresponds to a valid variant of that enum type, then it is guaranteed
1596
+ /// that the appropriate bytes of `m` are initialized as defined by that
1597
+ /// variant's bit validity (although note that the variant may contain another
1598
+ /// enum type, in which case the same rules apply depending on the state of
1599
+ /// its discriminant, and so on recursively).
1600
+ ///
1601
+ /// # Safety
1602
+ ///
1603
+ /// Unsafe code may assume that an instance of `MaybeValid` satisfies the
1604
+ /// constraints described above. Unsafe code may produce a `MaybeValid` or
1605
+ /// modify the bytes of an existing `MaybeValid` so long as these constraints
1606
+ /// are upheld. It is unsound to produce a `MaybeValid` which fails to uphold
1607
+ /// these constraints.
1608
+ #[ repr( transparent) ]
1609
+ pub struct MaybeValid < T : AsMaybeUninit + ?Sized > {
1610
+ inner : MaybeUninit < T > ,
1611
+ }
1612
+
1613
+ safety_comment ! {
1614
+ /// SAFETY:
1615
+ /// - `AsBytes`: `MaybeValid` requires that, if a byte in `T` is always
1616
+ /// initialized, the equivalent byte in `MaybeValid<T>` must be
1617
+ /// initialized. `T: AsBytes` implies that all bytes in `T` must always be
1618
+ /// initialized, and so all bytes in `MaybeValid<T>` must always be
1619
+ /// initialized, and so `MaybeValid<T>` satisfies `AsBytes`. `T: AsBytes`
1620
+ /// implies that `[T]: AsBytes`, so this holds is a sufficient bound for
1621
+ /// `MaybeValid<[T]>` too.
1622
+ /// - `Unaligned`: `MaybeValid<T>` and `MaybeValid<[T]>` have the same
1623
+ /// alignment as `T`.
1624
+ ///
1625
+ /// TODO(#5): Implement `FromZeroes` and `FromBytes` for `MaybeValid<T>` and
1626
+ /// `MaybeValid<[T]>`.
1627
+ unsafe_impl!( T : AsBytes => AsBytes for MaybeValid <T >) ;
1628
+ unsafe_impl!( T : AsBytes => AsBytes for MaybeValid <[ T ] >) ;
1629
+ unsafe_impl!( T : Unaligned => Unaligned for MaybeValid <T >) ;
1630
+ unsafe_impl!( T : Unaligned => Unaligned for MaybeValid <[ T ] >) ;
1631
+ }
1632
+
1633
+ // SAFETY: See safety comment on `MaybeUninit`.
1634
+ unsafe impl < T > AsMaybeUninit for MaybeValid < [ T ] > {
1635
+ // SAFETY:
1636
+ // - `MaybeUninit` has no bit validity requirements and `[U]` has the same
1637
+ // bit validity requirements as `U`, so `[MaybeUninit<T>]` has no bit
1638
+ // validity requirements. Thus, it is sound to write uninitialized bytes
1639
+ // at every offset.
1640
+ // - `MaybeValid<U>` is `repr(transparent)`, and thus has the same layout
1641
+ // and field offsets as its contained field of type `U::MaybeUninit`. In
1642
+ // this case, `U = [T]`, and so `U::MaybeUninit = [MaybeUninit<T>]`. Thus,
1643
+ // `MaybeValid<[T]>` has the same layout and field offsets as
1644
+ // `[MaybeUninit<T>]`, which is what we set `MaybeUninit` to here. Thus,
1645
+ // they trivially have the same alignment.
1646
+ // - By the same token, their raw pointer types are trivially `as` castable
1647
+ // and preserve size.
1648
+ // - By the same token, `[MaybeUninit<T>]` contains `UnsafeCell`s at the
1649
+ // same byte ranges as `MaybeValid<[T]>` does.
1650
+ type MaybeUninit = [ MaybeUninit < T > ] ;
1651
+
1652
+ // SAFETY: `as` preserves pointer address and provenance.
1653
+ #[ allow( clippy:: as_conversions) ]
1654
+ fn raw_from_maybe_uninit ( maybe_uninit : * const [ MaybeUninit < T > ] ) -> * const MaybeValid < [ T ] > {
1655
+ maybe_uninit as * const MaybeValid < [ T ] >
1656
+ }
1657
+
1658
+ // SAFETY: `as` preserves pointer address and provenance.
1659
+ #[ allow( clippy:: as_conversions) ]
1660
+ fn raw_mut_from_maybe_uninit ( maybe_uninit : * mut [ MaybeUninit < T > ] ) -> * mut MaybeValid < [ T ] > {
1661
+ maybe_uninit as * mut MaybeValid < [ T ] >
1662
+ }
1663
+
1664
+ // SAFETY: `as` preserves pointer address and provenance.
1665
+ #[ allow( clippy:: as_conversions) ]
1666
+ fn raw_maybe_uninit_from ( s : * const MaybeValid < [ T ] > ) -> * const [ MaybeUninit < T > ] {
1667
+ s as * const [ MaybeUninit < T > ]
1668
+ }
1669
+ }
1670
+
1671
+ impl < T > Default for MaybeValid < T > {
1672
+ fn default ( ) -> MaybeValid < T > {
1673
+ // SAFETY: All of the bytes of `inner` are initialized to 0, and so the
1674
+ // safety invariant on `MaybeValid` is upheld.
1675
+ MaybeValid { inner : MaybeUninit :: zeroed ( ) }
1676
+ }
1677
+ }
1678
+
1679
+ impl < T : AsMaybeUninit + ?Sized > MaybeValid < T > {
1680
+ /// Converts this `&MaybeValid<T>` to a `&T`.
1681
+ ///
1682
+ /// # Safety
1683
+ ///
1684
+ /// `self` must contain a valid `T`.
1685
+ pub unsafe fn assume_valid_ref ( & self ) -> & T {
1686
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
1687
+ // Since `Self` is `repr(transparent)`, it has the same layout as
1688
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
1689
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
1690
+ // `T`.
1691
+ unsafe { self . inner . assume_init_ref ( ) }
1692
+ }
1693
+
1694
+ /// Converts this `&mut MaybeValid<T>` to a `&mut T`.
1695
+ ///
1696
+ /// # Safety
1697
+ ///
1698
+ /// `self` must contain a valid `T`.
1699
+ pub unsafe fn assume_valid_mut ( & mut self ) -> & mut T {
1700
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
1701
+ // Since `Self` is `repr(transparent)`, it has the same layout as
1702
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
1703
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
1704
+ // `T`.
1705
+ unsafe { self . inner . assume_init_mut ( ) }
1706
+ }
1707
+
1708
+ /// Gets a view of this `&T` as a `&MaybeValid<T>`.
1709
+ ///
1710
+ /// There is no mutable equivalent to this function, as producing a `&mut
1711
+ /// MaybeValid<T>` from a `&mut T` would allow safe code to write invalid
1712
+ /// values which would be accessible through `&mut T`.
1713
+ pub fn from_ref ( r : & T ) -> & MaybeValid < T > {
1714
+ let m: * const MaybeUninit < T > = MaybeUninit :: from_ref ( r) ;
1715
+ #[ allow( clippy:: as_conversions) ]
1716
+ let ptr = m as * const MaybeValid < T > ;
1717
+ // SAFETY: Since `Self` is `repr(transparent)`, it has the same layout
1718
+ // as `MaybeUninit<T>`, so the size and alignment here are valid.
1719
+ //
1720
+ // `MaybeValid<T>`'s bit validity constraints are weaker than those of
1721
+ // `T`, so this is guaranteed not to produce an invalid `MaybeValid<T>`.
1722
+ // If it were possible to write a different value for `MaybeValid<T>`
1723
+ // through the returned reference, it could result in an invalid value
1724
+ // being exposed via the `&T`. Luckily, the only way for mutation to
1725
+ // happen is if `T` contains an `UnsafeCell` and the caller uses it to
1726
+ // perform interior mutation. Importantly, `T` containing an
1727
+ // `UnsafeCell` does not permit interior mutation through
1728
+ // `MaybeValid<T>`, so it doesn't permit writing uninitialized or
1729
+ // otherwise invalid values which would be visible through the original
1730
+ // `&T`.
1731
+ unsafe { & * ptr }
1732
+ }
1733
+ }
1734
+
1735
+ impl < T > MaybeValid < T > {
1736
+ /// Converts this `MaybeValid<T>` to a `T`.
1737
+ ///
1738
+ /// # Safety
1739
+ ///
1740
+ /// `self` must contain a valid `T`.
1741
+ pub const unsafe fn assume_valid ( self ) -> T {
1742
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
1743
+ // Since `Self` is `repr(transparent)`, it has the same layout as
1744
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
1745
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
1746
+ // `T`.
1747
+ unsafe { self . inner . assume_init ( ) }
1748
+ }
1749
+ }
1750
+
1751
+ impl < T > MaybeValid < [ T ] > {
1752
+ /// Converts a `MaybeValid<[T]>` to a `[MaybeValid<T>]`.
1753
+ ///
1754
+ /// `MaybeValid<T>` has the same layout as `T`, so these layouts are
1755
+ /// equivalent.
1756
+ pub const fn as_slice_of_maybe_valids ( & self ) -> & [ MaybeValid < T > ] {
1757
+ let inner: & [ <T as AsMaybeUninit >:: MaybeUninit ] = & self . inner . inner ;
1758
+ let inner_ptr: * const [ <T as AsMaybeUninit >:: MaybeUninit ] = inner;
1759
+ // Note: this Clippy warning is only emitted on our MSRV (1.61), but not
1760
+ // on later versions of Clippy. Thus, we consider it spurious.
1761
+ #[ allow( clippy:: as_conversions) ]
1762
+ let ret_ptr = inner_ptr as * const [ MaybeValid < T > ] ;
1763
+ // SAFETY: Since `inner` is a `&[MaybeUninit<T>]`, and `MaybeValid<T>`
1764
+ // is a `repr(transparent)` struct around `MaybeUninit<T>`, `inner` has
1765
+ // the same layout as `&[MaybeValid<T>]`.
1766
+ unsafe { & * ret_ptr }
1767
+ }
1768
+ }
1769
+
1770
+ impl < const N : usize , T > MaybeValid < [ T ; N ] > {
1771
+ /// Converts a `MaybeValid<[T; N]>` to a `MaybeValid<[T]>`.
1772
+ // TODO(#64): Make this `const` once our MSRV is >= 1.64.0 (when
1773
+ // `slice_from_raw_parts` was stabilized as `const`).
1774
+ pub fn as_slice ( & self ) -> & MaybeValid < [ T ] > {
1775
+ let base: * const MaybeValid < [ T ; N ] > = self ;
1776
+ let slice_of_t: * const [ T ] = ptr:: slice_from_raw_parts ( base. cast :: < T > ( ) , N ) ;
1777
+ // Note: this Clippy warning is only emitted on our MSRV (1.61), but not
1778
+ // on later versions of Clippy. Thus, we consider it spurious.
1779
+ #[ allow( clippy:: as_conversions) ]
1780
+ let mv_of_slice = slice_of_t as * const MaybeValid < [ T ] > ;
1781
+ // SAFETY: `MaybeValid<T>` is a `repr(transparent)` wrapper around
1782
+ // `MaybeUninit<T>`, which in turn has the same layout as `T`. Thus, the
1783
+ // trailing slices of `[T]` and of `MaybeValid<[T]>` both have element
1784
+ // type `T`. Since the number of elements is preserved during an `as`
1785
+ // cast of slice/DST pointers, the resulting `*const MaybeValid<[T]>`
1786
+ // has the same number of elements - and thus the same length - as the
1787
+ // original `*const [T]`.
1788
+ //
1789
+ // Thanks to their layouts, `MaybeValid<[T; N]>` and `MaybeValid<[T]>`
1790
+ // have the same alignment, so `mv_of_slice` is guaranteed to be
1791
+ // aligned.
1792
+ unsafe { & * mv_of_slice }
1793
+ }
1794
+ }
1795
+
1796
+ impl < T > Debug for MaybeValid < T > {
1797
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
1798
+ f. pad ( core:: any:: type_name :: < Self > ( ) )
1799
+ }
1800
+ }
1801
+
1573
1802
/// A type with no alignment requirement.
1574
1803
///
1575
1804
/// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`
@@ -3604,6 +3833,91 @@ mod tests {
3604
3833
assert_eq ! ( unsafe { m. assume_init_ref( ) } , & Cell :: new( 2 ) ) ;
3605
3834
}
3606
3835
3836
+ #[ test]
3837
+ fn test_maybe_valid ( ) {
3838
+ let m = MaybeValid :: < usize > :: default ( ) ;
3839
+ // SAFETY: all bit patterns are valid `usize`s, and `m` is initialized.
3840
+ let u = unsafe { m. assume_valid ( ) } ;
3841
+ // This ensures that Miri can see whether `u` (and thus `m`) has been
3842
+ // properly initialized.
3843
+ assert_eq ! ( u, u) ;
3844
+
3845
+ fn bytes_to_maybe_valid ( bytes : & mut [ u8 ] ) -> & mut MaybeValid < [ u8 ] > {
3846
+ // SAFETY: `MaybeValid<[u8]>` has the same layout as `[u8]`, and
3847
+ // `bytes` is initialized.
3848
+ unsafe {
3849
+ #[ allow( clippy:: as_conversions) ]
3850
+ let m = & mut * ( bytes as * mut [ u8 ] as * mut MaybeValid < [ u8 ] > ) ;
3851
+ m
3852
+ }
3853
+ }
3854
+
3855
+ let mut bytes = [ 0u8 , 1 , 2 ] ;
3856
+ let m = bytes_to_maybe_valid ( & mut bytes[ ..] ) ;
3857
+
3858
+ // SAFETY: `m` was created from a valid `[u8]`.
3859
+ let r = unsafe { m. assume_valid_ref ( ) } ;
3860
+ assert_eq ! ( r. len( ) , 3 ) ;
3861
+ assert_eq ! ( r, [ 0 , 1 , 2 ] ) ;
3862
+
3863
+ // SAFETY: `m` was created from a valid `[u8]`.
3864
+ let r = unsafe { m. assume_valid_mut ( ) } ;
3865
+ assert_eq ! ( r. len( ) , 3 ) ;
3866
+ assert_eq ! ( r, [ 0 , 1 , 2 ] ) ;
3867
+
3868
+ r[ 0 ] = 1 ;
3869
+ assert_eq ! ( bytes, [ 1 , 1 , 2 ] ) ;
3870
+
3871
+ let mut bytes = [ 0u8 , 1 , 2 ] ;
3872
+ let m = bytes_to_maybe_valid ( & mut bytes[ ..] ) ;
3873
+ let slc = m. as_slice_of_maybe_valids ( ) ;
3874
+ assert_eq ! ( slc. len( ) , 3 ) ;
3875
+ for i in 0u8 ..3 {
3876
+ // SAFETY: `m` was created from a valid `[u8]`.
3877
+ let u = unsafe { slc[ usize:: from ( i) ] . assume_valid_ref ( ) } ;
3878
+ assert_eq ! ( u, & i) ;
3879
+ }
3880
+ }
3881
+
3882
+ #[ test]
3883
+ fn test_maybe_valid_as_slice ( ) {
3884
+ let mut m = MaybeValid :: < [ u8 ; 3 ] > :: default ( ) ;
3885
+ // SAFETY: all bit patterns are valid `[u8; 3]`s, and `m` is
3886
+ // initialized.
3887
+ unsafe { * m. assume_valid_mut ( ) = [ 0 , 1 , 2 ] } ;
3888
+
3889
+ let slc = m. as_slice ( ) . as_slice_of_maybe_valids ( ) ;
3890
+ assert_eq ! ( slc. len( ) , 3 ) ;
3891
+
3892
+ for i in 0u8 ..3 {
3893
+ // SAFETY: `m` was initialized as a valid `[u8; 3]`.
3894
+ let u = unsafe { slc[ usize:: from ( i) ] . assume_valid_ref ( ) } ;
3895
+ assert_eq ! ( u, & i) ;
3896
+ }
3897
+ }
3898
+
3899
+ #[ test]
3900
+ fn test_maybe_valid_from_ref ( ) {
3901
+ use core:: cell:: Cell ;
3902
+
3903
+ let u = 1usize ;
3904
+ let m = MaybeValid :: from_ref ( & u) ;
3905
+ // SAFETY: `m` was constructed from a valid `&usize`.
3906
+ assert_eq ! ( unsafe { m. assume_valid_ref( ) } , & 1usize ) ;
3907
+
3908
+ // Test that interior mutability doesn't affect correctness or
3909
+ // soundness.
3910
+
3911
+ let c = Cell :: new ( 1usize ) ;
3912
+ let m = MaybeValid :: from_ref ( & c) ;
3913
+ // SAFETY: `m` was constructed from a valid `&usize`.
3914
+ assert_eq ! ( unsafe { m. assume_valid_ref( ) } , & Cell :: new( 1 ) ) ;
3915
+
3916
+ c. set ( 2 ) ;
3917
+ // SAFETY: `m` was constructed from a valid `&usize`.
3918
+ assert_eq ! ( unsafe { m. assume_valid_ref( ) } , & Cell :: new( 2 ) ) ;
3919
+ }
3920
+
3607
3921
#[ test]
3608
3922
fn test_unalign ( ) {
3609
3923
// Test methods that don't depend on alignment.
@@ -4519,6 +4833,12 @@ mod tests {
4519
4833
assert_impls ! ( MaybeUninit <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4520
4834
assert_impls ! ( MaybeUninit <MaybeUninit <NotZerocopy >>: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4521
4835
4836
+ assert_impls ! ( MaybeValid <u8 >: Unaligned , AsBytes , !FromZeroes , !FromBytes ) ;
4837
+ assert_impls ! ( MaybeValid <MaybeValid <u8 >>: Unaligned , AsBytes , !FromZeroes , !FromBytes ) ;
4838
+ assert_impls ! ( MaybeValid <[ u8 ] >: Unaligned , AsBytes , !FromZeroes , !FromBytes ) ;
4839
+ assert_impls ! ( MaybeValid <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4840
+ assert_impls ! ( MaybeValid <MaybeValid <NotZerocopy >>: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4841
+
4522
4842
assert_impls ! ( Wrapping <u8 >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
4523
4843
assert_impls ! ( Wrapping <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4524
4844
0 commit comments