@@ -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