Skip to content

Commit 8554b16

Browse files
committed
[macro] Support deriving certain traits via macro
This is intended to specifically support the cryptocorrosion project's ppv-lite86 and groestl-aesni crates. Currently, these crates rely on zerocopy's `derive` feature, which has caused long build times for some dependents such as in [1]. The added macro is `#[doc(hidden)]`, and is only designed to support ppv-lite86 and groestl-aesni. No semver stability guarantees are made except that we will support the use cases of those specific crates. [1] rust-random/rand#1574 (comment) gherrit-pr-id: I6021fbdc0d1e1e30549da129128e5bc002adcee7
1 parent f5cbd3e commit 8554b16

File tree

1 file changed

+388
-0
lines changed

1 file changed

+388
-0
lines changed

src/macros.rs

Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,268 @@ macro_rules! include_value {
733733
};
734734
}
735735

736+
#[doc(hidden)]
737+
#[macro_export]
738+
macro_rules! cryptocorrosion_derive_traits {
739+
(
740+
#[repr($repr:ident)]
741+
$(#[$attr:meta])*
742+
$vis:vis struct $name:ident $(<$($tyvar:ident),*>)?
743+
$(
744+
(
745+
$($tuple_field_vis:vis $tuple_field_ty:ty),*
746+
);
747+
)?
748+
749+
$(
750+
{
751+
$($field_vis:vis $field_name:ident: $field_ty:ty,)*
752+
}
753+
)?
754+
) => {
755+
$crate::cryptocorrosion_derive_traits!(@assert_allowed_struct_repr #[repr($repr)]);
756+
757+
$(#[$attr])*
758+
#[repr($repr)]
759+
$vis struct $name $(<$($tyvar),*>)?
760+
$(
761+
(
762+
$($tuple_field_vis $tuple_field_ty),*
763+
);
764+
)?
765+
766+
$(
767+
{
768+
$($field_vis $field_name: $field_ty,)*
769+
}
770+
)?
771+
772+
// SAFETY: See inline.
773+
unsafe impl $(<$($tyvar),*>)? $crate::TryFromBytes for $name$(<$($tyvar),*>)?
774+
where
775+
$(
776+
$($tuple_field_ty: $crate::FromBytes,)*
777+
)?
778+
779+
$(
780+
$($field_ty: $crate::FromBytes,)*
781+
)?
782+
{
783+
fn is_bit_valid<A>(_c: $crate::Maybe<'_, Self, A>) -> bool
784+
where
785+
A: $crate::pointer::invariant::Reference
786+
{
787+
// SAFETY: This macro only accepts `#[repr(C)]` and
788+
// `#[repr(transparent)]` structs, and this `impl` block
789+
// requires all field types to be `FromBytes`. Thus, all
790+
// initialized byte sequences constitutes valid instances of
791+
// `Self`.
792+
true
793+
}
794+
795+
fn only_derive_is_allowed_to_implement_this_trait() {}
796+
}
797+
798+
// SAFETY: This macro only accepts `#[repr(C)]` and
799+
// `#[repr(transparent)]` structs, and this `impl` block requires all
800+
// field types to be `FromBytes`, which is a sub-trait of `FromZeros`.
801+
unsafe impl $(<$($tyvar),*>)? $crate::FromZeros for $name$(<$($tyvar),*>)?
802+
where
803+
$(
804+
$($tuple_field_ty: $crate::FromBytes,)*
805+
)?
806+
807+
$(
808+
$($field_ty: $crate::FromBytes,)*
809+
)?
810+
{
811+
fn only_derive_is_allowed_to_implement_this_trait() {}
812+
}
813+
814+
// SAFETY: This macro only accepts `#[repr(C)]` and
815+
// `#[repr(transparent)]` structs, and this `impl` block requires all
816+
// field types to be `FromBytes`.
817+
unsafe impl $(<$($tyvar),*>)? $crate::FromBytes for $name$(<$($tyvar),*>)?
818+
where
819+
$(
820+
$($tuple_field_ty: $crate::FromBytes,)*
821+
)?
822+
823+
$(
824+
$($field_ty: $crate::FromBytes,)*
825+
)?
826+
{
827+
fn only_derive_is_allowed_to_implement_this_trait() {}
828+
}
829+
830+
// SAFETY: This macro only accepts `#[repr(C)]` and
831+
// `#[repr(transparent)]` structs, this `impl` block requires all field
832+
// types to be `IntoBytes`, and a padding check is used to ensures that
833+
// there are no padding bytes.
834+
unsafe impl $(<$($tyvar),*>)? $crate::IntoBytes for $name$(<$($tyvar),*>)?
835+
where
836+
$(
837+
$($tuple_field_ty: $crate::IntoBytes,)*
838+
)?
839+
840+
$(
841+
$($field_ty: $crate::IntoBytes,)*
842+
)?
843+
844+
(): $crate::util::macro_util::PaddingFree<
845+
Self,
846+
{
847+
$crate::cryptocorrosion_derive_traits!(
848+
@struct_padding_check #[repr($repr)]
849+
$(($($tuple_field_ty),*))?
850+
$({$($field_ty),*})?
851+
)
852+
},
853+
>,
854+
{
855+
fn only_derive_is_allowed_to_implement_this_trait() {}
856+
}
857+
858+
// SAFETY: This macro only accepts `#[repr(C)]` and
859+
// `#[repr(transparent)]` structs, and this `impl` block requires all
860+
// field types to be `Immutable`.
861+
unsafe impl $(<$($tyvar),*>)? $crate::Immutable for $name$(<$($tyvar),*>)?
862+
where
863+
$(
864+
$($tuple_field_ty: $crate::Immutable,)*
865+
)?
866+
867+
$(
868+
$($field_ty: $crate::Immutable,)*
869+
)?
870+
{
871+
fn only_derive_is_allowed_to_implement_this_trait() {}
872+
}
873+
};
874+
(@assert_allowed_struct_repr #[repr(transparent)]) => {};
875+
(@assert_allowed_struct_repr #[repr(C)]) => {};
876+
(@assert_allowed_struct_repr #[$_attr:meta]) => {
877+
compile_error!("repr must be `#[repr(transparent)]` or `#[repr(C)]`");
878+
};
879+
(
880+
@struct_padding_check #[repr(transparent)]
881+
$(($($tuple_field_ty:ty),*))?
882+
$({$($field_ty:ty),*})?
883+
) => {
884+
// SAFETY: `#[repr(transparent)]` structs cannot have the same layout as
885+
// their single non-zero-sized field, and so cannot have any padding
886+
// outside of that field.
887+
false
888+
};
889+
(
890+
@struct_padding_check #[repr(C)]
891+
$(($($tuple_field_ty:ty),*))?
892+
$({$($field_ty:ty),*})?
893+
) => {
894+
$crate::struct_has_padding!(
895+
Self,
896+
[
897+
$($($tuple_field_ty),*)?
898+
$($($field_ty),*)?
899+
]
900+
)
901+
};
902+
(
903+
#[repr(C)]
904+
$(#[$attr:meta])*
905+
$vis:vis union $name:ident {
906+
$(
907+
$field_name:ident: $field_ty:ty,
908+
)*
909+
}
910+
) => {
911+
$(#[$attr])*
912+
#[repr(C)]
913+
$vis union $name {
914+
$(
915+
$field_name: $field_ty,
916+
)*
917+
}
918+
919+
// SAFETY: See inline.
920+
unsafe impl $crate::TryFromBytes for $name
921+
where
922+
$(
923+
$field_ty: $crate::FromBytes,
924+
)*
925+
{
926+
fn is_bit_valid<A>(_c: $crate::Maybe<'_, Self, A>) -> bool
927+
where
928+
A: $crate::pointer::invariant::Reference
929+
{
930+
// SAFETY: This macro only accepts `#[repr(C)]` unions, and this
931+
// `impl` block requires all field types to be `FromBytes`.
932+
// Thus, all initialized byte sequences constitutes valid
933+
// instances of `Self`.
934+
true
935+
}
936+
937+
fn only_derive_is_allowed_to_implement_this_trait() {}
938+
}
939+
940+
// SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
941+
// block requires all field types to be `FromBytes`, which is a
942+
// sub-trait of `FromZeros`.
943+
unsafe impl $crate::FromZeros for $name
944+
where
945+
$(
946+
$field_ty: $crate::FromBytes,
947+
)*
948+
{
949+
fn only_derive_is_allowed_to_implement_this_trait() {}
950+
}
951+
952+
// SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
953+
// block requires all field types to be `FromBytes`.
954+
unsafe impl $crate::FromBytes for $name
955+
where
956+
$(
957+
$field_ty: $crate::FromBytes,
958+
)*
959+
{
960+
fn only_derive_is_allowed_to_implement_this_trait() {}
961+
}
962+
963+
// SAFETY: This macro only accepts `#[repr(C)]` unions, this `impl`
964+
// block requires all field types to be `IntoBytes`, and a padding check
965+
// is used to ensures that there are no padding bytes before or after
966+
// any field.
967+
unsafe impl $crate::IntoBytes for $name
968+
where
969+
$(
970+
$field_ty: $crate::IntoBytes,
971+
)*
972+
(): $crate::util::macro_util::PaddingFree<
973+
Self,
974+
{
975+
$crate::union_has_padding!(
976+
Self,
977+
[$($field_ty),*]
978+
)
979+
},
980+
>,
981+
{
982+
fn only_derive_is_allowed_to_implement_this_trait() {}
983+
}
984+
985+
// SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
986+
// block requires all field types to be `Immutable`.
987+
unsafe impl $crate::Immutable for $name
988+
where
989+
$(
990+
$field_ty: $crate::Immutable,
991+
)*
992+
{
993+
fn only_derive_is_allowed_to_implement_this_trait() {}
994+
}
995+
};
996+
}
997+
736998
#[cfg(test)]
737999
mod tests {
7381000
use crate::util::testutil::*;
@@ -990,4 +1252,130 @@ mod tests {
9901252
const AS_I32: i32 = include_value!("../testdata/include_value/data");
9911253
assert_eq!(AS_I32, i32::from_ne_bytes([b'a', b'b', b'c', b'd']));
9921254
}
1255+
1256+
#[test]
1257+
#[allow(non_camel_case_types)]
1258+
fn test_cryptocorrosion_derive_traits() {
1259+
// Test the set of invocations added in
1260+
// https://github.com/cryptocorrosion/cryptocorrosion/pull/85
1261+
1262+
fn assert_impls<T: FromBytes + IntoBytes + Immutable>() {}
1263+
1264+
cryptocorrosion_derive_traits! {
1265+
#[repr(C)]
1266+
#[derive(Clone, Copy)]
1267+
pub union vec128_storage {
1268+
d: [u32; 4],
1269+
q: [u64; 2],
1270+
}
1271+
}
1272+
1273+
assert_impls::<vec128_storage>();
1274+
1275+
cryptocorrosion_derive_traits! {
1276+
#[repr(transparent)]
1277+
#[derive(Copy, Clone, Debug, PartialEq)]
1278+
pub struct u32x4_generic([u32; 4]);
1279+
}
1280+
1281+
assert_impls::<u32x4_generic>();
1282+
1283+
cryptocorrosion_derive_traits! {
1284+
#[repr(transparent)]
1285+
#[derive(Copy, Clone, Debug, PartialEq)]
1286+
pub struct u64x2_generic([u64; 2]);
1287+
}
1288+
1289+
assert_impls::<u64x2_generic>();
1290+
1291+
cryptocorrosion_derive_traits! {
1292+
#[repr(transparent)]
1293+
#[derive(Copy, Clone, Debug, PartialEq)]
1294+
pub struct u128x1_generic([u128; 1]);
1295+
}
1296+
1297+
assert_impls::<u128x1_generic>();
1298+
1299+
cryptocorrosion_derive_traits! {
1300+
#[repr(transparent)]
1301+
#[derive(Copy, Clone, Default)]
1302+
#[allow(non_camel_case_types)]
1303+
pub struct x2<W, G>(pub [W; 2], PhantomData<G>);
1304+
}
1305+
1306+
enum NotZerocopy {}
1307+
assert_impls::<x2<(), NotZerocopy>>();
1308+
1309+
cryptocorrosion_derive_traits! {
1310+
#[repr(transparent)]
1311+
#[derive(Copy, Clone, Default)]
1312+
#[allow(non_camel_case_types)]
1313+
pub struct x4<W>(pub [W; 4]);
1314+
}
1315+
1316+
assert_impls::<x4<()>>();
1317+
1318+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1319+
{
1320+
use core::arch::x86::{__m128i, __m256i};
1321+
1322+
cryptocorrosion_derive_traits! {
1323+
#[repr(C)]
1324+
#[derive(Copy, Clone)]
1325+
pub struct X4(__m128i, __m128i, __m128i, __m128i);
1326+
}
1327+
1328+
assert_impls::<X4>();
1329+
1330+
cryptocorrosion_derive_traits! {
1331+
#[repr(C)]
1332+
/// Generic wrapper for unparameterized storage of any of the possible impls.
1333+
/// Converting into and out of this type should be essentially free, although it may be more
1334+
/// aligned than a particular impl requires.
1335+
#[allow(non_camel_case_types)]
1336+
#[derive(Copy, Clone)]
1337+
pub union vec128_storage {
1338+
u32x4: [u32; 4],
1339+
u64x2: [u64; 2],
1340+
u128x1: [u128; 1],
1341+
sse2: __m128i,
1342+
}
1343+
}
1344+
1345+
assert_impls::<vec128_storage>();
1346+
1347+
cryptocorrosion_derive_traits! {
1348+
#[repr(transparent)]
1349+
#[allow(non_camel_case_types)]
1350+
#[derive(Copy, Clone)]
1351+
pub struct vec<S3, S4, NI> {
1352+
x: __m128i,
1353+
s3: PhantomData<S3>,
1354+
s4: PhantomData<S4>,
1355+
ni: PhantomData<NI>,
1356+
}
1357+
}
1358+
1359+
assert_impls::<vec<NotZerocopy, NotZerocopy, NotZerocopy>>();
1360+
1361+
cryptocorrosion_derive_traits! {
1362+
#[repr(transparent)]
1363+
#[derive(Copy, Clone)]
1364+
pub struct u32x4x2_avx2<NI> {
1365+
x: __m256i,
1366+
ni: PhantomData<NI>,
1367+
}
1368+
}
1369+
1370+
assert_impls::<u32x4x2_avx2<NotZerocopy>>();
1371+
}
1372+
1373+
// Make sure that our derive works for `#[repr(C)]` structs even though
1374+
// cryptocorrosion doesn't currently have any.
1375+
cryptocorrosion_derive_traits! {
1376+
#[repr(C)]
1377+
#[derive(Copy, Clone, Debug, PartialEq)]
1378+
pub struct ReprC(u8, u8, u16);
1379+
}
1380+
}
9931381
}

0 commit comments

Comments
 (0)