Skip to content

Commit a9db986

Browse files
nullstalgiaemilio
authored andcommitted
bitflags: proper handling of repr(transparent) bitflags structs
Bitflags can be represented using either repr(C) or repr(transparent), but previously cbindgen would output field accesses w/ repr(transparent) bitflags. This commit falls back to using int_t typedefs and associated constants for those cases.
1 parent df45c23 commit a9db986

10 files changed

Lines changed: 333 additions & 7 deletions

src/bindgen/bitflags.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use syn::ext::IdentExt;
88
use syn::fold::Fold;
99
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
1010

11+
use crate::bindgen::ir::{Repr, ReprStyle};
12+
1113
// $(#[$outer:meta])*
1214
// ($($vis:tt)*) $BitFlags:ident: $T:ty {
1315
// $(
@@ -52,7 +54,17 @@ pub enum Bitflags {
5254
}
5355

5456
impl Bitflags {
55-
pub fn expand(&self) -> (Option<syn::ItemStruct>, syn::ItemImpl) {
57+
pub fn self_ty_name(&self) -> &syn::Ident {
58+
match self {
59+
Bitflags::Struct(s) => &s.name,
60+
Bitflags::Impl(i) => &i.name,
61+
}
62+
}
63+
64+
pub fn expand(
65+
&self,
66+
out_of_line_transparent: bool,
67+
) -> (Option<syn::ItemStruct>, syn::ItemImpl) {
5668
match self {
5769
Bitflags::Struct(BitflagsStruct {
5870
attrs,
@@ -69,7 +81,13 @@ impl Bitflags {
6981
}
7082
};
7183

72-
let consts = flags.expand(name, repr, false);
84+
let is_transparent = if let Ok(repr) = Repr::load(attrs) {
85+
matches!(repr.style, ReprStyle::Transparent)
86+
} else {
87+
false
88+
};
89+
90+
let consts = flags.expand(name, repr, false, is_transparent);
7391
let impl_ = parse_quote! {
7492
impl #name {
7593
#consts
@@ -81,7 +99,7 @@ impl Bitflags {
8199
Bitflags::Impl(BitflagsImpl {
82100
name, repr, flags, ..
83101
}) => {
84-
let consts = flags.expand(name, repr, true);
102+
let consts = flags.expand(name, repr, true, out_of_line_transparent);
85103
let impl_: syn::ItemImpl = parse_quote! {
86104
impl #name {
87105
#consts
@@ -136,6 +154,7 @@ struct FlagValueFold<'a> {
136154
struct_name: &'a syn::Ident,
137155
flag_names: &'a HashSet<String>,
138156
out_of_line: bool,
157+
is_transparent: bool,
139158
}
140159

141160
impl FlagValueFold<'_> {
@@ -192,6 +211,12 @@ impl Fold for FlagValueFold<'_> {
192211
.flag_names
193212
.contains(&path.segments.last().unwrap().ident.to_string())) =>
194213
{
214+
// In the transparent case the struct is rendered as a plain typedef to the
215+
// underlying integer (e.g. `typedef uint8_t Flags;`), so there is no field
216+
// to access, so we keep just the receiver.
217+
if self.is_transparent {
218+
return *receiver;
219+
}
195220
return syn::Expr::Field(syn::ExprField {
196221
attrs,
197222
base: receiver,
@@ -216,6 +241,7 @@ impl Flag {
216241
repr: &syn::Type,
217242
flag_names: &HashSet<String>,
218243
out_of_line: bool,
244+
is_transparent: bool,
219245
) -> TokenStream {
220246
let Flag {
221247
ref attrs,
@@ -227,9 +253,10 @@ impl Flag {
227253
struct_name,
228254
flag_names,
229255
out_of_line,
256+
is_transparent,
230257
}
231258
.fold_expr(value.clone());
232-
let value = if out_of_line {
259+
let value = if out_of_line || is_transparent {
233260
quote! { ((#folded_value) as #repr) }
234261
} else {
235262
quote! { { bits: (#folded_value) as #repr } }
@@ -275,15 +302,21 @@ impl Parse for Flags {
275302
}
276303

277304
impl Flags {
278-
fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type, out_of_line: bool) -> TokenStream {
305+
fn expand(
306+
&self,
307+
struct_name: &syn::Ident,
308+
repr: &syn::Type,
309+
out_of_line: bool,
310+
is_transparent: bool,
311+
) -> TokenStream {
279312
let mut ts = quote! {};
280313
let flag_names = self
281314
.0
282315
.iter()
283316
.map(|flag| flag.name.to_string())
284317
.collect::<HashSet<_>>();
285318
for flag in &self.0 {
286-
ts.extend(flag.expand(struct_name, repr, &flag_names, out_of_line));
319+
ts.extend(flag.expand(struct_name, repr, &flag_names, out_of_line, is_transparent));
287320
}
288321
ts
289322
}

src/bindgen/parser.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,16 @@ impl Parse {
10171017
}
10181018
};
10191019

1020-
let (struct_, impl_) = bitflags.expand();
1020+
let out_of_line_transparent = {
1021+
let mut transparent = false;
1022+
let bitflag_ident = bitflags.self_ty_name().unraw().to_string();
1023+
self.structs.for_items(&Path::new(&bitflag_ident), |s| {
1024+
transparent |= s.is_transparent;
1025+
});
1026+
transparent
1027+
};
1028+
1029+
let (struct_, impl_) = bitflags.expand(out_of_line_transparent);
10211030
if let Some(struct_) = struct_ {
10221031
self.load_syn_struct(config, crate_name, mod_cfg, &struct_);
10231032
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
root;
3+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
root;
3+
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <stdarg.h>
2+
#include <stdbool.h>
3+
#include <stdint.h>
4+
#include <stdlib.h>
5+
6+
/**
7+
* Constants shared by multiple CSS Box Alignment properties
8+
*
9+
* These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
10+
*/
11+
typedef uint8_t AlignFlags;
12+
/**
13+
* 'auto'
14+
*/
15+
#define AlignFlags_AUTO (uint8_t)0
16+
/**
17+
* 'normal'
18+
*/
19+
#define AlignFlags_NORMAL (uint8_t)1
20+
/**
21+
* 'start'
22+
*/
23+
#define AlignFlags_START (uint8_t)(1 << 1)
24+
/**
25+
* 'end'
26+
*/
27+
#define AlignFlags_END (uint8_t)(1 << 2)
28+
#define AlignFlags_ALIAS (uint8_t)AlignFlags_END
29+
/**
30+
* 'flex-start'
31+
*/
32+
#define AlignFlags_FLEX_START (uint8_t)(1 << 3)
33+
#define AlignFlags_MIXED (uint8_t)(((1 << 4) | AlignFlags_FLEX_START) | AlignFlags_END)
34+
#define AlignFlags_MIXED_SELF (uint8_t)(((1 << 5) | AlignFlags_FLEX_START) | AlignFlags_END)
35+
36+
typedef uint32_t DebugFlags;
37+
/**
38+
* Flag with the topmost bit set of the u32
39+
*/
40+
#define DebugFlags_BIGGEST_ALLOWED (uint32_t)(1 << 31)
41+
42+
typedef uint64_t LargeFlags;
43+
/**
44+
* Flag with a very large shift that usually would be narrowed.
45+
*/
46+
#define LargeFlags_LARGE_SHIFT (uint64_t)(1ull << 44)
47+
#define LargeFlags_INVERTED (uint64_t)~LargeFlags_LARGE_SHIFT
48+
49+
typedef uint32_t OutOfLine;
50+
#define OutOfLine_A (uint32_t)1
51+
#define OutOfLine_B (uint32_t)2
52+
#define OutOfLine_AB (uint32_t)(OutOfLine_A | OutOfLine_B)
53+
54+
void root(AlignFlags flags,
55+
DebugFlags bigger_flags,
56+
LargeFlags largest_flags,
57+
OutOfLine out_of_line);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include <stdarg.h>
2+
#include <stdbool.h>
3+
#include <stdint.h>
4+
#include <stdlib.h>
5+
6+
/**
7+
* Constants shared by multiple CSS Box Alignment properties
8+
*
9+
* These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
10+
*/
11+
typedef uint8_t AlignFlags;
12+
/**
13+
* 'auto'
14+
*/
15+
#define AlignFlags_AUTO (uint8_t)0
16+
/**
17+
* 'normal'
18+
*/
19+
#define AlignFlags_NORMAL (uint8_t)1
20+
/**
21+
* 'start'
22+
*/
23+
#define AlignFlags_START (uint8_t)(1 << 1)
24+
/**
25+
* 'end'
26+
*/
27+
#define AlignFlags_END (uint8_t)(1 << 2)
28+
#define AlignFlags_ALIAS (uint8_t)AlignFlags_END
29+
/**
30+
* 'flex-start'
31+
*/
32+
#define AlignFlags_FLEX_START (uint8_t)(1 << 3)
33+
#define AlignFlags_MIXED (uint8_t)(((1 << 4) | AlignFlags_FLEX_START) | AlignFlags_END)
34+
#define AlignFlags_MIXED_SELF (uint8_t)(((1 << 5) | AlignFlags_FLEX_START) | AlignFlags_END)
35+
36+
typedef uint32_t DebugFlags;
37+
/**
38+
* Flag with the topmost bit set of the u32
39+
*/
40+
#define DebugFlags_BIGGEST_ALLOWED (uint32_t)(1 << 31)
41+
42+
typedef uint64_t LargeFlags;
43+
/**
44+
* Flag with a very large shift that usually would be narrowed.
45+
*/
46+
#define LargeFlags_LARGE_SHIFT (uint64_t)(1ull << 44)
47+
#define LargeFlags_INVERTED (uint64_t)~LargeFlags_LARGE_SHIFT
48+
49+
typedef uint32_t OutOfLine;
50+
#define OutOfLine_A (uint32_t)1
51+
#define OutOfLine_B (uint32_t)2
52+
#define OutOfLine_AB (uint32_t)(OutOfLine_A | OutOfLine_B)
53+
54+
#ifdef __cplusplus
55+
extern "C" {
56+
#endif // __cplusplus
57+
58+
void root(AlignFlags flags,
59+
DebugFlags bigger_flags,
60+
LargeFlags largest_flags,
61+
OutOfLine out_of_line);
62+
63+
#ifdef __cplusplus
64+
} // extern "C"
65+
#endif // __cplusplus
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <cstdarg>
2+
#include <cstdint>
3+
#include <cstdlib>
4+
#include <ostream>
5+
#include <new>
6+
7+
/// Constants shared by multiple CSS Box Alignment properties
8+
///
9+
/// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
10+
using AlignFlags = uint8_t;
11+
/// 'auto'
12+
constexpr static const AlignFlags AlignFlags_AUTO = (uint8_t)0;
13+
/// 'normal'
14+
constexpr static const AlignFlags AlignFlags_NORMAL = (uint8_t)1;
15+
/// 'start'
16+
constexpr static const AlignFlags AlignFlags_START = (uint8_t)(1 << 1);
17+
/// 'end'
18+
constexpr static const AlignFlags AlignFlags_END = (uint8_t)(1 << 2);
19+
constexpr static const AlignFlags AlignFlags_ALIAS = (uint8_t)AlignFlags_END;
20+
/// 'flex-start'
21+
constexpr static const AlignFlags AlignFlags_FLEX_START = (uint8_t)(1 << 3);
22+
constexpr static const AlignFlags AlignFlags_MIXED = (uint8_t)(((1 << 4) | AlignFlags_FLEX_START) | AlignFlags_END);
23+
constexpr static const AlignFlags AlignFlags_MIXED_SELF = (uint8_t)(((1 << 5) | AlignFlags_FLEX_START) | AlignFlags_END);
24+
25+
using DebugFlags = uint32_t;
26+
/// Flag with the topmost bit set of the u32
27+
constexpr static const DebugFlags DebugFlags_BIGGEST_ALLOWED = (uint32_t)(1 << 31);
28+
29+
using LargeFlags = uint64_t;
30+
/// Flag with a very large shift that usually would be narrowed.
31+
constexpr static const LargeFlags LargeFlags_LARGE_SHIFT = (uint64_t)(1ull << 44);
32+
constexpr static const LargeFlags LargeFlags_INVERTED = (uint64_t)~LargeFlags_LARGE_SHIFT;
33+
34+
using OutOfLine = uint32_t;
35+
constexpr static const OutOfLine OutOfLine_A = (uint32_t)1;
36+
constexpr static const OutOfLine OutOfLine_B = (uint32_t)2;
37+
constexpr static const OutOfLine OutOfLine_AB = (uint32_t)(OutOfLine_A | OutOfLine_B);
38+
39+
extern "C" {
40+
41+
void root(AlignFlags flags,
42+
DebugFlags bigger_flags,
43+
LargeFlags largest_flags,
44+
OutOfLine out_of_line);
45+
46+
} // extern "C"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
2+
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
3+
cdef extern from *:
4+
ctypedef bint bool
5+
ctypedef struct va_list
6+
7+
cdef extern from *:
8+
9+
# Constants shared by multiple CSS Box Alignment properties
10+
#
11+
# These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
12+
ctypedef uint8_t AlignFlags;
13+
# 'auto'
14+
const AlignFlags AlignFlags_AUTO # = <uint8_t>0
15+
# 'normal'
16+
const AlignFlags AlignFlags_NORMAL # = <uint8_t>1
17+
# 'start'
18+
const AlignFlags AlignFlags_START # = <uint8_t>(1 << 1)
19+
# 'end'
20+
const AlignFlags AlignFlags_END # = <uint8_t>(1 << 2)
21+
const AlignFlags AlignFlags_ALIAS # = <uint8_t>AlignFlags_END
22+
# 'flex-start'
23+
const AlignFlags AlignFlags_FLEX_START # = <uint8_t>(1 << 3)
24+
const AlignFlags AlignFlags_MIXED # = <uint8_t>(((1 << 4) | AlignFlags_FLEX_START) | AlignFlags_END)
25+
const AlignFlags AlignFlags_MIXED_SELF # = <uint8_t>(((1 << 5) | AlignFlags_FLEX_START) | AlignFlags_END)
26+
27+
ctypedef uint32_t DebugFlags;
28+
# Flag with the topmost bit set of the u32
29+
const DebugFlags DebugFlags_BIGGEST_ALLOWED # = <uint32_t>(1 << 31)
30+
31+
ctypedef uint64_t LargeFlags;
32+
# Flag with a very large shift that usually would be narrowed.
33+
const LargeFlags LargeFlags_LARGE_SHIFT # = <uint64_t>(1ull << 44)
34+
const LargeFlags LargeFlags_INVERTED # = <uint64_t>~LargeFlags_LARGE_SHIFT
35+
36+
ctypedef uint32_t OutOfLine;
37+
const OutOfLine OutOfLine_A # = <uint32_t>1
38+
const OutOfLine OutOfLine_B # = <uint32_t>2
39+
const OutOfLine OutOfLine_AB # = <uint32_t>(OutOfLine_A | OutOfLine_B)
40+
41+
void root(AlignFlags flags,
42+
DebugFlags bigger_flags,
43+
LargeFlags largest_flags,
44+
OutOfLine out_of_line);

0 commit comments

Comments
 (0)