@@ -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// 
@@ -325,3 +368,76 @@ macro_rules! __item {
325368        $i
326369    } ; 
327370} 
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