@@ -157,6 +157,8 @@ use core::{
157
157
ptr, slice,
158
158
} ;
159
159
160
+ use project:: Projectable ;
161
+
160
162
#[ cfg( feature = "alloc" ) ]
161
163
extern crate alloc;
162
164
#[ cfg( feature = "alloc" ) ]
@@ -174,6 +176,29 @@ mod zerocopy {
174
176
pub ( crate ) use crate :: * ;
175
177
}
176
178
179
+ /// Documents multiple unsafe blocks with a single safety comment.
180
+ ///
181
+ /// Invoked as:
182
+ ///
183
+ /// ```rust,ignore
184
+ /// safety_comment! {
185
+ /// // Non-doc comments come first.
186
+ /// /// SAFETY:
187
+ /// /// Safety comment starts on its own line.
188
+ /// macro_1!(args);
189
+ /// macro_2! { args };
190
+ /// }
191
+ /// ```
192
+ ///
193
+ /// The macro invocations are emitted, each decorated with the following
194
+ /// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`.
195
+ macro_rules! safety_comment {
196
+ ( #[ doc = r" SAFETY:" ] $( #[ doc = $_doc: literal] ) * $( $macro: ident!$args: tt; ) * ) => {
197
+ #[ allow( clippy:: undocumented_unsafe_blocks) ]
198
+ const _: ( ) = { $( $macro!$args; ) * } ;
199
+ }
200
+ }
201
+
177
202
/// Types for which a sequence of bytes all set to zero represents a valid
178
203
/// instance of the type.
179
204
///
@@ -499,6 +524,293 @@ pub unsafe trait FromBytes: FromZeroes {
499
524
}
500
525
}
501
526
527
+ /// TODO
528
+ ///
529
+ /// # Safety
530
+ ///
531
+ /// `AsMaybeUninit` must only be implemented for types which are `Sized` or
532
+ /// whose last field is a slice whose element type is `Sized` (this includes
533
+ /// slice types themselves; in a slice type, the "last field" simply refers to
534
+ /// the slice itself).
535
+ pub unsafe trait AsMaybeUninit {
536
+ /// TODO
537
+ ///
538
+ /// # Safety
539
+ ///
540
+ /// For `T: AsMaybeUninit`, the following must hold:
541
+ /// - Given `m: T::MaybeUninit`, it is sound to write uninitialized bytes at
542
+ /// every byte offset in `m` (this description avoids the "what lengths
543
+ /// are valid" problem)
544
+ /// - `T` and `T::MaybeUninit` have the same alignment requirement (can't
545
+ /// use `align_of` to describe this because it requires that its argument
546
+ /// is sized)
547
+ /// - `T` and `T::MaybeUninit` are either both `Sized` or both `!Sized`
548
+ /// - If they are `Sized`, `size_of::<T>() == size_of::<T::MaybeUninit>()`
549
+ /// - If they are `!Sized`, then they are both DSTs with a trailing slice.
550
+ /// Given `t: &T` and `m: &T::MaybeUninit` with the same number of
551
+ /// elements in their trailing slices, `size_of_val(t) == size_of_val(m)`.
552
+ type MaybeUninit : ?Sized + FromBytes ;
553
+ }
554
+
555
+ unsafe impl < T : Sized > AsMaybeUninit for T {
556
+ type MaybeUninit = MaybeUninit < T > ;
557
+ }
558
+
559
+ unsafe impl < T : Sized > AsMaybeUninit for [ T ] {
560
+ type MaybeUninit = [ MaybeUninit < T > ] ;
561
+ }
562
+
563
+ unsafe impl AsMaybeUninit for str {
564
+ type MaybeUninit = <[ u8 ] as AsMaybeUninit >:: MaybeUninit ;
565
+ }
566
+
567
+ /// A value which might or might not constitute a valid instance of `T`.
568
+ ///
569
+ /// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
570
+ /// as `T`. However, it may contain any bit pattern with a few restrictions:
571
+ /// Given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
572
+ /// size_of::<MaybeValid<T>>())`:
573
+ /// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
574
+ /// initialized, then the byte at offset `b` within `m` is guaranteed to be
575
+ /// initialized.
576
+ /// - Let `s` be the sequence of bytes of length `b` in the offset range `[0,
577
+ /// b)` in `m`. Let `TT` be the subset of valid instances of `T` which contain
578
+ /// this sequence in the offset range `[0, b)`. If, for all instances of `t:
579
+ /// T` in `TT`, the byte at offset `b` in `t` is initialized, then the byte at
580
+ /// offset `b` in `m` is guaranteed to be initialized.
581
+ ///
582
+ /// Pragmatically, this means that if `m` is guaranteed to contain an enum
583
+ /// type at a particular offset, and the enum discriminant stored in `m`
584
+ /// corresponds to a valid variant of that enum type, then it is guaranteed
585
+ /// that the appropriate bytes of `m` are initialized as defined by that
586
+ /// variant's layout (although note that the variant's layout may contain
587
+ /// another enum type, in which case the same rules apply depending on the
588
+ /// state of its discriminant, and so on recursively).
589
+ ///
590
+ /// # Safety
591
+ ///
592
+ /// TODO (make sure to mention enum layout)
593
+ #[ derive( FromZeroes , FromBytes , AsBytes , Unaligned ) ]
594
+ #[ repr( transparent) ]
595
+ pub struct MaybeValid < T : AsMaybeUninit + ?Sized > {
596
+ inner : T :: MaybeUninit ,
597
+ }
598
+
599
+ impl < T > MaybeValid < T > {
600
+ /// TODO
601
+ pub const unsafe fn assume_valid ( self ) -> T {
602
+ unsafe { self . inner . assume_init ( ) }
603
+ }
604
+ }
605
+
606
+ impl < T > MaybeValid < [ T ] > {
607
+ /// TODO
608
+ pub const fn as_slice_of_maybe_valids ( & self ) -> & [ MaybeValid < T > ] {
609
+ let inner: & [ <T as AsMaybeUninit >:: MaybeUninit ] = & self . inner ;
610
+ let inner_ptr: * const [ <T as AsMaybeUninit >:: MaybeUninit ] = inner;
611
+ let ret_ptr = inner_ptr as * const [ MaybeValid < T > ] ;
612
+ // SAFETY: Since `inner` is a `&[T::MaybeUninit]`, and `MaybeValid<T>`
613
+ // is a `repr(transparent)` struct around `T::MaybeUninit`, `inner` has
614
+ // the same layout as `&[MaybeValid<T>]`.
615
+ unsafe { & * ret_ptr }
616
+ }
617
+ }
618
+
619
+ impl < const N : usize , T > MaybeValid < [ T ; N ] > {
620
+ /// TODO
621
+ pub const fn as_slice ( & self ) -> & MaybeValid < [ T ] > {
622
+ todo ! ( )
623
+ }
624
+ }
625
+
626
+ unsafe impl < T , F > Projectable < F , MaybeValid < F > > for MaybeValid < T > {
627
+ type Inner = T ;
628
+ }
629
+
630
+ impl < T > Debug for MaybeValid < T > {
631
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
632
+ f. pad ( core:: any:: type_name :: < Self > ( ) )
633
+ }
634
+ }
635
+
636
+ /// TODO
637
+ pub unsafe trait TryFromBytes : AsMaybeUninit {
638
+ /// TODO
639
+ fn is_bit_valid ( candidate : & MaybeValid < Self > ) -> bool ;
640
+
641
+ /// TODO
642
+ // Note that, in a future in which we distinguish between `FromBytes` and `RefFromBytes`,
643
+ // this requires `where Self: RefFromBytes` to disallow interior mutability.
644
+ fn try_from_ref ( bytes : & [ u8 ] ) -> Option < & Self >
645
+ where
646
+ // TODO: Remove this bound.
647
+ Self : Sized ,
648
+ {
649
+ // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this
650
+ // function once #115080 is resolved.
651
+ #[ inline( always) ]
652
+ fn try_read_from_inner < T : Sized , F : FnOnce ( & MaybeValid < T > ) -> bool > (
653
+ bytes : & [ u8 ] ,
654
+ is_bit_valid : F ,
655
+ ) -> Option < & T > {
656
+ let maybe_valid = Ref :: < _ , MaybeValid < T > > :: new ( bytes) ?. into_ref ( ) ;
657
+
658
+ if is_bit_valid ( maybe_valid) {
659
+ Some ( unsafe { & * bytes. as_ptr ( ) . cast :: < T > ( ) } )
660
+ } else {
661
+ None
662
+ }
663
+ }
664
+
665
+ try_read_from_inner ( bytes, Self :: is_bit_valid)
666
+ }
667
+
668
+ /// TODO
669
+ fn try_from_mut ( bytes : & mut [ u8 ] ) -> Option < & mut Self >
670
+ where
671
+ // TODO: Remove the `Sized` bound.
672
+ Self : AsBytes + Sized ,
673
+ {
674
+ // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this
675
+ // function once #115080 is resolved.
676
+ #[ inline( always) ]
677
+ fn try_read_from_mut_inner < T : Sized , F : FnOnce ( & MaybeValid < T > ) -> bool > (
678
+ bytes : & mut [ u8 ] ,
679
+ is_bit_valid : F ,
680
+ ) -> Option < & mut T > {
681
+ let maybe_valid = Ref :: < _ , MaybeValid < T > > :: new ( & * bytes) ?. into_ref ( ) ;
682
+
683
+ if is_bit_valid ( maybe_valid) {
684
+ Some ( unsafe { & mut * bytes. as_mut_ptr ( ) . cast :: < T > ( ) } )
685
+ } else {
686
+ None
687
+ }
688
+ }
689
+
690
+ try_read_from_mut_inner ( bytes, Self :: is_bit_valid)
691
+ }
692
+
693
+ /// TODO
694
+ fn try_read_from ( bytes : & [ u8 ] ) -> Option < Self >
695
+ where
696
+ Self : Sized ,
697
+ {
698
+ // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this
699
+ // function once #115080 is resolved.
700
+ #[ inline( always) ]
701
+ fn try_read_from_inner < T : Sized , F : FnOnce ( & MaybeValid < T > ) -> bool > (
702
+ bytes : & [ u8 ] ,
703
+ is_bit_valid : F ,
704
+ ) -> Option < T > {
705
+ let maybe_valid = MaybeValid :: < T > :: read_from ( bytes) ?;
706
+
707
+ if is_bit_valid ( & maybe_valid) {
708
+ Some ( unsafe { maybe_valid. assume_valid ( ) } )
709
+ } else {
710
+ None
711
+ }
712
+ }
713
+
714
+ try_read_from_inner ( bytes, Self :: is_bit_valid)
715
+ }
716
+ }
717
+
718
+ unsafe impl < T : FromBytes > TryFromBytes for T {
719
+ fn is_bit_valid ( _candidate : & MaybeValid < T > ) -> bool {
720
+ true
721
+ }
722
+ }
723
+
724
+ // TODO: This conflicts with the blanket impl for `T: TryFromBytes`.
725
+
726
+ // unsafe impl<const N: usize, T: TryFromBytes + Sized> TryFromBytes for [T; N]
727
+ // where
728
+ // // TODO: Why can't Rust infer this based on the blanket impl for `T: Sized`?
729
+ // // It sets `MaybeUninit = MaybeUninit<T>`, which is `Sized`.
730
+ // <T as AsMaybeUninit>::MaybeUninit: Sized,
731
+ // {
732
+ // fn is_bit_valid(candidate: &MaybeValid<[T; N]>) -> bool {
733
+ // candidate.as_slice().as_slice_of_maybe_valids().iter().all(|c| T::is_bit_valid(c))
734
+ // }
735
+ // }
736
+
737
+ unsafe impl < T : TryFromBytes + Sized > TryFromBytes for [ T ]
738
+ where
739
+ // TODO: Why can't Rust infer this based on the blanket impl for `T: Sized`?
740
+ // It sets `MaybeUninit = MaybeUninit<T>`, which is `Sized`.
741
+ <T as AsMaybeUninit >:: MaybeUninit : Sized ,
742
+ {
743
+ fn is_bit_valid ( candidate : & MaybeValid < [ T ] > ) -> bool {
744
+ candidate. as_slice_of_maybe_valids ( ) . iter ( ) . all ( |c| T :: is_bit_valid ( c) )
745
+ }
746
+ }
747
+
748
+ /// # Safety
749
+ ///
750
+ /// It must be sound to transmute `&MaybeValid<$ty>` into `&$repr`.
751
+ macro_rules! unsafe_impl_try_from_bytes {
752
+ ( $ty: ty, $repr: ty, |$candidate: ident| $is_bit_valid: expr) => {
753
+ unsafe impl TryFromBytes for $ty {
754
+ fn is_bit_valid( candidate: & MaybeValid <$ty>) -> bool {
755
+ let $candidate = unsafe { & * ( candidate as * const MaybeValid <$ty> as * const $repr) } ;
756
+ $is_bit_valid
757
+ }
758
+ }
759
+ } ;
760
+ }
761
+
762
+ safety_comment ! {
763
+ /// SAFETY:
764
+ /// All of the `NonZeroXxx` types have the same layout as `Xxx`. Also, every
765
+ /// byte of such a type is required to be initialized, so it is guaranteed
766
+ /// that every byte of a `MaybeValid<NonZeroXxx>` must also be initialized.
767
+ /// Thus, it is sound to transmute a `&MaybeValid<NonZeroXxx>` to a `&Xxx`.
768
+ ///
769
+ /// TODO: Why are these impls correct (ie, ensure valid NonZeroXxx types)?
770
+ unsafe_impl_try_from_bytes!( NonZeroU8 , u8 , |n| * n != 0 ) ;
771
+ unsafe_impl_try_from_bytes!( NonZeroU16 , u16 , |n| * n != 0 ) ;
772
+ unsafe_impl_try_from_bytes!( NonZeroU32 , u32 , |n| * n != 0 ) ;
773
+ unsafe_impl_try_from_bytes!( NonZeroU64 , u64 , |n| * n != 0 ) ;
774
+ unsafe_impl_try_from_bytes!( NonZeroU128 , u128 , |n| * n != 0 ) ;
775
+ unsafe_impl_try_from_bytes!( NonZeroUsize , usize , |n| * n != 0 ) ;
776
+ unsafe_impl_try_from_bytes!( NonZeroI8 , i8 , |n| * n != 0 ) ;
777
+ unsafe_impl_try_from_bytes!( NonZeroI16 , i16 , |n| * n != 0 ) ;
778
+ unsafe_impl_try_from_bytes!( NonZeroI32 , i32 , |n| * n != 0 ) ;
779
+ unsafe_impl_try_from_bytes!( NonZeroI64 , i64 , |n| * n != 0 ) ;
780
+ unsafe_impl_try_from_bytes!( NonZeroI128 , i128 , |n| * n != 0 ) ;
781
+ unsafe_impl_try_from_bytes!( NonZeroIsize , isize , |n| * n != 0 ) ;
782
+ }
783
+
784
+ unsafe_impl_try_from_bytes ! ( bool , u8 , |byte| * byte < 2 ) ;
785
+
786
+ unsafe_impl_try_from_bytes ! ( char , [ u8 ; 4 ] , |bytes| {
787
+ let c = u32 :: from_ne_bytes( * bytes) ;
788
+ char :: from_u32( c) . is_some( )
789
+ } ) ;
790
+
791
+ unsafe_impl_try_from_bytes ! ( str , [ u8 ] , |bytes| core:: str :: from_utf8( bytes) . is_ok( ) ) ;
792
+
793
+ mod try_from_bytes_derive_example {
794
+ use super :: * ;
795
+
796
+ struct Foo {
797
+ a : u8 ,
798
+ b : u16 ,
799
+ }
800
+
801
+ impl_try_from_bytes ! ( Foo { a: u8 , b: u16 } ) ;
802
+
803
+ struct Bar ( Foo ) ;
804
+
805
+ impl Bar {
806
+ fn is_valid ( & self ) -> bool {
807
+ u16:: from ( self . 0 . a ) < self . 0 . b
808
+ }
809
+ }
810
+
811
+ impl_try_from_bytes ! ( Bar { 0 : Foo } => is_valid) ;
812
+ }
813
+
502
814
/// Types which are safe to treat as an immutable byte slice.
503
815
///
504
816
/// WARNING: Do not implement this trait yourself! Instead, use
@@ -696,29 +1008,6 @@ pub unsafe trait Unaligned {
696
1008
Self : Sized ;
697
1009
}
698
1010
699
- /// Documents multiple unsafe blocks with a single safety comment.
700
- ///
701
- /// Invoked as:
702
- ///
703
- /// ```rust,ignore
704
- /// safety_comment! {
705
- /// // Non-doc comments come first.
706
- /// /// SAFETY:
707
- /// /// Safety comment starts on its own line.
708
- /// macro_1!(args);
709
- /// macro_2! { args };
710
- /// }
711
- /// ```
712
- ///
713
- /// The macro invocations are emitted, each decorated with the following
714
- /// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`.
715
- macro_rules! safety_comment {
716
- ( #[ doc = r" SAFETY:" ] $( #[ doc = $_doc: literal] ) * $( $macro: ident!$args: tt; ) * ) => {
717
- #[ allow( clippy:: undocumented_unsafe_blocks) ]
718
- const _: ( ) = { $( $macro!$args; ) * } ;
719
- }
720
- }
721
-
722
1011
/// Unsafely implements trait(s) for a type.
723
1012
macro_rules! unsafe_impl {
724
1013
// Implement `$trait` for `$ty` with no bounds.
0 commit comments