@@ -1783,6 +1783,199 @@ safety_comment! {
1783
1783
assert_unaligned!( mem:: MaybeUninit <( ) >, MaybeUninit <u8 >) ;
1784
1784
}
1785
1785
1786
+ /// A value which might or might not constitute a valid instance of `T`.
1787
+ ///
1788
+ /// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
1789
+ /// as `T`. Unlike `T`, it may contain any bit pattern, except that
1790
+ /// uninitialized bytes may only appear in `MaybeValid<T>` at byte offsets where
1791
+ /// they may appear in `T`. This is a dynamic property: if, at a particular byte
1792
+ /// offset, a valid enum discriminant is set, the subsequent bytes may only have
1793
+ /// uninitialized bytes as specified by the corresponding enum variant.
1794
+ ///
1795
+ /// Formally, given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
1796
+ /// size_of_val(m))`:
1797
+ /// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
1798
+ /// initialized, then the byte at offset `b` within `m` is guaranteed to be
1799
+ /// initialized.
1800
+ /// - Let `c` be the contents of the byte range `[0, b)` in `m`. Let `TT` be the
1801
+ /// subset of valid instances of `T` which contain `c` in the offset range
1802
+ /// `[0, b)`. If, for all instances of `t: T` in `TT`, the byte at offset `b`
1803
+ /// in `t` is initialized, then the byte at offset `b` in `m` is guaranteed to
1804
+ /// be initialized.
1805
+ ///
1806
+ /// Pragmatically, this means that if `m` is guaranteed to contain an enum
1807
+ /// type at a particular offset, and the enum discriminant stored in `m`
1808
+ /// corresponds to a valid variant of that enum type, then it is guaranteed
1809
+ /// that the appropriate bytes of `m` are initialized as defined by that
1810
+ /// variant's bit validity (although note that the variant may contain another
1811
+ /// enum type, in which case the same rules apply depending on the state of
1812
+ /// its discriminant, and so on recursively).
1813
+ ///
1814
+ /// # Safety
1815
+ ///
1816
+ /// Unsafe code may assume that an instance of `MaybeValid` satisfies the
1817
+ /// constraints described above. Unsafe code may produce a `MaybeValid` or
1818
+ /// modify the bytes of an existing `MaybeValid` so long as these constraints
1819
+ /// are upheld. It is unsound to produce a `MaybeValid` which fails to uphold
1820
+ /// these constraints.
1821
+ #[ repr( transparent) ]
1822
+ pub struct MaybeValid < T : ?Sized + KnownLayout > {
1823
+ inner : MaybeUninit < T > ,
1824
+ }
1825
+
1826
+ safety_comment ! {
1827
+ /// SAFETY:
1828
+ /// - `AsBytes`: `MaybeValid` requires that, if a byte in `T` is always
1829
+ /// initialized, the equivalent byte in `MaybeValid<T>` must be
1830
+ /// initialized. `T: AsBytes` implies that all bytes in `T` must always be
1831
+ /// initialized, and so all bytes in `MaybeValid<T>` must always be
1832
+ /// initialized, and so `MaybeValid<T>` satisfies `AsBytes`.
1833
+ /// - `Unaligned`: `MaybeValid<T>` has the same alignment as `T`.
1834
+ /// - `KnownLayout`: Since `MaybeUninit<T>` is a `repr(transparent)` wrapper
1835
+ /// around `T::MaybeUninit`:
1836
+ /// - They have the same prefix size, alignment, and trailing slice
1837
+ /// element size
1838
+ /// - It is valid to perform an `as` cast in either direction, and this
1839
+ /// operation preserves referent size
1840
+ ///
1841
+ /// TODO(#5): Implement `FromZeroes` and `FromBytes` for `MaybeValid<T>` and
1842
+ /// `MaybeValid<[T]>`.
1843
+ unsafe_impl!( T : ?Sized + KnownLayout + AsBytes => AsBytes for MaybeValid <T >) ;
1844
+ unsafe_impl!( T : ?Sized + KnownLayout + Unaligned => Unaligned for MaybeValid <T >) ;
1845
+ unsafe_impl_known_layout!( T : ?Sized + KnownLayout => #[ repr( MaybeUninit <T >) ] MaybeValid <T >) ;
1846
+ }
1847
+
1848
+ // impl<T: KnownLayout> Default for MaybeValid<T> {
1849
+ // fn default() -> MaybeValid<T> {
1850
+ // // SAFETY: All of the bytes of `inner` are initialized to 0, and so the
1851
+ // // safety invariant on `MaybeValid` is upheld.
1852
+ // MaybeValid { inner: MaybeUninit::zeroed() }
1853
+ // }
1854
+ // }
1855
+
1856
+ impl < T : KnownLayout + ?Sized > MaybeValid < T > {
1857
+ /// Converts this `&MaybeValid<T>` to a `&T`.
1858
+ ///
1859
+ /// # Safety
1860
+ ///
1861
+ /// `self` must contain a valid `T`.
1862
+ pub unsafe fn assume_valid_ref ( & self ) -> & T {
1863
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
1864
+ // Since `Self` is `repr(transparent)`, it has the same layout as
1865
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
1866
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
1867
+ // `T`.
1868
+ unsafe { self . inner . assume_init_ref ( ) }
1869
+ }
1870
+
1871
+ /// Converts this `&mut MaybeValid<T>` to a `&mut T`.
1872
+ ///
1873
+ /// # Safety
1874
+ ///
1875
+ /// `self` must contain a valid `T`.
1876
+ pub unsafe fn assume_valid_mut ( & mut self ) -> & mut T {
1877
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
1878
+ // Since `Self` is `repr(transparent)`, it has the same layout as
1879
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
1880
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
1881
+ // `T`.
1882
+ unsafe { self . inner . assume_init_mut ( ) }
1883
+ }
1884
+
1885
+ /// Gets a view of this `&T` as a `&MaybeValid<T>`.
1886
+ ///
1887
+ /// There is no mutable equivalent to this function, as producing a `&mut
1888
+ /// MaybeValid<T>` from a `&mut T` would allow safe code to write invalid
1889
+ /// values which would be accessible through `&mut T`.
1890
+ pub fn from_ref ( r : & T ) -> & MaybeValid < T > {
1891
+ let m: * const MaybeUninit < T > = MaybeUninit :: from_ref ( r) ;
1892
+ #[ allow( clippy:: as_conversions) ]
1893
+ let ptr = m as * const MaybeValid < T > ;
1894
+ // SAFETY: Since `Self` is `repr(transparent)`, it has the same layout
1895
+ // as `MaybeUninit<T>`, so the size and alignment here are valid.
1896
+ //
1897
+ // `MaybeValid<T>`'s bit validity constraints are weaker than those of
1898
+ // `T`, so this is guaranteed not to produce an invalid `MaybeValid<T>`.
1899
+ // If it were possible to write a different value for `MaybeValid<T>`
1900
+ // through the returned reference, it could result in an invalid value
1901
+ // being exposed via the `&T`. Luckily, the only way for mutation to
1902
+ // happen is if `T` contains an `UnsafeCell` and the caller uses it to
1903
+ // perform interior mutation. Importantly, `T` containing an
1904
+ // `UnsafeCell` does not permit interior mutation through
1905
+ // `MaybeValid<T>`, so it doesn't permit writing uninitialized or
1906
+ // otherwise invalid values which would be visible through the original
1907
+ // `&T`.
1908
+ unsafe { & * ptr }
1909
+ }
1910
+ }
1911
+
1912
+ impl < T : KnownLayout < MaybeUninit = mem:: MaybeUninit < T > > > MaybeValid < T > {
1913
+ /// Converts this `MaybeValid<T>` to a `T`.
1914
+ ///
1915
+ /// # Safety
1916
+ ///
1917
+ /// `self` must contain a valid `T`.
1918
+ pub const unsafe fn assume_valid ( self ) -> T {
1919
+ // SAFETY: The caller has promised that `self` contains a valid `T`.
1920
+ // Since `Self` is `repr(transparent)`, it has the same layout as
1921
+ // `MaybeUninit<T>`, which in turn is guaranteed to have the same layout
1922
+ // as `T`. Thus, it is sound to treat `self.inner` as containing a valid
1923
+ // `T`.
1924
+ unsafe { self . inner . assume_init ( ) }
1925
+ }
1926
+ }
1927
+
1928
+ impl < T : KnownLayout < MaybeUninit = mem:: MaybeUninit < T > > > MaybeValid < [ T ] > {
1929
+ /// Converts a `MaybeValid<[T]>` to a `[MaybeValid<T>]`.
1930
+ ///
1931
+ /// `MaybeValid<T>` has the same layout as `T`, so these layouts are
1932
+ /// equivalent.
1933
+ pub const fn as_slice_of_maybe_valids ( & self ) -> & [ MaybeValid < T > ] {
1934
+ let inner: & [ <T as KnownLayout >:: MaybeUninit ] = & self . inner . inner ;
1935
+ let inner_ptr: * const [ <T as KnownLayout >:: MaybeUninit ] = inner;
1936
+ // Note: this Clippy warning is only emitted on our MSRV (1.61), but not
1937
+ // on later versions of Clippy. Thus, we consider it spurious.
1938
+ #[ allow( clippy:: as_conversions) ]
1939
+ let ret_ptr = inner_ptr as * const [ MaybeValid < T > ] ;
1940
+ // SAFETY: Since `inner` is a `&[MaybeUninit<T>]`, and `MaybeValid<T>`
1941
+ // is a `repr(transparent)` struct around `MaybeUninit<T>`, `inner` has
1942
+ // the same layout as `&[MaybeValid<T>]`.
1943
+ unsafe { & * ret_ptr }
1944
+ }
1945
+ }
1946
+
1947
+ impl < const N : usize , T : KnownLayout > MaybeValid < [ T ; N ] > {
1948
+ /// Converts a `MaybeValid<[T; N]>` to a `MaybeValid<[T]>`.
1949
+ // TODO(#64): Make this `const` once our MSRV is >= 1.64.0 (when
1950
+ // `slice_from_raw_parts` was stabilized as `const`).
1951
+ pub fn as_slice ( & self ) -> & MaybeValid < [ T ] > {
1952
+ let base: * const MaybeValid < [ T ; N ] > = self ;
1953
+ let slice_of_t: * const [ T ] = ptr:: slice_from_raw_parts ( base. cast :: < T > ( ) , N ) ;
1954
+ // Note: this Clippy warning is only emitted on our MSRV (1.61), but not
1955
+ // on later versions of Clippy. Thus, we consider it spurious.
1956
+ #[ allow( clippy:: as_conversions) ]
1957
+ let mv_of_slice = slice_of_t as * const MaybeValid < [ T ] > ;
1958
+ // SAFETY: `MaybeValid<T>` is a `repr(transparent)` wrapper around
1959
+ // `MaybeUninit<T>`, which in turn has the same layout as `T`. Thus, the
1960
+ // trailing slices of `[T]` and of `MaybeValid<[T]>` both have element
1961
+ // type `T`. Since the number of elements is preserved during an `as`
1962
+ // cast of slice/DST pointers, the resulting `*const MaybeValid<[T]>`
1963
+ // has the same number of elements - and thus the same length - as the
1964
+ // original `*const [T]`.
1965
+ //
1966
+ // Thanks to their layouts, `MaybeValid<[T; N]>` and `MaybeValid<[T]>`
1967
+ // have the same alignment, so `mv_of_slice` is guaranteed to be
1968
+ // aligned.
1969
+ unsafe { & * mv_of_slice }
1970
+ }
1971
+ }
1972
+
1973
+ impl < T : ?Sized + KnownLayout > Debug for MaybeValid < T > {
1974
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
1975
+ f. pad ( core:: any:: type_name :: < Self > ( ) )
1976
+ }
1977
+ }
1978
+
1786
1979
/// A type with no alignment requirement.
1787
1980
///
1788
1981
/// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`
@@ -3822,6 +4015,91 @@ mod tests {
3822
4015
assert_eq ! ( unsafe { m. assume_init_ref( ) } , & Cell :: new( 2 ) ) ;
3823
4016
}
3824
4017
4018
+ #[ test]
4019
+ fn test_maybe_valid ( ) {
4020
+ let m = MaybeValid :: < usize > :: default ( ) ;
4021
+ // SAFETY: all bit patterns are valid `usize`s, and `m` is initialized.
4022
+ let u = unsafe { m. assume_valid ( ) } ;
4023
+ // This ensures that Miri can see whether `u` (and thus `m`) has been
4024
+ // properly initialized.
4025
+ assert_eq ! ( u, u) ;
4026
+
4027
+ fn bytes_to_maybe_valid ( bytes : & mut [ u8 ] ) -> & mut MaybeValid < [ u8 ] > {
4028
+ // SAFETY: `MaybeValid<[u8]>` has the same layout as `[u8]`, and
4029
+ // `bytes` is initialized.
4030
+ unsafe {
4031
+ #[ allow( clippy:: as_conversions) ]
4032
+ let m = & mut * ( bytes as * mut [ u8 ] as * mut MaybeValid < [ u8 ] > ) ;
4033
+ m
4034
+ }
4035
+ }
4036
+
4037
+ let mut bytes = [ 0u8 , 1 , 2 ] ;
4038
+ let m = bytes_to_maybe_valid ( & mut bytes[ ..] ) ;
4039
+
4040
+ // SAFETY: `m` was created from a valid `[u8]`.
4041
+ let r = unsafe { m. assume_valid_ref ( ) } ;
4042
+ assert_eq ! ( r. len( ) , 3 ) ;
4043
+ assert_eq ! ( r, [ 0 , 1 , 2 ] ) ;
4044
+
4045
+ // SAFETY: `m` was created from a valid `[u8]`.
4046
+ let r = unsafe { m. assume_valid_mut ( ) } ;
4047
+ assert_eq ! ( r. len( ) , 3 ) ;
4048
+ assert_eq ! ( r, [ 0 , 1 , 2 ] ) ;
4049
+
4050
+ r[ 0 ] = 1 ;
4051
+ assert_eq ! ( bytes, [ 1 , 1 , 2 ] ) ;
4052
+
4053
+ let mut bytes = [ 0u8 , 1 , 2 ] ;
4054
+ let m = bytes_to_maybe_valid ( & mut bytes[ ..] ) ;
4055
+ let slc = m. as_slice_of_maybe_valids ( ) ;
4056
+ assert_eq ! ( slc. len( ) , 3 ) ;
4057
+ for i in 0u8 ..3 {
4058
+ // SAFETY: `m` was created from a valid `[u8]`.
4059
+ let u = unsafe { slc[ usize:: from ( i) ] . assume_valid_ref ( ) } ;
4060
+ assert_eq ! ( u, & i) ;
4061
+ }
4062
+ }
4063
+
4064
+ #[ test]
4065
+ fn test_maybe_valid_as_slice ( ) {
4066
+ let mut m = MaybeValid :: < [ u8 ; 3 ] > :: default ( ) ;
4067
+ // SAFETY: all bit patterns are valid `[u8; 3]`s, and `m` is
4068
+ // initialized.
4069
+ unsafe { * m. assume_valid_mut ( ) = [ 0 , 1 , 2 ] } ;
4070
+
4071
+ let slc = m. as_slice ( ) . as_slice_of_maybe_valids ( ) ;
4072
+ assert_eq ! ( slc. len( ) , 3 ) ;
4073
+
4074
+ for i in 0u8 ..3 {
4075
+ // SAFETY: `m` was initialized as a valid `[u8; 3]`.
4076
+ let u = unsafe { slc[ usize:: from ( i) ] . assume_valid_ref ( ) } ;
4077
+ assert_eq ! ( u, & i) ;
4078
+ }
4079
+ }
4080
+
4081
+ #[ test]
4082
+ fn test_maybe_valid_from_ref ( ) {
4083
+ use core:: cell:: Cell ;
4084
+
4085
+ let u = 1usize ;
4086
+ let m = MaybeValid :: from_ref ( & u) ;
4087
+ // SAFETY: `m` was constructed from a valid `&usize`.
4088
+ assert_eq ! ( unsafe { m. assume_valid_ref( ) } , & 1usize ) ;
4089
+
4090
+ // Test that interior mutability doesn't affect correctness or
4091
+ // soundness.
4092
+
4093
+ let c = Cell :: new ( 1usize ) ;
4094
+ let m = MaybeValid :: from_ref ( & c) ;
4095
+ // SAFETY: `m` was constructed from a valid `&usize`.
4096
+ assert_eq ! ( unsafe { m. assume_valid_ref( ) } , & Cell :: new( 1 ) ) ;
4097
+
4098
+ c. set ( 2 ) ;
4099
+ // SAFETY: `m` was constructed from a valid `&usize`.
4100
+ assert_eq ! ( unsafe { m. assume_valid_ref( ) } , & Cell :: new( 2 ) ) ;
4101
+ }
4102
+
3825
4103
#[ test]
3826
4104
fn test_unalign ( ) {
3827
4105
// Test methods that don't depend on alignment.
@@ -4740,6 +5018,12 @@ mod tests {
4740
5018
assert_impls ! ( MaybeUninit <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4741
5019
assert_impls ! ( MaybeUninit <MaybeUninit <NotZerocopy >>: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4742
5020
5021
+ assert_impls ! ( MaybeValid <u8 >: Unaligned , AsBytes , !FromZeroes , !FromBytes ) ;
5022
+ assert_impls ! ( MaybeValid <MaybeValid <u8 >>: Unaligned , AsBytes , !FromZeroes , !FromBytes ) ;
5023
+ assert_impls ! ( MaybeValid <[ u8 ] >: Unaligned , AsBytes , !FromZeroes , !FromBytes ) ;
5024
+ assert_impls ! ( MaybeValid <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
5025
+ assert_impls ! ( MaybeValid <MaybeValid <NotZerocopy >>: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
5026
+
4743
5027
assert_impls ! ( Wrapping <u8 >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
4744
5028
assert_impls ! ( Wrapping <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
4745
5029
0 commit comments