Skip to content

Support repr(C) for rustified enums #3265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
--------------------------------------------------------------------------------
# Unreleased
## Added
* Add option in CLI to use rustified repr-C enums (--rustified-repr-c-enum, #3265).
## Changed
## Removed
## Fixed
Expand Down
1 change: 1 addition & 0 deletions bindgen-integration/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ fn setup_macro_test() {
.enable_cxx_namespaces()
.default_enum_style(EnumVariation::Rust {
non_exhaustive: false,
repr_c: false,
})
.raw_line("pub use self::root::*;")
.raw_line("extern { fn my_prefixed_function_to_remove(i: i32); }")
Expand Down
21 changes: 21 additions & 0 deletions bindgen-tests/tests/expectations/tests/enum-doc-rusty-repr-c.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions bindgen-tests/tests/headers/enum-doc-rusty-repr-c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// bindgen-flags: --rustified-repr-c-enum B

#include "enum-doc.h"
6 changes: 6 additions & 0 deletions bindgen/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ pub(crate) mod attributes {
}
}

pub(crate) fn repr_c() -> TokenStream {
quote! {
#[repr(C)]
}
}

pub(crate) fn doc(comment: &str) -> TokenStream {
if comment.is_empty() {
quote!()
Expand Down
49 changes: 39 additions & 10 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3168,6 +3168,8 @@ pub enum EnumVariation {
Rust {
/// Indicates whether the generated struct should be `#[non_exhaustive]`
non_exhaustive: bool,
/// Indicates whether the generated struct should be `#[repr(C)]`
repr_c: bool,
},
/// The code for this enum will use a newtype
NewType {
Expand Down Expand Up @@ -3199,11 +3201,14 @@ impl fmt::Display for EnumVariation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Rust {
non_exhaustive: false,
} => "rust",
Self::Rust {
non_exhaustive: true,
} => "rust_non_exhaustive",
non_exhaustive,
repr_c,
} => match (*non_exhaustive, *repr_c) {
(false, false) => "rust",
(false, true) => "rust_repr_c",
(true, false) => "rust_non_exhaustive",
(true, true) => "rust_non_exhaustive_repr_c",
},
Self::NewType {
is_bitfield: true, ..
} => "bitfield",
Expand Down Expand Up @@ -3232,9 +3237,19 @@ impl FromStr for EnumVariation {
match s {
"rust" => Ok(EnumVariation::Rust {
non_exhaustive: false,
repr_c: false,
}),
"rust_repr_c" => Ok(EnumVariation::Rust {
non_exhaustive: false,
repr_c: true,
}),
"rust_non_exhaustive" => Ok(EnumVariation::Rust {
non_exhaustive: true,
repr_c: false,
}),
"rust_non_exhaustive_repr_c" => Ok(EnumVariation::Rust {
non_exhaustive: true,
repr_c: true,
}),
"bitfield" => Ok(EnumVariation::NewType {
is_bitfield: true,
Expand Down Expand Up @@ -3281,6 +3296,7 @@ struct EnumBuilder {
enum EnumBuilderKind {
Rust {
non_exhaustive: bool,
repr_c: bool,
},
NewType {
is_bitfield: bool,
Expand Down Expand Up @@ -3326,9 +3342,13 @@ impl EnumBuilder {
is_anonymous: enum_is_anonymous,
},

EnumVariation::Rust { non_exhaustive } => {
EnumBuilderKind::Rust { non_exhaustive }
}
EnumVariation::Rust {
non_exhaustive,
repr_c,
} => EnumBuilderKind::Rust {
non_exhaustive,
repr_c,
},

EnumVariation::Consts => EnumBuilderKind::Consts {
needs_typedef: !has_typedef,
Expand Down Expand Up @@ -3539,14 +3559,23 @@ impl EnumBuilder {

// 2. Generate the enum representation
match self.kind {
EnumBuilderKind::Rust { non_exhaustive } => {
EnumBuilderKind::Rust {
non_exhaustive,
repr_c,
} => {
let non_exhaustive_opt =
non_exhaustive.then(attributes::non_exhaustive);

let repr = if repr_c {
attributes::repr_c()
} else {
quote! { #[repr(#enum_repr)] }
};

quote! {
// Note: repr is on top of attrs to keep the test expectations diff small.
// a future commit could move it further down.
#[repr(#enum_repr)]
#repr
#non_exhaustive_opt
#( #attrs )*
pub enum #enum_ident {
Expand Down
11 changes: 11 additions & 0 deletions bindgen/ir/enum_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ impl Enum {
) {
EnumVariation::Rust {
non_exhaustive: false,
repr_c: false,
}
} else if self.is_matching_enum(
ctx,
Expand All @@ -224,6 +225,16 @@ impl Enum {
) {
EnumVariation::Rust {
non_exhaustive: true,
repr_c: false,
}
} else if self.is_matching_enum(
ctx,
&ctx.options().rustified_repr_c_enums,
item,
) {
EnumVariation::Rust {
non_exhaustive: false,
repr_c: true,
}
} else if self.is_matching_enum(
ctx,
Expand Down
4 changes: 3 additions & 1 deletion bindgen/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ impl Builder {

impl BindgenOptions {
fn build(&mut self) {
const REGEX_SETS_LEN: usize = 29;
const REGEX_SETS_LEN: usize = 30;

let regex_sets: [_; REGEX_SETS_LEN] = [
&mut self.blocklisted_types,
Expand All @@ -489,6 +489,7 @@ impl BindgenOptions {
&mut self.newtype_global_enums,
&mut self.rustified_enums,
&mut self.rustified_non_exhaustive_enums,
&mut self.rustified_repr_c_enums,
&mut self.type_alias,
&mut self.new_type_alias,
&mut self.new_type_alias_deref,
Expand Down Expand Up @@ -524,6 +525,7 @@ impl BindgenOptions {
"--newtype-global-enum",
"--rustified-enum",
"--rustified-enum-non-exhaustive",
"--rustified-enum-repr-c",
"--constified-enum-module",
"--constified-enum",
"--type-alias",
Expand Down
5 changes: 5 additions & 0 deletions bindgen/options/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ struct BindgenCommand {
/// Mark any enum whose name matches REGEX as a non-exhaustive Rust enum.
#[arg(long, value_name = "REGEX")]
rustified_non_exhaustive_enum: Vec<String>,
/// Mark any enum whose name matches REGEX as a repr(C) Rust enum.
#[arg(long, value_name = "REGEX")]
rustified_repr_c_enum: Vec<String>,
/// Mark any enum whose name matches REGEX as a series of constants.
#[arg(long, value_name = "REGEX")]
constified_enum: Vec<String>,
Expand Down Expand Up @@ -560,6 +563,7 @@ where
newtype_global_enum,
rustified_enum,
rustified_non_exhaustive_enum,
rustified_repr_c_enum,
constified_enum,
constified_enum_module,
default_macro_constant_type,
Expand Down Expand Up @@ -869,6 +873,7 @@ where
newtype_global_enum,
rustified_enum,
rustified_non_exhaustive_enum,
rustified_repr_c_enum,
constified_enum,
constified_enum_module,
default_macro_constant_type,
Expand Down
19 changes: 18 additions & 1 deletion bindgen/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ options! {
/// To set the style for individual `enum`s, use [`Builder::bitfield_enum`],
/// [`Builder::newtype_enum`], [`Builder::newtype_global_enum`],
/// [`Builder::rustified_enum`], [`Builder::rustified_non_exhaustive_enum`],
/// [`Builder::constified_enum_module`] or [`Builder::constified_enum`].
/// [`Builder::rustified_repr_c_enum`], [`Builder::constified_enum_module`],
/// or [`Builder::constified_enum`].
pub fn default_enum_style(
mut self,
arg: EnumVariation,
Expand Down Expand Up @@ -549,6 +550,22 @@ options! {
},
as_args: "--rustified-non-exhaustive-enum",
},
/// `enum`s marked as `repr(C)` Rust `enum`s.
rustified_repr_c_enums: RegexSet {
methods: {
regex_option! {
/// Mark the given `enum` as a `repr(C)` Rust `enum`.
///
/// This is similar to the [`Builder::rustified_enum`] style, but the `enum` is
/// tagged with the `#[repr(C)]` attribute.
pub fn rustified_repr_c_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.rustified_repr_c_enums.insert(arg);
self
}
}
},
as_args: "--rustified-repr-c-enum",
},
/// `enum`s marked as modules of constants.
constified_enum_modules: RegexSet {
methods: {
Expand Down
Loading