@@ -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
//
@@ -325,3 +368,76 @@ macro_rules! __item {
325
368
$i
326
369
} ;
327
370
}
371
+
372
+ #[ cfg( test) ]
373
+ mod tests {
374
+ #[ test]
375
+ fn c_enumbasic ( ) {
376
+ // By default, variants get sequential values.
377
+ c_enum ! {
378
+ e {
379
+ VAR0 ,
380
+ VAR1 ,
381
+ VAR2 ,
382
+ }
383
+ }
384
+
385
+ assert_eq ! ( VAR0 , 0_u32 ) ;
386
+ assert_eq ! ( VAR1 , 1_u32 ) ;
387
+ assert_eq ! ( VAR2 , 2_u32 ) ;
388
+ }
389
+
390
+ #[ test]
391
+ fn c_enumrepr ( ) {
392
+ // By default, variants get sequential values.
393
+ c_enum ! {
394
+ #[ repr( u16 ) ]
395
+ e {
396
+ VAR0 ,
397
+ }
398
+ }
399
+
400
+ assert_eq ! ( VAR0 , 0_u16 ) ;
401
+ }
402
+
403
+ #[ test]
404
+ fn c_enumset_value ( ) {
405
+ // Setting an explicit value resets the count.
406
+ c_enum ! {
407
+ e {
408
+ VAR2 = 2 ,
409
+ VAR3 ,
410
+ VAR4 ,
411
+ }
412
+ }
413
+
414
+ assert_eq ! ( VAR2 , 2_u32 ) ;
415
+ assert_eq ! ( VAR3 , 3_u32 ) ;
416
+ assert_eq ! ( VAR4 , 4_u32 ) ;
417
+ }
418
+
419
+ #[ test]
420
+ fn c_enummultiple_set_value ( ) {
421
+ // C enums always take one more than the previous value, unless set to a specific
422
+ // value. Duplicates are allowed.
423
+ c_enum ! {
424
+ e {
425
+ VAR0 ,
426
+ VAR2_0 = 2 ,
427
+ VAR3_0 ,
428
+ VAR4_0 ,
429
+ VAR2_1 = 2 ,
430
+ VAR3_1 ,
431
+ VAR4_1 ,
432
+ }
433
+ }
434
+
435
+ assert_eq ! ( VAR0 , 0_u32 ) ;
436
+ assert_eq ! ( VAR2_0 , 2_u32 ) ;
437
+ assert_eq ! ( VAR3_0 , 3_u32 ) ;
438
+ assert_eq ! ( VAR4_0 , 4_u32 ) ;
439
+ assert_eq ! ( VAR2_1 , 2_u32 ) ;
440
+ assert_eq ! ( VAR3_1 , 3_u32 ) ;
441
+ assert_eq ! ( VAR4_1 , 4_u32 ) ;
442
+ }
443
+ }
0 commit comments