Skip to content
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
- Removed support for generating code for rustc versions < 1.51.
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
38 changes: 38 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.

67 changes: 67 additions & 0 deletions bindgen-tests/tests/expectations/tests/enums-repr-c.rs

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

9 changes: 9 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,9 @@
// bindgen-flags: --rustified-repr-c-enum B|BigEnum

#include "enum-doc.h"

/** An enum with a value larger than the platform's `c_int` */
enum BigEnum {
/** A value that is too large to fit in a 32-bit integer */
BIG_ENUM_BIG = 4294967296,
};
39 changes: 39 additions & 0 deletions bindgen-tests/tests/headers/enums-repr-c.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// bindgen-flags: --rustified-repr-c-enum ".*" -- -std=c++11

typedef enum {
SMALL_VALUE = 0x1,
} small_value_t;

static_assert(sizeof(small_value_t) == 4, "");

typedef enum {
MEDIUM_VALUE = 0x100,
} medium_value_t;

static_assert(sizeof(medium_value_t) == 4, "");

typedef enum {
LARGE_VALUE = 0x1000000,
} large_value_t;

static_assert(sizeof(large_value_t) == 4, "");

typedef enum {
UNSIGNED_VALUE = 0x80000000,
} unsigned_value_t;

static_assert(sizeof(unsigned_value_t) == 4, "");

typedef enum {
NEGATIVE_VALUE = -1,
POSITIVE_VALUE = 1,
} mixed_sign_t;

static_assert(sizeof(mixed_sign_t) == 4, "");

typedef enum {
WIDE_NEGATIVE_VALUE = -1,
WIDE_UNSIGNED_VALUE = 0x80000000,
} wide_mixed_sign_t;

static_assert(sizeof(wide_mixed_sign_t) == 8, "");
6 changes: 6 additions & 0 deletions bindgen/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,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
110 changes: 99 additions & 11 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3304,6 +3304,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 @@ -3335,11 +3337,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 @@ -3368,9 +3373,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 All @@ -3390,7 +3405,8 @@ impl FromStr for EnumVariation {
std::io::ErrorKind::InvalidInput,
concat!(
"Got an invalid EnumVariation. Accepted values ",
"are 'rust', 'rust_non_exhaustive', 'bitfield', 'consts',",
"are 'rust', 'rust_repr_c', 'rust_non_exhaustive', ",
"'rust_non_exhaustive_repr_c', 'bitfield', 'consts', ",
"'moduleconsts', 'newtype' and 'newtype_global'."
),
)),
Expand All @@ -3417,6 +3433,7 @@ struct EnumBuilder {
enum EnumBuilderKind {
Rust {
non_exhaustive: bool,
repr_c: bool,
},
NewType {
is_bitfield: bool,
Expand Down Expand Up @@ -3462,9 +3479,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 @@ -3675,14 +3696,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 Expand Up @@ -4085,6 +4115,64 @@ impl CodeGenerator for Enum {

let item = builder.build(ctx, &enum_rust_ty);
result.push(item);

if matches!(variation, EnumVariation::Rust { repr_c: true, .. }) {
if let Some(layout) = layout {
// rustc sizes a fieldless `#[repr(C)]` enum like the target's
// default C enum type, i.e. `c_int` on all supported targets.
if layout.size != 4 || layout.align != 4 {
warn!(
"enum `{ident}` has size {} and alignment {}, but \
`#[repr(C)]` enums are usually 4 bytes; it and any \
type containing it may get an incompatible layout \
(e.g. due to -fshort-enums or an explicit underlying \
type)",
layout.size, layout.align
);
}

if !ctx.options().layout_tests {
return;
}

let compile_time = ctx.options().rust_features().offset_of;
let fn_name = if compile_time {
None
} else {
let fn_name = format!("bindgen_test_layout_{ident}");
Some(ctx.rust_ident_raw(fn_name))
};
let prefix = ctx.trait_prefix();
let size_of_expr = quote! {
::#prefix::mem::size_of::<#ident>()
};
let align_of_expr = quote! {
::#prefix::mem::align_of::<#ident>()
};
let size = layout.size;
let align = layout.align;
let size_of_err = format!("Size of {ident}");
let align_of_err = format!("Alignment of {ident}");

if compile_time {
result.push(quote! {
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
[#size_of_err][#size_of_expr - #size];
[#align_of_err][#align_of_expr - #align];
};
});
} else {
result.push(quote! {
#[test]
fn #fn_name() {
assert_eq!(#size_of_expr, #size, #size_of_err);
assert_eq!(#align_of_expr, #align, #align_of_err);
}
});
}
}
}
}
}

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 @@ -219,6 +219,7 @@ impl Enum {
) {
EnumVariation::Rust {
non_exhaustive: false,
repr_c: false,
}
} else if self.is_matching_enum(
ctx,
Expand All @@ -227,6 +228,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
Loading
Loading