@@ -193,6 +193,7 @@ macro_rules! missing {
193193
194194/// Implement `Clone` and `Copy` for an enum, as well as `Debug`, `Eq`, `Hash`, and 
195195/// `PartialEq` if the `extra_traits` feature is enabled. 
196+ // FIXME(#4419): Replace all uses of `e!` with `c_enum!` 
196197macro_rules!  e { 
197198    ( $( 
198199        $( #[ $attr: meta] ) * 
@@ -210,6 +211,48 @@ macro_rules! e {
210211    ) * ) ; 
211212} 
212213
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+ 
213256// This is a pretty horrible hack to allow us to conditionally mark some functions as 'const', 
214257// without requiring users of this macro to care "libc_const_extern_fn". 
215258// 
@@ -359,3 +402,76 @@ macro_rules! deprecated_mach {
359402        ) * 
360403    } 
361404} 
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