Generating enum classes doesn't annotate the C-equivalent tag type
This issue happened on gcc-arm-none-eabi-10.3-2021.10.
When you annotate a Rust type like:
#[repr(C)]
pub enum Error {
A,
B
}
cbindgen generates the following for C++:
enum class Error {
A,
B,
};
However, this is not necessarily correct. cbindgen needs to specify the type of the enum class using the same method that C does. cbindgen must be aware of how to generate the appropriate C representation, so it just needs to ensure that the enum class has the same layout. On the platform I am building for, this is apparently different between the enum class and C style enums.
What it should look like:
enum class Error: <c enum type here> {
A,
B,
};
As a workaround, I use #[repr(usize)] and #[repr(C, usize)]. cbindgen automatically annotates the correct type for the enum classes in this case.
Let me know if any more info is required.
However, this is not necessarily correct. cbindgen needs to specify the type of the enum class using the same method that C does.
Can you elaborate? C uses int representation by default, IIRC, what do you do to make that layout not match?
However, this is not necessarily correct. cbindgen needs to specify the type of the enum class using the same method that C does.
Can you elaborate? C uses
intrepresentation by default, IIRC, what do you do to make that layout not match?
For some reason, with the example above, it doesn't match the ABI unless I add , usize after C. I am doing nothing other than linking it in via staticlib. If you would like, I can get the exact size of the field with and without that annotation in Rust on the C++ side. It is possible that the compiler I am using has been modified. It is part of the Renesas FSP.
Can you printf("size: %zu\n", sizeof(Error)); on your target? Are you compiling with flags that shrink the enum or something? I've remember seeing such flags at some point
I am on an embedded platform, so I can't currently printf easily, but I will send the data over a serial port.
I got a sizeof(Error) of 4 with #[repr(usize)]. I got a sizeof(Error) of 4 with #[repr(C)] as well.
My C flags: -D_RA_CORE=CM23 -D_RENESAS_RA_ <include dirs> -std=c99
My C++ flags: -D_RA_CORE=CM23 -D_RENESAS_RA_ <include dirs> -std=c++11 -fabi-version=0
My linker flags: -T "fsp.ld" -Xlinker --gc-sections <library dirs> -Wl,-Map,"digitizer.map" --specs=nano.specs --specs=rdimon.specs
So apparently the issue isn't that the size is wrong. Notably, I have this section in my header generated by cbindgen:
extern "C" {
CResult<uintptr_t, Error> serialize(const Message *self, CSliceMut<uint8_t> dest);
} // extern "C"
When I run the code, it creates a hard fault. I debugged it to investigate the issue, and the returned CResult<uintptr_t, Error> has an incorrect tag. The tag is clearly garbage data, but it should be 0 or 1.
I think I can help you best if I just show you exactly what is happening with some screenshots.
Here is what happens when I return this tagged enum using #[repr(C)]:

Here is what happens when I return this tagged enum using #[repr(C, usize)]:

The only change that I make is just that I change cbindgen from #[repr(C)] to #[repr(C, usize)]. There is no change to any other lines of code aside from the type specifier on this enum and on the Rust side. With #[repr(C)], the code has undefined behavior. With #[repr(C, usize)], the code works fine. I am not sure exactly why that is at this point.
Let me know if there is anything else I can do to help. Perhaps this ticket needs to be renamed based on the issue here.
If #[repr(C)] in rust disagrees with enum Foo in some platforms, that seems like a rustc bug rather than a cbindgen bug, IMO.