Skip to content

Support repr(C) for enums #3263

@thedataking

Description

@thedataking

Problem

Bindgen supports different enum representations which are either marked repr(transparent) or repr($ty) where $ty is the representation used for the enum (an integer type or an alias for one). However, for cross-language CFI to work correctly, it is sometimes desirable to use repr(C) instead as it causes the compiler to emit the right type ID for cross-language CFI to work.

Example:

enum handler_return {
    INT_NO_RESCHEDULE = 0,
    INT_RESCHEDULE,
};

gives us the following bindings (using --rustified-enum handler_return):

// in context of cross-language CFI, the return type is `core::ffi::c_uint` but
// the expected type is `enum handler_return`) thus an indirect function
// call using this type will abort
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum handler_return {
    INT_NO_RESCHEDULE = 0,
    INT_RESCHEDULE = 1,
}

The following two variations work:

// this works but isn't supported by bindgen as far as I can tell
// (e.g. can't pass `repr(c)` using `--with-attribute-custom-enum ` flag)
#[repr(C)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum handler_return {
    INT_NO_RESCHEDULE = 0,
    INT_RESCHEDULE = 1,
}

// this also works but requires the user to know about the 
// C++ Itanium ABI/cross-lang CFI internals - not ideal...
#[repr(u32)]
#[cfi_encoding = "14handler_return"]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum handler_return {
    INT_NO_RESCHEDULE = 0,
    INT_RESCHEDULE = 1,
}

Using --newtype-enum handler_return fails the CFI check too:

// fails cfi check
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct handler_return(pub core::ffi::c_uint);

and repr(C) or cfi_encoding = "14handler_return" attributes make the CFI check pass again.

Potential Solutions

  1. Add new flags to request repr(C) for enums, e.g. --rustified-enum-repr, --newtype-enum-repr, etc.
  2. Allow --with-attribute-custom-enum REGEX=#[repr(C)]
  3. No action; require the user to use #[cfi_encoding] or change the LLVM cross-language CFI implementation.

The first solution complicates the bindgen CLI but the functionality is easily discoverable. The second solution makes the inverse tradeoff: more can be done with the existing flags but few people will discover that this is the case.

I have prototyped the second approach and can put up a draft PR unless there's a feeling it is not the right way to go. Also very interested in hearing about potentially better solutions I just haven't thought of.

cc @rcvalle @maurer

(Edited to fix mistake pointed out by @emilio in this comment.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions