From 9b4a14958ea6ce2a61accb9cdbad84cfca9c3013 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sun, 10 Sep 2023 06:42:35 +0900 Subject: [PATCH] Add support for out-of-line bitfields declarations --- src/bindgen/bitflags.rs | 142 ++++++++++++++++------ src/bindgen/ir/structure.rs | 13 +- src/bindgen/parser.rs | 26 ++-- tests/expectations/bitflags.c | 12 +- tests/expectations/bitflags.compat.c | 12 +- tests/expectations/bitflags.cpp | 40 +++++- tests/expectations/bitflags.pyx | 11 +- tests/expectations/bitflags_both.c | 12 +- tests/expectations/bitflags_both.compat.c | 12 +- tests/expectations/bitflags_tag.c | 12 +- tests/expectations/bitflags_tag.compat.c | 12 +- tests/expectations/bitflags_tag.pyx | 11 +- tests/rust/bitflags.rs | 20 ++- 13 files changed, 274 insertions(+), 61 deletions(-) diff --git a/src/bindgen/bitflags.rs b/src/bindgen/bitflags.rs index 6ebb1d912..2ca0be6c3 100644 --- a/src/bindgen/bitflags.rs +++ b/src/bindgen/bitflags.rs @@ -15,7 +15,7 @@ use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult}; // )+ // } #[derive(Debug)] -pub struct Bitflags { +pub struct BitflagsStruct { attrs: Vec, vis: syn::Visibility, #[allow(dead_code)] @@ -27,46 +27,91 @@ pub struct Bitflags { flags: Flags, } +// impl $BitFlags:ident: $T:ty { +// $( +// $(#[$inner:ident $($args:tt)*])* +// const $Flag:ident = $value:expr; +// )+ +// } +#[derive(Debug)] +pub struct BitflagsImpl { + #[allow(dead_code)] + impl_token: Token![impl], + name: syn::Ident, + #[allow(dead_code)] + colon_token: Token![:], + repr: syn::Type, + flags: Flags, +} + +#[derive(Debug)] +pub enum Bitflags { + Struct(BitflagsStruct), + Impl(BitflagsImpl), +} + impl Bitflags { - pub fn expand(&self) -> (syn::ItemStruct, syn::ItemImpl) { - let Bitflags { - ref attrs, - ref vis, - ref name, - ref repr, - ref flags, - .. - } = *self; + pub fn expand(&self) -> (Option, syn::ItemImpl) { + match self { + Bitflags::Struct(BitflagsStruct { + attrs, + vis, + name, + repr, + flags, + .. + }) => { + let struct_ = parse_quote! { + #(#attrs)* + #vis struct #name { + bits: #repr, + } + }; - let struct_ = parse_quote! { - /// cbindgen:internal-derive-bitflags=true - #(#attrs)* - #vis struct #name { - bits: #repr, - } - }; + let consts = flags.expand(name, repr, false); + let impl_ = parse_quote! { + impl #name { + #consts + } + }; - let consts = flags.expand(name, repr); - let impl_ = parse_quote! { - impl #name { - #consts + (Some(struct_), impl_) } - }; - - (struct_, impl_) + Bitflags::Impl(BitflagsImpl { + name, repr, flags, .. + }) => { + let consts = flags.expand(name, repr, true); + let impl_: syn::ItemImpl = parse_quote! { + impl #name { + #consts + } + }; + (None, impl_) + } + } } } impl Parse for Bitflags { fn parse(input: ParseStream) -> ParseResult { - Ok(Self { - attrs: input.call(syn::Attribute::parse_outer)?, - vis: input.parse()?, - struct_token: input.parse()?, - name: input.parse()?, - colon_token: input.parse()?, - repr: input.parse()?, - flags: input.parse()?, + Ok(if input.peek(Token![impl]) { + Self::Impl(BitflagsImpl { + impl_token: input.parse()?, + name: input.parse()?, + colon_token: input.parse()?, + repr: input.parse()?, + flags: input.parse()?, + }) + } else { + Self::Struct(BitflagsStruct { + attrs: input.call(syn::Attribute::parse_outer)?, + vis: input.parse()?, + struct_token: input.parse()?, + name: input.parse()?, + colon_token: input.parse()?, + repr: input.parse()?, + flags: input.parse()?, + }) }) } } @@ -89,6 +134,7 @@ struct Flag { struct FlagValueFold<'a> { struct_name: &'a syn::Ident, flag_names: &'a HashSet, + out_of_line: bool, } impl<'a> FlagValueFold<'a> { @@ -114,6 +160,19 @@ impl<'a> Fold for FlagValueFold<'a> { // as far as our bindings generation is concerned, `bits` is available as a field, // so by replacing `StructName::FLAG.bits()` with `StructName::FLAG.bits`, we make // e.g. `Flags::AB` available in the generated bindings. + // For out-of-line definitions of the struct(*), where the struct is defined as a + // newtype, we replace it with `StructName::FLAGS.0`. + // * definitions like: + // ``` + // struct Flags(u8); + // bitflags! { + // impl Flags: u8 { + // const A = 1; + // const B = 1 << 1; + // const AB = Flags::A.bits() | Flags::B.bits(); + // } + // } + // ``` match node { syn::Expr::MethodCall(syn::ExprMethodCall { attrs, @@ -136,7 +195,11 @@ impl<'a> Fold for FlagValueFold<'a> { attrs, base: receiver, dot_token, - member: syn::Member::Named(method), + member: if self.out_of_line { + syn::Member::Unnamed(parse_quote! {0}) + } else { + syn::Member::Named(method) + }, }); } _ => {} @@ -151,6 +214,7 @@ impl Flag { struct_name: &syn::Ident, repr: &syn::Type, flag_names: &HashSet, + out_of_line: bool, ) -> TokenStream { let Flag { ref attrs, @@ -161,11 +225,17 @@ impl Flag { let folded_value = FlagValueFold { struct_name, flag_names, + out_of_line, } .fold_expr(value.clone()); + let value = if out_of_line { + quote! { ((#folded_value) as #repr) } + } else { + quote! { { bits: (#folded_value) as #repr } } + }; quote! { #(#attrs)* - pub const #name : #struct_name = #struct_name { bits: (#folded_value) as #repr }; + pub const #name : #struct_name = #struct_name #value; } } } @@ -199,7 +269,7 @@ impl Parse for Flags { } impl Flags { - fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream { + fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type, out_of_line: bool) -> TokenStream { let mut ts = quote! {}; let flag_names = self .0 @@ -207,7 +277,7 @@ impl Flags { .map(|flag| flag.name.to_string()) .collect::>(); for flag in &self.0 { - ts.extend(flag.expand(struct_name, repr, &flag_names)); + ts.extend(flag.expand(struct_name, repr, &flag_names, out_of_line)); } ts } diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index 7c5c4a95b..2eefa953d 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -210,6 +210,7 @@ impl Struct { other: &str, out: &mut SourceWriter, ) { + let bits = &self.fields[0].name; out.new_line(); write!( out, @@ -223,10 +224,8 @@ impl Struct { out.open_brace(); write!( out, - "return {} {{ static_cast(this->bits {} {}.bits) }};", - self.export_name(), - operator, - other + "return {} {{ static_cast(this->{bits} {operator} {other}.{bits}) }};", + self.export_name() ); out.close_brace(false); @@ -534,6 +533,8 @@ impl Source for Struct { .bool("internal-derive-bitflags") .unwrap_or(false) { + assert_eq!(self.fields.len(), 1); + let bits = &self.fields[0].name; if !wrote_start_newline { wrote_start_newline = true; out.new_line(); @@ -547,7 +548,7 @@ impl Source for Struct { out.new_line(); write!(out, "{}explicit operator bool() const", constexpr_prefix); out.open_brace(); - write!(out, "return !!bits;"); + write!(out, "return !!{bits};"); out.close_brace(false); out.new_line(); @@ -560,7 +561,7 @@ impl Source for Struct { out.open_brace(); write!( out, - "return {} {{ static_cast(~bits) }};", + "return {} {{ static_cast(~{bits}) }};", self.export_name() ); out.close_brace(false); diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index 9cd6a54a4..b54c7fbbd 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -15,8 +15,8 @@ use crate::bindgen::cargo::{Cargo, PackageRef}; use crate::bindgen::config::{Config, ParseConfig}; use crate::bindgen::error::Error; use crate::bindgen::ir::{ - AnnotationSet, Cfg, Constant, Documentation, Enum, Function, GenericParam, GenericParams, - ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, + AnnotationSet, AnnotationValue, Cfg, Constant, Documentation, Enum, Function, GenericParam, + GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, }; use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemHelpers}; @@ -547,7 +547,7 @@ impl Parse { } } syn::Item::Macro(ref item) => { - self.load_builtin_macro(config, crate_name, mod_cfg, item) + self.load_builtin_macro(config, crate_name, mod_cfg, item); } syn::Item::Mod(ref item) => { nested_modules.push(item); @@ -986,7 +986,7 @@ impl Parse { } let bitflags = match bitflags::parse(item.mac.tokens.clone()) { - Ok(b) => b, + Ok(bf) => bf, Err(e) => { warn!("Failed to parse bitflags invocation: {:?}", e); return; @@ -994,10 +994,18 @@ impl Parse { }; let (struct_, impl_) = bitflags.expand(); - self.load_syn_struct(config, crate_name, mod_cfg, &struct_); - // We know that the expansion will only reference `struct_`, so it's - // fine to just do it here instead of deferring it like we do with the - // other calls to this function. - self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, &impl_); + if let Some(struct_) = struct_ { + self.load_syn_struct(config, crate_name, mod_cfg, &struct_); + } + if let syn::Type::Path(ref path) = *impl_.self_ty { + if let Some(type_name) = path.path.get_ident() { + self.structs + .for_items_mut(&Path::new(type_name.unraw().to_string()), |item| { + item.annotations + .add_default("internal-derive-bitflags", AnnotationValue::Bool(true)); + }); + } + } + self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, &impl_) } } diff --git a/tests/expectations/bitflags.c b/tests/expectations/bitflags.c index 9af76af44..2d212c102 100644 --- a/tests/expectations/bitflags.c +++ b/tests/expectations/bitflags.c @@ -52,4 +52,14 @@ typedef struct { #define LargeFlags_LARGE_SHIFT (LargeFlags){ .bits = (uint64_t)(1ull << 44) } #define LargeFlags_INVERTED (LargeFlags){ .bits = (uint64_t)~(LargeFlags_LARGE_SHIFT).bits } -void root(AlignFlags flags, DebugFlags bigger_flags, LargeFlags largest_flags); +typedef struct { + uint32_t _0; +} OutOfLine; +#define OutOfLine_A (OutOfLine){ ._0 = (uint32_t)1 } +#define OutOfLine_B (OutOfLine){ ._0 = (uint32_t)2 } +#define OutOfLine_AB (OutOfLine){ ._0 = (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) } + +void root(AlignFlags flags, + DebugFlags bigger_flags, + LargeFlags largest_flags, + OutOfLine out_of_line); diff --git a/tests/expectations/bitflags.compat.c b/tests/expectations/bitflags.compat.c index 863de08b1..3ff2b4613 100644 --- a/tests/expectations/bitflags.compat.c +++ b/tests/expectations/bitflags.compat.c @@ -52,11 +52,21 @@ typedef struct { #define LargeFlags_LARGE_SHIFT (LargeFlags){ .bits = (uint64_t)(1ull << 44) } #define LargeFlags_INVERTED (LargeFlags){ .bits = (uint64_t)~(LargeFlags_LARGE_SHIFT).bits } +typedef struct { + uint32_t _0; +} OutOfLine; +#define OutOfLine_A (OutOfLine){ ._0 = (uint32_t)1 } +#define OutOfLine_B (OutOfLine){ ._0 = (uint32_t)2 } +#define OutOfLine_AB (OutOfLine){ ._0 = (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) } + #ifdef __cplusplus extern "C" { #endif // __cplusplus -void root(AlignFlags flags, DebugFlags bigger_flags, LargeFlags largest_flags); +void root(AlignFlags flags, + DebugFlags bigger_flags, + LargeFlags largest_flags, + OutOfLine out_of_line); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/bitflags.cpp b/tests/expectations/bitflags.cpp index 6ed473665..7ad71b9ae 100644 --- a/tests/expectations/bitflags.cpp +++ b/tests/expectations/bitflags.cpp @@ -121,8 +121,46 @@ struct LargeFlags { constexpr static const LargeFlags LargeFlags_LARGE_SHIFT = LargeFlags{ /* .bits = */ (uint64_t)(1ull << 44) }; constexpr static const LargeFlags LargeFlags_INVERTED = LargeFlags{ /* .bits = */ (uint64_t)~(LargeFlags_LARGE_SHIFT).bits }; +struct OutOfLine { + uint32_t _0; + + constexpr explicit operator bool() const { + return !!_0; + } + constexpr OutOfLine operator~() const { + return OutOfLine { static_cast(~_0) }; + } + constexpr OutOfLine operator|(const OutOfLine& other) const { + return OutOfLine { static_cast(this->_0 | other._0) }; + } + OutOfLine& operator|=(const OutOfLine& other) { + *this = (*this | other); + return *this; + } + constexpr OutOfLine operator&(const OutOfLine& other) const { + return OutOfLine { static_cast(this->_0 & other._0) }; + } + OutOfLine& operator&=(const OutOfLine& other) { + *this = (*this & other); + return *this; + } + constexpr OutOfLine operator^(const OutOfLine& other) const { + return OutOfLine { static_cast(this->_0 ^ other._0) }; + } + OutOfLine& operator^=(const OutOfLine& other) { + *this = (*this ^ other); + return *this; + } +}; +constexpr static const OutOfLine OutOfLine_A = OutOfLine{ /* ._0 = */ (uint32_t)1 }; +constexpr static const OutOfLine OutOfLine_B = OutOfLine{ /* ._0 = */ (uint32_t)2 }; +constexpr static const OutOfLine OutOfLine_AB = OutOfLine{ /* ._0 = */ (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) }; + extern "C" { -void root(AlignFlags flags, DebugFlags bigger_flags, LargeFlags largest_flags); +void root(AlignFlags flags, + DebugFlags bigger_flags, + LargeFlags largest_flags, + OutOfLine out_of_line); } // extern "C" diff --git a/tests/expectations/bitflags.pyx b/tests/expectations/bitflags.pyx index e3d104015..5137b53f9 100644 --- a/tests/expectations/bitflags.pyx +++ b/tests/expectations/bitflags.pyx @@ -36,4 +36,13 @@ cdef extern from *: const LargeFlags LargeFlags_LARGE_SHIFT # = { (1ull << 44) } const LargeFlags LargeFlags_INVERTED # = { ~(LargeFlags_LARGE_SHIFT).bits } - void root(AlignFlags flags, DebugFlags bigger_flags, LargeFlags largest_flags); + ctypedef struct OutOfLine: + uint32_t _0; + const OutOfLine OutOfLine_A # = { 1 } + const OutOfLine OutOfLine_B # = { 2 } + const OutOfLine OutOfLine_AB # = { ((OutOfLine_A)._0 | (OutOfLine_B)._0) } + + void root(AlignFlags flags, + DebugFlags bigger_flags, + LargeFlags largest_flags, + OutOfLine out_of_line); diff --git a/tests/expectations/bitflags_both.c b/tests/expectations/bitflags_both.c index b5cb6a7cf..2baed9844 100644 --- a/tests/expectations/bitflags_both.c +++ b/tests/expectations/bitflags_both.c @@ -52,4 +52,14 @@ typedef struct LargeFlags { #define LargeFlags_LARGE_SHIFT (LargeFlags){ .bits = (uint64_t)(1ull << 44) } #define LargeFlags_INVERTED (LargeFlags){ .bits = (uint64_t)~(LargeFlags_LARGE_SHIFT).bits } -void root(struct AlignFlags flags, struct DebugFlags bigger_flags, struct LargeFlags largest_flags); +typedef struct OutOfLine { + uint32_t _0; +} OutOfLine; +#define OutOfLine_A (OutOfLine){ ._0 = (uint32_t)1 } +#define OutOfLine_B (OutOfLine){ ._0 = (uint32_t)2 } +#define OutOfLine_AB (OutOfLine){ ._0 = (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) } + +void root(struct AlignFlags flags, + struct DebugFlags bigger_flags, + struct LargeFlags largest_flags, + struct OutOfLine out_of_line); diff --git a/tests/expectations/bitflags_both.compat.c b/tests/expectations/bitflags_both.compat.c index 35726624c..13591e3be 100644 --- a/tests/expectations/bitflags_both.compat.c +++ b/tests/expectations/bitflags_both.compat.c @@ -52,11 +52,21 @@ typedef struct LargeFlags { #define LargeFlags_LARGE_SHIFT (LargeFlags){ .bits = (uint64_t)(1ull << 44) } #define LargeFlags_INVERTED (LargeFlags){ .bits = (uint64_t)~(LargeFlags_LARGE_SHIFT).bits } +typedef struct OutOfLine { + uint32_t _0; +} OutOfLine; +#define OutOfLine_A (OutOfLine){ ._0 = (uint32_t)1 } +#define OutOfLine_B (OutOfLine){ ._0 = (uint32_t)2 } +#define OutOfLine_AB (OutOfLine){ ._0 = (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) } + #ifdef __cplusplus extern "C" { #endif // __cplusplus -void root(struct AlignFlags flags, struct DebugFlags bigger_flags, struct LargeFlags largest_flags); +void root(struct AlignFlags flags, + struct DebugFlags bigger_flags, + struct LargeFlags largest_flags, + struct OutOfLine out_of_line); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/bitflags_tag.c b/tests/expectations/bitflags_tag.c index c4be368d0..874ac1755 100644 --- a/tests/expectations/bitflags_tag.c +++ b/tests/expectations/bitflags_tag.c @@ -52,4 +52,14 @@ struct LargeFlags { #define LargeFlags_LARGE_SHIFT (LargeFlags){ .bits = (uint64_t)(1ull << 44) } #define LargeFlags_INVERTED (LargeFlags){ .bits = (uint64_t)~(LargeFlags_LARGE_SHIFT).bits } -void root(struct AlignFlags flags, struct DebugFlags bigger_flags, struct LargeFlags largest_flags); +struct OutOfLine { + uint32_t _0; +}; +#define OutOfLine_A (OutOfLine){ ._0 = (uint32_t)1 } +#define OutOfLine_B (OutOfLine){ ._0 = (uint32_t)2 } +#define OutOfLine_AB (OutOfLine){ ._0 = (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) } + +void root(struct AlignFlags flags, + struct DebugFlags bigger_flags, + struct LargeFlags largest_flags, + struct OutOfLine out_of_line); diff --git a/tests/expectations/bitflags_tag.compat.c b/tests/expectations/bitflags_tag.compat.c index d12ba40f3..dc310d4f5 100644 --- a/tests/expectations/bitflags_tag.compat.c +++ b/tests/expectations/bitflags_tag.compat.c @@ -52,11 +52,21 @@ struct LargeFlags { #define LargeFlags_LARGE_SHIFT (LargeFlags){ .bits = (uint64_t)(1ull << 44) } #define LargeFlags_INVERTED (LargeFlags){ .bits = (uint64_t)~(LargeFlags_LARGE_SHIFT).bits } +struct OutOfLine { + uint32_t _0; +}; +#define OutOfLine_A (OutOfLine){ ._0 = (uint32_t)1 } +#define OutOfLine_B (OutOfLine){ ._0 = (uint32_t)2 } +#define OutOfLine_AB (OutOfLine){ ._0 = (uint32_t)((OutOfLine_A)._0 | (OutOfLine_B)._0) } + #ifdef __cplusplus extern "C" { #endif // __cplusplus -void root(struct AlignFlags flags, struct DebugFlags bigger_flags, struct LargeFlags largest_flags); +void root(struct AlignFlags flags, + struct DebugFlags bigger_flags, + struct LargeFlags largest_flags, + struct OutOfLine out_of_line); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/bitflags_tag.pyx b/tests/expectations/bitflags_tag.pyx index d5f2df6d5..44207087a 100644 --- a/tests/expectations/bitflags_tag.pyx +++ b/tests/expectations/bitflags_tag.pyx @@ -36,4 +36,13 @@ cdef extern from *: const LargeFlags LargeFlags_LARGE_SHIFT # = { (1ull << 44) } const LargeFlags LargeFlags_INVERTED # = { ~(LargeFlags_LARGE_SHIFT).bits } - void root(AlignFlags flags, DebugFlags bigger_flags, LargeFlags largest_flags); + cdef struct OutOfLine: + uint32_t _0; + const OutOfLine OutOfLine_A # = { 1 } + const OutOfLine OutOfLine_B # = { 2 } + const OutOfLine OutOfLine_AB # = { ((OutOfLine_A)._0 | (OutOfLine_B)._0) } + + void root(AlignFlags flags, + DebugFlags bigger_flags, + LargeFlags largest_flags, + OutOfLine out_of_line); diff --git a/tests/rust/bitflags.rs b/tests/rust/bitflags.rs index 1fe1c6e3e..cba837721 100644 --- a/tests/rust/bitflags.rs +++ b/tests/rust/bitflags.rs @@ -38,6 +38,24 @@ bitflags! { } } +// bitflags 2 allows to define types out-of-line for custom derives +// #[derive(SomeTrait)] +#[repr(C)] +pub struct OutOfLine(u32); + +bitflags! { + impl OutOfLine: u32 { + const A = 1; + const B = 2; + const AB = Self::A.bits() | Self::B.bits(); + } +} #[no_mangle] -pub extern "C" fn root(flags: AlignFlags, bigger_flags: DebugFlags, largest_flags: LargeFlags) {} +pub extern "C" fn root( + flags: AlignFlags, + bigger_flags: DebugFlags, + largest_flags: LargeFlags, + out_of_line: OutOfLine, +) { +}