Skip to content

Add positive and negative trait impl tests for SIMD types #130

@joshlf

Description

@joshlf

We have tests to ensure that our implementations of FromBytes, AsBytes, and Unaligned for various primitive does not regress:

zerocopy/src/lib.rs

Lines 4512 to 4517 in 3bb9a54

assert_impls!((): FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(u8: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(i8: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(u16: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(i16: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(u32: FromZeroes, FromBytes, AsBytes, !Unaligned);

We should have similar tests for our implementations (or lack thereof) for SIMD types.

Mentorship Instructions

To do this, we will be adding tests at the end of test_impls:

zerocopy/src/lib.rs

Lines 4488 to 4584 in b083f1f

#[test]
fn test_impls() {
// Asserts that `$ty` implements any `$trait` and doesn't implement any
// `!$trait`. Note that all `$trait`s must come before any `!$trait`s.
macro_rules! assert_impls {
($ty:ty: $trait:ident) => {
#[allow(dead_code)]
const _: () = { static_assertions::assert_impl_all!($ty: $trait); };
};
($ty:ty: !$trait:ident) => {
#[allow(dead_code)]
const _: () = { static_assertions::assert_not_impl_any!($ty: $trait); };
};
($ty:ty: $($trait:ident),* $(,)? $(!$negative_trait:ident),*) => {
$(
assert_impls!($ty: $trait);
)*
$(
assert_impls!($ty: !$negative_trait);
)*
};
}
assert_impls!((): FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(u8: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(i8: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(u16: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(i16: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(u32: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(i32: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(u64: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(i64: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(u128: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(i128: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(usize: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(isize: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(f32: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(f64: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(bool: FromZeroes, AsBytes, Unaligned, !FromBytes);
assert_impls!(char: FromZeroes, AsBytes, !FromBytes, !Unaligned);
assert_impls!(str: FromZeroes, AsBytes, Unaligned, !FromBytes);
assert_impls!(NonZeroU8: AsBytes, Unaligned, !FromZeroes, !FromBytes);
assert_impls!(NonZeroI8: AsBytes, Unaligned, !FromZeroes, !FromBytes);
assert_impls!(NonZeroU16: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroI16: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroU32: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroI32: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroU64: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroI64: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroU128: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroI128: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroUsize: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(NonZeroIsize: AsBytes, !FromZeroes, !FromBytes, !Unaligned);
assert_impls!(Option<NonZeroU8>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(Option<NonZeroI8>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(Option<NonZeroU16>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroI16>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroU32>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroI32>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroU64>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroI64>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroU128>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroI128>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroUsize>: FromZeroes, FromBytes, AsBytes, !Unaligned);
assert_impls!(Option<NonZeroIsize>: FromZeroes, FromBytes, AsBytes, !Unaligned);
// Implements none of the ZC traits.
struct NotZerocopy;
assert_impls!(PhantomData<NotZerocopy>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(PhantomData<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(ManuallyDrop<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(ManuallyDrop<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(ManuallyDrop<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!(ManuallyDrop<[NotZerocopy]>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!(MaybeUninit<u8>: FromZeroes, FromBytes, Unaligned, !AsBytes);
assert_impls!(MaybeUninit<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!(Wrapping<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(Wrapping<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!(Unalign<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!(Unalign<NotZerocopy>: Unaligned, !FromZeroes, !FromBytes, !AsBytes);
assert_impls!([u8]: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!([NotZerocopy]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!([u8; 0]: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!([NotZerocopy; 0]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!([u8; 1]: FromZeroes, FromBytes, AsBytes, Unaligned);
assert_impls!([NotZerocopy; 1]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
}

At the bottom of that test function, create a new block annotated with #[cfg(simd)]; e.g.:

#[cfg(feature = "simd")]
{
    /* everything else will go in this block */
}

Then, add a macro definition like this:

/// Tests that the given SIMD types (belonging to some given architecture)
/// implement exactly the expected set of traits.
macro_rules! test_simd_arch_mod {
    ($arch:ident, $($typ:ident),*) => {
        {
            use core::arch::$arch::{$($typ),*};
            use crate::*;
            $( assert_impls!($ty: KnownLayout, FromZeroes, FromBytes, AsBytes, !Unaligned); )*
        }
    };
}

Finally, invoke the macro for each of the SIMD-supporting architectures (see here); e.g.:

#[cfg(target_arch = "x86")]
test_simd_arch_mod!(x86, __m128, __m128d, __m128i, __m256, __m256d, __m256i);
#[cfg(target_arch = "x86_64")]
test_simd_arch_mod!(x86_64, __m128, __m128d, __m128i, __m256, __m256d, __m256i);

/* and so on */

Metadata

Metadata

Assignees

Labels

compatibility-nonbreakingChanges that are (likely to be) non-breakingexperience-easyThis issue is easy, and shouldn't require much experiencehelp wantedExtra attention is needed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions