Skip to content

Commit

Permalink
Add support for out-of-line bitfields declarations
Browse files Browse the repository at this point in the history
  • Loading branch information
glandium committed Sep 10, 2023
1 parent 35f2e44 commit e4d9537
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 73 deletions.
142 changes: 106 additions & 36 deletions src/bindgen/bitflags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
// )+
// }
#[derive(Debug)]
pub struct Bitflags {
pub struct BitflagsStruct {
attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
#[allow(dead_code)]
Expand All @@ -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::ItemStruct>, 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<Self> {
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()?,
})
})
}
}
Expand All @@ -89,6 +134,7 @@ struct Flag {
struct FlagValueFold<'a> {
struct_name: &'a syn::Ident,
flag_names: &'a HashSet<String>,
out_of_line: bool,
}

impl<'a> FlagValueFold<'a> {
Expand All @@ -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,
Expand All @@ -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)
},
});
}
_ => {}
Expand All @@ -151,6 +214,7 @@ impl Flag {
struct_name: &syn::Ident,
repr: &syn::Type,
flag_names: &HashSet<String>,
out_of_line: bool,
) -> TokenStream {
let Flag {
ref attrs,
Expand All @@ -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;
}
}
}
Expand Down Expand Up @@ -199,15 +269,15 @@ 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
.iter()
.map(|flag| flag.name.to_string())
.collect::<HashSet<_>>();
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
}
Expand Down
13 changes: 7 additions & 6 deletions src/bindgen/ir/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ impl Struct {
other: &str,
out: &mut SourceWriter<F>,
) {
let bits = &self.fields[0].name;
out.new_line();
write!(
out,
Expand All @@ -223,10 +224,8 @@ impl Struct {
out.open_brace();
write!(
out,
"return {} {{ static_cast<decltype(bits)>(this->bits {} {}.bits) }};",
self.export_name(),
operator,
other
"return {} {{ static_cast<decltype({bits})>(this->{bits} {operator} {other}.{bits}) }};",
self.export_name()
);
out.close_brace(false);

Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -560,7 +561,7 @@ impl Source for Struct {
out.open_brace();
write!(
out,
"return {} {{ static_cast<decltype(bits)>(~bits) }};",
"return {} {{ static_cast<decltype({bits})>(~{bits}) }};",
self.export_name()
);
out.close_brace(false);
Expand Down
53 changes: 32 additions & 21 deletions src/bindgen/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -483,6 +483,7 @@ impl Parse {
) -> Vec<&'a syn::ItemMod> {
let mut impls_with_assoc_consts = Vec::new();
let mut nested_modules = Vec::new();
let mut bitflags_items = Vec::new();

for item in items {
if item.should_skip_parsing() {
Expand Down Expand Up @@ -547,7 +548,9 @@ impl Parse {
}
}
syn::Item::Macro(ref item) => {
self.load_builtin_macro(config, crate_name, mod_cfg, item)
if let Some(bitflags) = self.load_builtin_macro(config, item) {
bitflags_items.push(bitflags);
}
}
syn::Item::Mod(ref item) => {
nested_modules.push(item);
Expand All @@ -560,6 +563,25 @@ impl Parse {
self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, item_impl)
}

for bitflags in bitflags_items {
let (struct_, impl_) = bitflags.expand();
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_)
}

nested_modules
}

Expand Down Expand Up @@ -972,32 +994,21 @@ impl Parse {
fn load_builtin_macro(
&mut self,
config: &Config,
crate_name: &str,
mod_cfg: Option<&Cfg>,
item: &syn::ItemMacro,
) {
) -> Option<bitflags::Bitflags> {
let name = match item.mac.path.segments.last() {
Some(n) => n.ident.unraw().to_string(),
None => return,
None => return None,
};

if name != "bitflags" || !config.macro_expansion.bitflags {
return;
return None;
}

let bitflags = match bitflags::parse(item.mac.tokens.clone()) {
Ok(b) => b,
Err(e) => {
bitflags::parse(item.mac.tokens.clone())
.map_err(|e| {
warn!("Failed to parse bitflags invocation: {:?}", e);
return;
}
};

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_);
})
.ok()
}
}
12 changes: 11 additions & 1 deletion tests/expectations/bitflags.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Loading

0 comments on commit e4d9537

Please sign in to comment.