@@ -259,6 +259,32 @@ macro_rules! align_of {
259
259
} } ;
260
260
}
261
261
262
+ mod size_to_tag {
263
+ pub trait SizeToTag < const SIZE : usize > {
264
+ type Tag ;
265
+ }
266
+
267
+ impl SizeToTag < 1 > for ( ) {
268
+ type Tag = u8 ;
269
+ }
270
+ impl SizeToTag < 2 > for ( ) {
271
+ type Tag = u16 ;
272
+ }
273
+ impl SizeToTag < 4 > for ( ) {
274
+ type Tag = u32 ;
275
+ }
276
+ impl SizeToTag < 8 > for ( ) {
277
+ type Tag = u64 ;
278
+ }
279
+ impl SizeToTag < 16 > for ( ) {
280
+ type Tag = u128 ;
281
+ }
282
+ }
283
+
284
+ /// An alias for the unsigned integer of the given size in bytes.
285
+ #[ doc( hidden) ]
286
+ pub type SizeToTag < const SIZE : usize > = <( ) as size_to_tag:: SizeToTag < SIZE > >:: Tag ;
287
+
262
288
/// Does the struct type `$t` have padding?
263
289
///
264
290
/// `$ts` is the list of the type of every field in `$t`. `$t` must be a
@@ -274,7 +300,7 @@ macro_rules! align_of {
274
300
#[ doc( hidden) ] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
275
301
#[ macro_export]
276
302
macro_rules! struct_has_padding {
277
- ( $t: ty, $( $ts: ty) ,* ) => {
303
+ ( $t: ty, [ $( $ts: ty) ,* ] ) => {
278
304
:: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$t>( ) > 0 $( + :: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$ts>( ) ) *
279
305
} ;
280
306
}
@@ -294,11 +320,41 @@ macro_rules! struct_has_padding {
294
320
#[ doc( hidden) ] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
295
321
#[ macro_export]
296
322
macro_rules! union_has_padding {
297
- ( $t: ty, $( $ts: ty) ,* ) => {
323
+ ( $t: ty, [ $( $ts: ty) ,* ] ) => {
298
324
false $( || :: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$t>( ) != :: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$ts>( ) ) *
299
325
} ;
300
326
}
301
327
328
+ /// Does the enum type `$t` have padding?
329
+ ///
330
+ /// `$disc` is the type of the enum tag, and `$ts` is a list of fields in each
331
+ /// square-bracket-delimited variant. `$t` must be an enum, or else
332
+ /// `enum_has_padding!`'s result may be meaningless. An enum has padding if any
333
+ /// of its variant structs [1][2] contain padding, and so all of the variants of
334
+ /// an enum must be "full" in order for the enum to not have padding.
335
+ ///
336
+ /// The results of `enum_has_padding!` require that the enum is not
337
+ /// `repr(Rust)`, as `repr(Rust)` enums may niche the enum's tag and reduce the
338
+ /// total number of bytes required to represent the enum as a result. As long as
339
+ /// the enum is `repr(C)`, `repr(int)`, or `repr(C, int)`, this will
340
+ /// consistently return whether the enum contains any padding bytes.
341
+ ///
342
+ /// [1]: https://doc.rust-lang.org/1.81.0/reference/type-layout.html#reprc-enums-with-fields
343
+ /// [2]: https://doc.rust-lang.org/1.81.0/reference/type-layout.html#primitive-representation-of-enums-with-fields
344
+ #[ doc( hidden) ] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
345
+ #[ macro_export]
346
+ macro_rules! enum_has_padding {
347
+ ( $t: ty, $disc: ty, $( [ $( $ts: ty) ,* ] ) ,* ) => {
348
+ false $(
349
+ || :: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$t>( )
350
+ != (
351
+ :: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$disc>( )
352
+ $( + :: zerocopy:: util:: macro_util:: core_reexport:: mem:: size_of:: <$ts>( ) ) *
353
+ )
354
+ ) *
355
+ }
356
+ }
357
+
302
358
/// Does `t` have alignment greater than or equal to `u`? If not, this macro
303
359
/// produces a compile error. It must be invoked in a dead codepath. This is
304
360
/// used in `transmute_ref!` and `transmute_mut!`.
@@ -810,6 +866,36 @@ mod tests {
810
866
*/
811
867
}
812
868
869
+ #[ test]
870
+ fn test_enum_casts ( ) {
871
+ // Test that casting the variants of enums with signed integer reprs to
872
+ // unsigned integers obeys expected signed -> unsigned casting rules.
873
+
874
+ #[ repr( i8 ) ]
875
+ enum ReprI8 {
876
+ MinusOne = -1 ,
877
+ Zero = 0 ,
878
+ Min = i8:: MIN ,
879
+ Max = i8:: MAX ,
880
+ }
881
+
882
+ #[ allow( clippy:: as_conversions) ]
883
+ let x = ReprI8 :: MinusOne as u8 ;
884
+ assert_eq ! ( x, u8 :: MAX ) ;
885
+
886
+ #[ allow( clippy:: as_conversions) ]
887
+ let x = ReprI8 :: Zero as u8 ;
888
+ assert_eq ! ( x, 0 ) ;
889
+
890
+ #[ allow( clippy:: as_conversions) ]
891
+ let x = ReprI8 :: Min as u8 ;
892
+ assert_eq ! ( x, 128 ) ;
893
+
894
+ #[ allow( clippy:: as_conversions) ]
895
+ let x = ReprI8 :: Max as u8 ;
896
+ assert_eq ! ( x, 127 ) ;
897
+ }
898
+
813
899
#[ test]
814
900
fn test_struct_has_padding ( ) {
815
901
// Test that, for each provided repr, `struct_has_padding!` reports the
@@ -818,7 +904,7 @@ mod tests {
818
904
( #[ $cfg: meta] ( $( $ts: ty) ,* ) => $expect: expr) => { {
819
905
#[ $cfg]
820
906
struct Test ( $( #[ allow( dead_code) ] $ts) ,* ) ;
821
- assert_eq!( struct_has_padding!( Test , $( $ts) ,* ) , $expect) ;
907
+ assert_eq!( struct_has_padding!( Test , [ $( $ts) ,* ] ) , $expect) ;
822
908
} } ;
823
909
( #[ $cfg: meta] $( #[ $cfgs: meta] ) * ( $( $ts: ty) ,* ) => $expect: expr) => {
824
910
test!( #[ $cfg] ( $( $ts) ,* ) => $expect) ;
@@ -849,7 +935,7 @@ mod tests {
849
935
#[ $cfg]
850
936
#[ allow( unused) ] // fields are never read
851
937
union Test { $( $fs: $ts) ,* }
852
- assert_eq!( union_has_padding!( Test , $( $ts) ,* ) , $expect) ;
938
+ assert_eq!( union_has_padding!( Test , [ $( $ts) ,* ] ) , $expect) ;
853
939
} } ;
854
940
( #[ $cfg: meta] $( #[ $cfgs: meta] ) * { $( $fs: ident: $ts: ty) ,* } => $expect: expr) => {
855
941
test!( #[ $cfg] { $( $fs: $ts) ,* } => $expect) ;
@@ -867,4 +953,78 @@ mod tests {
867
953
// anyway.
868
954
test ! ( #[ repr( C ) ] #[ repr( packed) ] { a: u8 , b: u64 } => true ) ;
869
955
}
956
+
957
+ #[ test]
958
+ fn test_enum_has_padding ( ) {
959
+ // Test that, for each provided repr, `enum_has_padding!` reports the
960
+ // expected value.
961
+ macro_rules! test {
962
+ ( #[ repr( $disc: ident $( , $c: ident) ?) ] { $( $vs: ident ( $( $ts: ty) ,* ) , ) * } => $expect: expr) => {
963
+ test!( @case #[ repr( $disc $( , $c) ?) ] { $( $vs ( $( $ts) ,* ) , ) * } => $expect) ;
964
+ } ;
965
+ ( #[ repr( $disc: ident $( , $c: ident) ?) ] #[ $cfg: meta] $( #[ $cfgs: meta] ) * { $( $vs: ident ( $( $ts: ty) ,* ) , ) * } => $expect: expr) => {
966
+ test!( @case #[ repr( $disc $( , $c) ?) ] #[ $cfg] { $( $vs ( $( $ts) ,* ) , ) * } => $expect) ;
967
+ test!( #[ repr( $disc $( , $c) ?) ] $( #[ $cfgs] ) * { $( $vs ( $( $ts) ,* ) , ) * } => $expect) ;
968
+ } ;
969
+ ( @case #[ repr( $disc: ident $( , $c: ident) ?) ] $( #[ $cfg: meta] ) ? { $( $vs: ident ( $( $ts: ty) ,* ) , ) * } => $expect: expr) => { {
970
+ #[ repr( $disc $( , $c) ?) ]
971
+ $( #[ $cfg] ) ?
972
+ #[ allow( unused) ] // variants and fields are never used
973
+ enum Test {
974
+ $( $vs ( $( $ts) ,* ) , ) *
975
+ }
976
+ assert_eq!(
977
+ enum_has_padding!( Test , $disc, $( [ $( $ts) ,* ] ) ,* ) ,
978
+ $expect
979
+ ) ;
980
+ } } ;
981
+ }
982
+
983
+ #[ allow( unused) ]
984
+ #[ repr( align( 2 ) ) ]
985
+ struct U16 ( u16 ) ;
986
+
987
+ #[ allow( unused) ]
988
+ #[ repr( align( 4 ) ) ]
989
+ struct U32 ( u32 ) ;
990
+
991
+ test ! ( #[ repr( u8 ) ] #[ repr( C ) ] {
992
+ A ( u8 ) ,
993
+ } => false ) ;
994
+ test ! ( #[ repr( u16 ) ] #[ repr( C ) ] {
995
+ A ( u8 , u8 ) ,
996
+ B ( U16 ) ,
997
+ } => false ) ;
998
+ test ! ( #[ repr( u32 ) ] #[ repr( C ) ] {
999
+ A ( u8 , u8 , u8 , u8 ) ,
1000
+ B ( U16 , u8 , u8 ) ,
1001
+ C ( u8 , u8 , U16 ) ,
1002
+ D ( U16 , U16 ) ,
1003
+ E ( U32 ) ,
1004
+ } => false ) ;
1005
+
1006
+ // `repr(int)` can pack the discriminant more efficiently
1007
+ test ! ( #[ repr( u8 ) ] {
1008
+ A ( u8 , U16 ) ,
1009
+ } => false ) ;
1010
+ test ! ( #[ repr( u8 ) ] {
1011
+ A ( u8 , U16 , U32 ) ,
1012
+ } => false ) ;
1013
+
1014
+ // `repr(C)` cannot
1015
+ test ! ( #[ repr( u8 , C ) ] {
1016
+ A ( u8 , U16 ) ,
1017
+ } => true ) ;
1018
+ test ! ( #[ repr( u8 , C ) ] {
1019
+ A ( u8 , u8 , u8 , U32 ) ,
1020
+ } => true ) ;
1021
+
1022
+ // And field ordering can always cause problems
1023
+ test ! ( #[ repr( u8 ) ] #[ repr( C ) ] {
1024
+ A ( U16 , u8 ) ,
1025
+ } => true ) ;
1026
+ test ! ( #[ repr( u8 ) ] #[ repr( C ) ] {
1027
+ A ( U32 , u8 , u8 , u8 ) ,
1028
+ } => true ) ;
1029
+ }
870
1030
}
0 commit comments