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