@@ -193,6 +193,7 @@ macro_rules! missing {
193
193
194
194
/// Implement `Clone` and `Copy` for an enum, as well as `Debug`, `Eq`, `Hash`, and
195
195
/// `PartialEq` if the `extra_traits` feature is enabled.
196
+ // FIXME(#4419): Replace all uses of `e!` with `c_enum!`
196
197
macro_rules! e {
197
198
( $(
198
199
$( #[ $attr: meta] ) *
@@ -210,6 +211,48 @@ macro_rules! e {
210
211
) * ) ;
211
212
}
212
213
214
+ /// Represent a C enum as Rust constants and a type.
215
+ ///
216
+ /// C enums can't soundly be mapped to Rust enums since C enums are allowed to have duplicates or
217
+ /// unlisted values, but this is UB in Rust. This enum doesn't implement any traits, its main
218
+ /// purpose is to calculate the correct enum values.
219
+ ///
220
+ /// See <https://github.com/rust-lang/libc/issues/4419> for more.
221
+ macro_rules! c_enum {
222
+ (
223
+ $( #[ repr( $repr: ty) ] ) ?
224
+ $ty_name: ident {
225
+ $( $variant: ident $( = $value: literal) ?, ) +
226
+ }
227
+ ) => {
228
+ pub type $ty_name = c_enum!( @ty $( $repr) ?) ;
229
+ c_enum!( @one; $ty_name; 0 ; $( $variant $( = $value) ?, ) +) ;
230
+ } ;
231
+
232
+ // Matcher for a single variant
233
+ ( @one; $_ty_name: ident; $_idx: expr; ) => { } ;
234
+ (
235
+ @one; $ty_name: ident; $default_val: expr;
236
+ $variant: ident $( = $value: literal) ?,
237
+ $( $tail: tt) *
238
+ ) => {
239
+ pub const $variant: $ty_name = {
240
+ #[ allow( unused_variables) ]
241
+ let r = $default_val;
242
+ $( let r = $value; ) ?
243
+ r
244
+ } ;
245
+
246
+ // The next value is always one more than the previous value, unless
247
+ // set explicitly.
248
+ c_enum!( @one; $ty_name; $variant + 1 ; $( $tail) * ) ;
249
+ } ;
250
+
251
+ // Use a specific type if provided, otherwise default to `c_uint`
252
+ ( @ty $repr: ty) => { $repr } ;
253
+ ( @ty) => { $crate:: c_uint } ;
254
+ }
255
+
213
256
// This is a pretty horrible hack to allow us to conditionally mark some functions as 'const',
214
257
// without requiring users of this macro to care "libc_const_extern_fn".
215
258
//
@@ -359,3 +402,76 @@ macro_rules! deprecated_mach {
359
402
) *
360
403
}
361
404
}
405
+
406
+ #[ cfg( test) ]
407
+ mod tests {
408
+ #[ test]
409
+ fn c_enumbasic ( ) {
410
+ // By default, variants get sequential values.
411
+ c_enum ! {
412
+ e {
413
+ VAR0 ,
414
+ VAR1 ,
415
+ VAR2 ,
416
+ }
417
+ }
418
+
419
+ assert_eq ! ( VAR0 , 0_u32 ) ;
420
+ assert_eq ! ( VAR1 , 1_u32 ) ;
421
+ assert_eq ! ( VAR2 , 2_u32 ) ;
422
+ }
423
+
424
+ #[ test]
425
+ fn c_enumrepr ( ) {
426
+ // By default, variants get sequential values.
427
+ c_enum ! {
428
+ #[ repr( u16 ) ]
429
+ e {
430
+ VAR0 ,
431
+ }
432
+ }
433
+
434
+ assert_eq ! ( VAR0 , 0_u16 ) ;
435
+ }
436
+
437
+ #[ test]
438
+ fn c_enumset_value ( ) {
439
+ // Setting an explicit value resets the count.
440
+ c_enum ! {
441
+ e {
442
+ VAR2 = 2 ,
443
+ VAR3 ,
444
+ VAR4 ,
445
+ }
446
+ }
447
+
448
+ assert_eq ! ( VAR2 , 2_u32 ) ;
449
+ assert_eq ! ( VAR3 , 3_u32 ) ;
450
+ assert_eq ! ( VAR4 , 4_u32 ) ;
451
+ }
452
+
453
+ #[ test]
454
+ fn c_enummultiple_set_value ( ) {
455
+ // C enums always take one more than the previous value, unless set to a specific
456
+ // value. Duplicates are allowed.
457
+ c_enum ! {
458
+ e {
459
+ VAR0 ,
460
+ VAR2_0 = 2 ,
461
+ VAR3_0 ,
462
+ VAR4_0 ,
463
+ VAR2_1 = 2 ,
464
+ VAR3_1 ,
465
+ VAR4_1 ,
466
+ }
467
+ }
468
+
469
+ assert_eq ! ( VAR0 , 0_u32 ) ;
470
+ assert_eq ! ( VAR2_0 , 2_u32 ) ;
471
+ assert_eq ! ( VAR3_0 , 3_u32 ) ;
472
+ assert_eq ! ( VAR4_0 , 4_u32 ) ;
473
+ assert_eq ! ( VAR2_1 , 2_u32 ) ;
474
+ assert_eq ! ( VAR3_1 , 3_u32 ) ;
475
+ assert_eq ! ( VAR4_1 , 4_u32 ) ;
476
+ }
477
+ }
0 commit comments