Skip to content

Commit 9dc10c8

Browse files
authored
Add new trait impls (#141)
Add the following impls: - `str: Unaligned` - `NonZeroU8: Unaligned` - `NonZeroI8: Unaligned` - `Option<NonZeroU8>: Unaligned` - `Option<NonZeroI8>: Unaligned` - `MaybeUninit<T>: Unaligned` where `T: Unaligned` Previously, `unsafe_impl!` special-cased `Unaligned` and emitted a static assertion that the type in question actually had alignment 1. Since this used `core::mem::align_of`, which only works for `Sized` types, it was incompatible with adding an impl of `Unaligned` for `str` in this commit. In order to avoid this issue, this commit removes that special-casing, and introduces a new `assert_unaligned!` macro. Existing `Unaligned` implementations are modified to also call `assert_unaligned!`. `assert_unaligned!(str)` is not used since `str: !Unaligned`.
1 parent 6beb8a4 commit 9dc10c8

File tree

1 file changed

+79
-42
lines changed

1 file changed

+79
-42
lines changed

src/lib.rs

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -576,19 +576,6 @@ macro_rules! safety_comment {
576576

577577
/// Unsafely implements trait(s) for a type.
578578
macro_rules! unsafe_impl {
579-
// Implement `Unaligned` for `$ty` with no bounds.
580-
//
581-
// For `Unaligned` in particular, it's possible to assert at compile time
582-
// that the trait impl is sound. This provides a small speed bump to
583-
// accidentally implementing `Unaligned` for a type with alignment greater
584-
// than 1.
585-
($ty:ty: Unaligned) => {
586-
// We only compile this assertion under `cfg(test)` to avoid making this
587-
// crate more expensive to compile for our dependents.
588-
#[cfg(test)]
589-
const _: () = { static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1); };
590-
unsafe impl Unaligned for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
591-
};
592579
// Implement `$trait` for `$ty` with no bounds.
593580
($ty:ty: $trait:ty) => {
594581
unsafe impl $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
@@ -622,6 +609,23 @@ macro_rules! unsafe_impl {
622609
};
623610
}
624611

612+
/// Uses `align_of` to confirm that a type or set of types have alignment 1.
613+
///
614+
/// Note that `align_of<T>` requires `T: Sized`, so this macro doesn't work for
615+
/// unsized types.
616+
macro_rules! assert_unaligned {
617+
($ty:ty) => {
618+
// We only compile this assertion under `cfg(test)` to avoid taking an
619+
// extra non-dev dependency (and making this crate more expensive to
620+
// compile for our dependents).
621+
#[cfg(test)]
622+
static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1);
623+
};
624+
($($ty:ty),*) => {
625+
$(assert_unaligned!($ty);)*
626+
};
627+
}
628+
625629
safety_comment! {
626630
/// SAFETY:
627631
/// Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a
@@ -633,6 +637,7 @@ safety_comment! {
633637
///
634638
/// [1] https://doc.rust-lang.org/reference/type-layout.html#tuple-layout
635639
unsafe_impl!((): FromBytes, AsBytes, Unaligned);
640+
assert_unaligned!(());
636641
}
637642

638643
safety_comment! {
@@ -651,6 +656,7 @@ safety_comment! {
651656
/// [2] https://doc.rust-lang.org/reference/type-layout.html#primitive-data-layout
652657
unsafe_impl!(u8: FromBytes, AsBytes, Unaligned);
653658
unsafe_impl!(i8: FromBytes, AsBytes, Unaligned);
659+
assert_unaligned!(u8, i8);
654660
unsafe_impl!(u16: FromBytes, AsBytes);
655661
unsafe_impl!(i16: FromBytes, AsBytes);
656662
unsafe_impl!(u32: FromBytes, AsBytes);
@@ -692,6 +698,7 @@ safety_comment! {
692698
///
693699
/// [1] https://doc.rust-lang.org/reference/types/boolean.html
694700
unsafe_impl!(bool: AsBytes, Unaligned);
701+
assert_unaligned!(bool);
695702
}
696703
safety_comment! {
697704
/// SAFETY:
@@ -704,26 +711,41 @@ safety_comment! {
704711
}
705712
safety_comment! {
706713
/// SAFETY:
707-
/// - `AsBytes`: Per the reference [1], `str` has the same layout as `[u8]`,
708-
/// and `[u8]` is `AsBytes`.
714+
/// - `AsBytes`, `Unaligned`: Per the reference [1], `str` has the same
715+
/// layout as `[u8]`, and `[u8]` is `AsBytes` and `Unaligned`.
716+
///
717+
/// Note that we don't `assert_unaligned!(str)` because `assert_unaligned!`
718+
/// uses `align_of`, which only works for `Sized` types.
709719
///
710720
/// [1] https://doc.rust-lang.org/reference/type-layout.html#str-layout
711-
unsafe_impl!(str: AsBytes);
721+
unsafe_impl!(str: AsBytes, Unaligned);
712722
}
713723

714724
safety_comment! {
715725
// `NonZeroXxx` is `AsBytes`, but not `FromBytes`.
716726
//
717727
/// SAFETY:
718-
/// `NonZeroXxx` has the same layout as its associated primitive. Since it
719-
/// is the same size, this guarantees it has no padding - integers have no
720-
/// padding, and there's no room for padding if it can represent all of the
721-
/// same values except 0.
722-
///
728+
/// - `AsBytes`: `NonZeroXxx` has the same layout as its associated
729+
/// primitive. Since it is the same size, this guarantees it has no
730+
/// padding - integers have no padding, and there's no room for padding
731+
/// if it can represent all of the same values except 0.
732+
/// - `Unaligned`: `NonZeroU8` and `NonZeroI8` document that
733+
/// `Option<NonZeroU8>` and `Option<NonZeroI8>` both have size 1. [1] [2]
734+
/// This is worded in a way that makes it unclear whether it's meant as a
735+
/// guarantee, but given the purpose of those types, it's virtually
736+
/// unthinkable that that would ever change. `Option` cannot be smaller
737+
/// than its contained type, which implies that, and `NonZeroX8` are of
738+
/// size 1 or 0. `NonZeroX8` can represent multiple states, so they cannot
739+
/// be 0 bytes, which means that they must be 1 byte. The only valid
740+
/// alignment for a 1-byte type is 1.
741+
///
742+
/// [1] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html
743+
/// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html
723744
/// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation
724745
/// that layout is the same as primitive layout.
725-
unsafe_impl!(NonZeroU8: AsBytes);
726-
unsafe_impl!(NonZeroI8: AsBytes);
746+
unsafe_impl!(NonZeroU8: AsBytes, Unaligned);
747+
unsafe_impl!(NonZeroI8: AsBytes, Unaligned);
748+
assert_unaligned!(NonZeroU8, NonZeroI8);
727749
unsafe_impl!(NonZeroU16: AsBytes);
728750
unsafe_impl!(NonZeroI16: AsBytes);
729751
unsafe_impl!(NonZeroU32: AsBytes);
@@ -735,17 +757,26 @@ safety_comment! {
735757
unsafe_impl!(NonZeroUsize: AsBytes);
736758
unsafe_impl!(NonZeroIsize: AsBytes);
737759
}
738-
739760
safety_comment! {
740761
/// SAFETY:
741-
/// The Rust compiler reuses `0` value to represent `None`, so
742-
/// `size_of::<Option<NonZeroXxx>>() == size_of::<xxx>()`; see `NonZeroXxx`
743-
/// documentation.
762+
/// - `FromBytes`, `AsBytes`: The Rust compiler reuses `0` value to
763+
/// represent `None`, so `size_of::<Option<NonZeroXxx>>() ==
764+
/// size_of::<xxx>()`; see `NonZeroXxx` documentation.
765+
/// - `Unaligned`: `NonZeroU8` and `NonZeroI8` document that
766+
/// `Option<NonZeroU8>` and `Option<NonZeroI8>` both have size 1. [1] [2]
767+
/// This is worded in a way that makes it unclear whether it's meant as a
768+
/// guarantee, but given the purpose of those types, it's virtually
769+
/// unthinkable that that would ever change. The only valid alignment for
770+
/// a 1-byte type is 1.
771+
///
772+
/// [1] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html
773+
/// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html
744774
///
745775
/// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation
746776
/// for layout guarantees.
747-
unsafe_impl!(Option<NonZeroU8>: FromBytes, AsBytes);
748-
unsafe_impl!(Option<NonZeroI8>: FromBytes, AsBytes);
777+
unsafe_impl!(Option<NonZeroU8>: FromBytes, AsBytes, Unaligned);
778+
unsafe_impl!(Option<NonZeroI8>: FromBytes, AsBytes, Unaligned);
779+
assert_unaligned!(Option<NonZeroU8>, Option<NonZeroI8>);
749780
unsafe_impl!(Option<NonZeroU16>: FromBytes, AsBytes);
750781
unsafe_impl!(Option<NonZeroI16>: FromBytes, AsBytes);
751782
unsafe_impl!(Option<NonZeroU32>: FromBytes, AsBytes);
@@ -776,6 +807,7 @@ safety_comment! {
776807
unsafe_impl!(T: ?Sized => FromBytes for PhantomData<T>);
777808
unsafe_impl!(T: ?Sized => AsBytes for PhantomData<T>);
778809
unsafe_impl!(T: ?Sized => Unaligned for PhantomData<T>);
810+
assert_unaligned!(PhantomData<()>, PhantomData<u8>, PhantomData<u64>);
779811
}
780812
safety_comment! {
781813
/// SAFETY:
@@ -789,17 +821,22 @@ safety_comment! {
789821
unsafe_impl!(T: FromBytes => FromBytes for Wrapping<T>);
790822
unsafe_impl!(T: AsBytes => AsBytes for Wrapping<T>);
791823
unsafe_impl!(T: Unaligned => Unaligned for Wrapping<T>);
824+
assert_unaligned!(Wrapping<()>, Wrapping<u8>);
792825
}
793-
794826
safety_comment! {
795827
// `MaybeUninit<T>` is `FromBytes`, but never `AsBytes` since it may contain
796828
// uninitialized bytes.
797829
//
798830
/// SAFETY:
799-
/// `MaybeUninit<T>` has no restrictions on its contents.
831+
/// - `FromBytes`: `MaybeUninit<T>` has no restrictions on its contents.
832+
/// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1]
833+
/// to have the same alignment as `T`.
834+
///
835+
/// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
800836
unsafe_impl!(T => FromBytes for MaybeUninit<T>);
837+
unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit<T>);
838+
assert_unaligned!(MaybeUninit<()>, MaybeUninit<u8>);
801839
}
802-
803840
safety_comment! {
804841
/// SAFETY:
805842
/// `ManuallyDrop` has the same layout as `T`, and accessing the inner value
@@ -818,8 +855,8 @@ safety_comment! {
818855
unsafe_impl!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>);
819856
unsafe_impl!(T: ?Sized + AsBytes => AsBytes for ManuallyDrop<T>);
820857
unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>);
858+
assert_unaligned!(ManuallyDrop<()>, ManuallyDrop<u8>);
821859
}
822-
823860
safety_comment! {
824861
/// SAFETY:
825862
/// Per the reference [1]:
@@ -839,10 +876,14 @@ safety_comment! {
839876
/// since an array/slice has "the same alignment of `T`", `[T]` and `[T; N]`
840877
/// are `Unaligned` if `T` is.
841878
///
879+
/// Note that we don't `assert_unaligned!` for slice types because
880+
/// `assert_unaligned!` uses `align_of`, which only works for `Sized` types.
881+
///
842882
/// [1] https://doc.rust-lang.org/reference/type-layout.html#array-layout
843883
unsafe_impl!(T: FromBytes, const N: usize => FromBytes for [T; N]);
844884
unsafe_impl!(T: AsBytes, const N: usize => AsBytes for [T; N]);
845885
unsafe_impl!(T: Unaligned, const N: usize => Unaligned for [T; N]);
886+
assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]);
846887
unsafe_impl!(T: FromBytes => FromBytes for [T]);
847888
unsafe_impl!(T: AsBytes => AsBytes for [T]);
848889
unsafe_impl!(T: Unaligned => Unaligned for [T]);
@@ -3830,10 +3871,8 @@ mod tests {
38303871
// `!Unaligned` at some point.
38313872
assert_impls!(str: AsBytes, !FromBytes, !Unaligned);
38323873

3833-
// `NonZeroU8/NonZeroI8: Unaligned` is probably sound, so we can
3834-
// probably remove `!Unaligned` at some point.
3835-
assert_impls!(NonZeroU8: AsBytes, !FromBytes, !Unaligned);
3836-
assert_impls!(NonZeroI8: AsBytes, !FromBytes, !Unaligned);
3874+
assert_impls!(NonZeroU8: AsBytes, Unaligned, !FromBytes);
3875+
assert_impls!(NonZeroI8: AsBytes, Unaligned, !FromBytes);
38373876
assert_impls!(NonZeroU16: AsBytes, !FromBytes, !Unaligned);
38383877
assert_impls!(NonZeroI16: AsBytes, !FromBytes, !Unaligned);
38393878
assert_impls!(NonZeroU32: AsBytes, !FromBytes, !Unaligned);
@@ -3845,10 +3884,8 @@ mod tests {
38453884
assert_impls!(NonZeroUsize: AsBytes, !FromBytes, !Unaligned);
38463885
assert_impls!(NonZeroIsize: AsBytes, !FromBytes, !Unaligned);
38473886

3848-
// `Option<NonZeroU8>/Option<NonZeroI8>: Unaligned` is probably sound,
3849-
// so we can probably remove `!Unaligned` at some point.
3850-
assert_impls!(Option<NonZeroU8>: FromBytes, AsBytes, !Unaligned);
3851-
assert_impls!(Option<NonZeroI8>: FromBytes, AsBytes, !Unaligned);
3887+
assert_impls!(Option<NonZeroU8>: FromBytes, AsBytes, Unaligned);
3888+
assert_impls!(Option<NonZeroI8>: FromBytes, AsBytes, Unaligned);
38523889
assert_impls!(Option<NonZeroU16>: FromBytes, AsBytes, !Unaligned);
38533890
assert_impls!(Option<NonZeroI16>: FromBytes, AsBytes, !Unaligned);
38543891
assert_impls!(Option<NonZeroU32>: FromBytes, AsBytes, !Unaligned);
@@ -3871,7 +3908,7 @@ mod tests {
38713908
assert_impls!(ManuallyDrop<NotZerocopy>: !FromBytes, !AsBytes, !Unaligned);
38723909
assert_impls!(ManuallyDrop<[NotZerocopy]>: !FromBytes, !AsBytes, !Unaligned);
38733910

3874-
assert_impls!(MaybeUninit<u8>: FromBytes, !AsBytes, !Unaligned);
3911+
assert_impls!(MaybeUninit<u8>: FromBytes, Unaligned, !AsBytes);
38753912
assert_impls!(MaybeUninit<NotZerocopy>: FromBytes, !AsBytes, !Unaligned);
38763913

38773914
assert_impls!(Wrapping<u8>: FromBytes, AsBytes, Unaligned);

0 commit comments

Comments
 (0)