Forward references to enum types end up generating the wrong type · Issue #3179 · rust-lang/rust-bindgen (original) (raw)

Forward references to enum types end up generating the wrong type for the enum. For instance:

generates:

pub const E_A: E = 0; pub type E = i32;

instead of the expected:

pub const E_A: E = 0; pub type E = ::std::os::raw::c_uint;

If we move it below:

or if we remove it:

then the generated type is the expected one.

Another reproduction case, closer to what we hit in the kernel, was in a return type:

enum E f(void); enum E { A };

In turn, this can mean that big codebases that #include headers from other headers end up in situations that are quite confusing.

For instance, in the Linux kernel, one may end up with a forward reference or not, depending on how the headers are included, which in turn may depend on the kernel configuration or the architecture. In turn, that means that we need to either be careful to avoid those (e.g. adding extra #includes to the top of our bindgen input to bring the actual definitions first), or developers may end up forcing casts and adding #[allow(clippy::unnecessary_cast)] (since the type of the generated constants, like E_A above, depends on the particular configuration):

#[repr(u32)] pub enum HrTimerRestart { #[allow(clippy::unnecessary_cast)] NoRestart = bindings::hrtimer_restart_HRTIMER_NORESTART as u32,

#[allow(clippy::unnecessary_cast)]
Restart = bindings::hrtimer_restart_HRTIMER_RESTART as u32,

}

It is worth noting that GCC and Clang both allow these forward references to enum types and do not complain unless -Wpedantic is used:

warning: ISO C forbids forward references to 'enum' types [-Wpedantic]

Other compilers such as MSVC and TCC allow them, too.

Moreover, to double-check that the type of the actual enum in GCC and Clang is not changing on their side whether there is a forward reference or not, I added the following to the C examples above and it always passes (assuming the compiler picks unsigned int), which contradicts bindgen's output:

_Static_assert(_Generic((enum E)A, unsigned int: 1, default: 0), "");

Finally, I could reproduce the issue with bindgen 0.71.1 and libclang 20.1.1, i.e. latest releases, among others.