From e0439c598477ee5ada7c37c700358610644a4d93 Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Thu, 14 Aug 2025 06:35:00 +0000 Subject: [PATCH 1/4] Support repr(C) rustified enums It is not possible to control the repr via custom attributes so add a new rustified enum variant which does not use repr(u*) or repr(i*). Using repr(C) is sometimes necessary to bindgen enums used in functions subject to cross-language CFI checks. Closes 3263. Link: https://rcvalle.com/docs/rust-cfi-design-doc/ Signed-off-by: Per Larsen --- bindgen/codegen/helpers.rs | 6 +++++ bindgen/codegen/mod.rs | 49 ++++++++++++++++++++++++++++++-------- bindgen/ir/enum_ty.rs | 11 +++++++++ bindgen/lib.rs | 4 +++- bindgen/options/cli.rs | 5 ++++ bindgen/options/mod.rs | 19 ++++++++++++++- 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs index 82172f3488..3c64367dc6 100644 --- a/bindgen/codegen/helpers.rs +++ b/bindgen/codegen/helpers.rs @@ -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!() diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 5425962bac..46118e950b 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -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 { @@ -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", @@ -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, @@ -3281,6 +3296,7 @@ struct EnumBuilder { enum EnumBuilderKind { Rust { non_exhaustive: bool, + repr_c: bool, }, NewType { is_bitfield: bool, @@ -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, @@ -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 { diff --git a/bindgen/ir/enum_ty.rs b/bindgen/ir/enum_ty.rs index 9b08da3bce..1fbc3669f5 100644 --- a/bindgen/ir/enum_ty.rs +++ b/bindgen/ir/enum_ty.rs @@ -216,6 +216,7 @@ impl Enum { ) { EnumVariation::Rust { non_exhaustive: false, + repr_c: false, } } else if self.is_matching_enum( ctx, @@ -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, diff --git a/bindgen/lib.rs b/bindgen/lib.rs index e4294c505a..36c8457a99 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -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, @@ -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, @@ -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", diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index b60de39603..b281a1ff32 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -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, + /// Mark any enum whose name matches REGEX as a repr(C) Rust enum. + #[arg(long, value_name = "REGEX")] + rustified_repr_c_enum: Vec, /// Mark any enum whose name matches REGEX as a series of constants. #[arg(long, value_name = "REGEX")] constified_enum: Vec, @@ -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, @@ -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, diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 767be03e35..7794fd16dc 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -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, @@ -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>(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: { From 8a8aa6a59889caa07be09f744e10c0f5b7ddfc3b Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Thu, 14 Aug 2025 07:28:48 +0000 Subject: [PATCH 2/4] Test --rustified-repr-c-enum flag --- .../tests/enum-doc-rusty-repr-c.rs | 21 +++++++++++++++++++ .../tests/headers/enum-doc-rusty-repr-c.h | 3 +++ 2 files changed, 24 insertions(+) create mode 100644 bindgen-tests/tests/expectations/tests/enum-doc-rusty-repr-c.rs create mode 100644 bindgen-tests/tests/headers/enum-doc-rusty-repr-c.h diff --git a/bindgen-tests/tests/expectations/tests/enum-doc-rusty-repr-c.rs b/bindgen-tests/tests/expectations/tests/enum-doc-rusty-repr-c.rs new file mode 100644 index 0000000000..9348cfabe3 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/enum-doc-rusty-repr-c.rs @@ -0,0 +1,21 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +/// Document enum +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum B { + /// Document field with three slashes + VAR_A = 0, + /// Document field with preceding star + VAR_B = 1, + /// Document field with preceding exclamation + VAR_C = 2, + ///< Document field with following star + VAR_D = 3, + ///< Document field with following exclamation + VAR_E = 4, + /** Document field with preceding star, with a loong long multiline + comment. + + Very interesting documentation, definitely.*/ + VAR_F = 5, +} diff --git a/bindgen-tests/tests/headers/enum-doc-rusty-repr-c.h b/bindgen-tests/tests/headers/enum-doc-rusty-repr-c.h new file mode 100644 index 0000000000..41753225ea --- /dev/null +++ b/bindgen-tests/tests/headers/enum-doc-rusty-repr-c.h @@ -0,0 +1,3 @@ +// bindgen-flags: --rustified-repr-c-enum B + +#include "enum-doc.h" From 8443943dea9f3c416b19e99bfd4db1a29bb10a6a Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Thu, 14 Aug 2025 07:48:31 +0000 Subject: [PATCH 3/4] Update bindgen-integration --- bindgen-integration/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs index c940c3e9fa..e7999a5d03 100644 --- a/bindgen-integration/build.rs +++ b/bindgen-integration/build.rs @@ -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); }") From d020ef1b70fa30114f873cfe04727b97c29ce54e Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Fri, 15 Aug 2025 01:18:01 +0000 Subject: [PATCH 4/4] Update CHANGELOG.md Note addition of https://github.com/rust-lang/rust-bindgen/pull/3265 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d3494e75..cfab576eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -227,6 +227,7 @@ -------------------------------------------------------------------------------- # Unreleased ## Added + * Add option in CLI to use rustified repr-C enums (--rustified-repr-c-enum, #3265). ## Changed ## Removed ## Fixed