Skip to content

Commit 8c76d22

Browse files
committed
Add support for transparent enum
1 parent 8a76012 commit 8c76d22

20 files changed

+992
-233
lines changed

docs.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,12 @@ You can learn about all of the different repr attributes [by reading Rust's refe
133133

134134
* `#[repr(C)]`: give this struct/union/enum the same layout and ABI C would
135135
* `#[repr(u8, u16, ... etc)]`: give this enum the same layout and ABI as the given integer type
136-
* `#[repr(transparent)]`: give this single-field struct the same ABI as its field (useful for newtyping integers but keeping the integer ABI)
136+
* `#[repr(transparent)]`: give this single-field struct or enum the same ABI as its field (useful for newtyping integers but keeping the integer ABI)
137137

138138
cbindgen supports the `#[repr(align(N))]` and `#[repr(packed)]` attributes, but currently does not support `#[repr(packed(N))]`.
139139

140+
cbindgen supports using `repr(transparent)` on single-field structs and single-variant enums with fields. Transparent structs and enums are exported as typedefs that alias the underlying single field's type.
141+
140142
cbindgen also supports using `repr(C)`/`repr(u8)` on non-C-like enums (enums with fields). This gives a C-compatible tagged union layout, as [defined by this RFC 2195][really-tagged-unions]. `repr(C)` will give a simpler layout that is perhaps more intuitive, while `repr(u8)` will produce a more compact layout.
141143

142144
If you ensure everything has a guaranteed repr, then cbindgen will generate definitions for:
@@ -407,9 +409,17 @@ The rest are just local overrides for the same options found in the cbindgen.tom
407409

408410
### Enum Annotations
409411

410-
* enum-trailing-values=\[variant1, variant2, ...\] -- add the following fieldless enum variants to the end of the enum's definition. These variant names *will* have the enum's renaming rules applied.
412+
* enum-trailing-values=\[variant1, variant2, ...\] -- add the following fieldless enum variants to
413+
the end of the enum's definition. These variant names *will* have the enum's renaming rules
414+
applied.
415+
416+
WARNING: if any of these values are ever passed into Rust, behaviour will be Undefined. Rust does
417+
not know about them, and will assume they cannot happen.
411418

412-
WARNING: if any of these values are ever passed into Rust, behaviour will be Undefined. Rust does not know about them, and will assume they cannot happen.
419+
* transparent-typedef -- when emitting the typedef for a transparent enum, mark it as
420+
transparent. All references to the enum will be replaced with the type of its underlying NZST
421+
variant field, effectively making the enum invisible on the FFI side. For exmaples of how this
422+
works, see [Struct Annotations](#struct-annotations).
413423

414424
The rest are just local overrides for the same options found in the cbindgen.toml:
415425

src/bindgen/ir/enumeration.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::bindgen::dependencies::Dependencies;
1212
use crate::bindgen::ir::{
1313
AnnotationSet, AnnotationValue, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, Field,
1414
GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr,
15-
ReprStyle, Struct, ToCondition, TransparentTypeEraser, Type,
15+
ReprStyle, Struct, ToCondition, TransparentTypeEraser, Type, Typedef,
1616
};
1717
use crate::bindgen::language_backend::LanguageBackend;
1818
use crate::bindgen::library::Library;
@@ -311,6 +311,10 @@ impl Enum {
311311
repr.style != ReprStyle::C
312312
}
313313

314+
pub fn is_transparent(&self) -> bool {
315+
self.repr.style == ReprStyle::Transparent
316+
}
317+
314318
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
315319
if self.is_generic() {
316320
return;
@@ -438,6 +442,31 @@ impl Enum {
438442
documentation,
439443
}
440444
}
445+
446+
/// Attempts to convert this enum to a typedef (only works for transparent enums).
447+
pub fn as_typedef(&self) -> Option<Typedef> {
448+
if self.is_transparent() {
449+
// NOTE: A `#[repr(transparent)]` enum fails to compile unless it has
450+
// exactly one NZT variant. So we can safely assume the variant exists.
451+
if let Some(EnumVariant {
452+
body: VariantBody::Body { ref body, .. },
453+
..
454+
}) = self.variants.first()
455+
{
456+
// NOTE: Inline tagged enum has the tag field first, ignore it.
457+
if let Some(field) = body.fields.last() {
458+
return Some(Typedef::new_from_item_field(self, field));
459+
}
460+
}
461+
}
462+
None
463+
}
464+
465+
// Transparent enums become typedefs, so try converting to typedef and recurse on that.
466+
pub fn as_transparent_alias(&self, generics: &[GenericArgument]) -> Option<Type> {
467+
self.as_typedef()
468+
.and_then(|t| t.as_transparent_alias(generics))
469+
}
441470
}
442471

443472
impl Item for Enum {
@@ -470,7 +499,9 @@ impl Item for Enum {
470499
}
471500

472501
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
473-
if self.tag.is_some() {
502+
if self.repr.style == ReprStyle::Transparent {
503+
resolver.add_none(&self.path);
504+
} else if self.tag.is_some() {
474505
if self.repr.style == ReprStyle::C {
475506
resolver.add_struct(&self.path);
476507
} else {

src/bindgen/ir/structure.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ impl Struct {
150150
// NOTE: A `#[repr(transparent)]` struct with 2+ NZT fields fails to compile, but 0
151151
// fields is allowed for some strange reason. Don't emit the typedef in that case.
152152
if let Some(field) = self.fields.first() {
153-
return Some(Typedef::new_from_struct_field(self, field));
153+
return Some(Typedef::new_from_item_field(self, field));
154154
} else {
155155
error!(
156156
"Cannot convert empty transparent struct {} to typedef",

src/bindgen/ir/ty.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ impl Type {
569569
ItemContainer::OpaqueItem(o) => o.as_transparent_alias(&generics),
570570
ItemContainer::Typedef(t) => t.as_transparent_alias(&generics),
571571
ItemContainer::Struct(s) => s.as_transparent_alias(&generics),
572+
ItemContainer::Enum(e) => e.as_transparent_alias(&generics),
572573
_ => None,
573574
};
574575
if let Some(mut ty) = aliased_ty {

src/bindgen/ir/typedef.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
1111
use crate::bindgen::dependencies::Dependencies;
1212
use crate::bindgen::ir::{
1313
AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer,
14-
Path, Struct, TransparentTypeEraser, Type,
14+
Path, TransparentTypeEraser, Type,
1515
};
1616
use crate::bindgen::library::Library;
1717
use crate::bindgen::mangle;
@@ -69,12 +69,12 @@ impl Typedef {
6969
}
7070
}
7171

72-
// Used to convert a transparent Struct to a Typedef.
73-
pub fn new_from_struct_field(item: &Struct, field: &Field) -> Self {
72+
// Used to convert a transparent Struct or Enum to a Typedef.
73+
pub fn new_from_item_field(item: &impl Item, field: &Field) -> Self {
7474
Self {
7575
path: item.path().clone(),
7676
export_name: item.export_name().to_string(),
77-
generic_params: item.generic_params.clone(),
77+
generic_params: item.generic_params().clone(),
7878
aliased: field.ty.clone(),
7979
cfg: item.cfg().cloned(),
8080
annotations: item.annotations().clone(),

src/bindgen/language_backend/mod.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,27 @@ pub trait LanguageBackend: Sized {
157157
}
158158
}
159159

160+
/// If the enum is transparent, emit a typedef of its NZST field type instead.
161+
fn write_enum_or_typedef<W: Write>(
162+
&mut self,
163+
out: &mut SourceWriter<W>,
164+
e: &Enum,
165+
_b: &Bindings,
166+
) {
167+
if let Some(typedef) = e.as_typedef() {
168+
self.write_type_def(out, &typedef);
169+
// TODO: Associated constants are not supported for enums. Should they be? Rust
170+
// enum exports as a union, and C/C++ at least supports static union members?
171+
//
172+
//for constant in &e.associated_constants {
173+
// out.new_line();
174+
// constant.write(b.config, self, out, Some(e));
175+
//}
176+
} else {
177+
self.write_enum(out, e);
178+
}
179+
}
180+
160181
fn write_items<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
161182
for item in &b.items {
162183
if item
@@ -172,7 +193,7 @@ pub trait LanguageBackend: Sized {
172193
match *item {
173194
ItemContainer::Constant(..) => unreachable!(),
174195
ItemContainer::Static(..) => unreachable!(),
175-
ItemContainer::Enum(ref x) => self.write_enum(out, x),
196+
ItemContainer::Enum(ref x) => self.write_enum_or_typedef(out, x, b),
176197
ItemContainer::Struct(ref x) => self.write_struct_or_typedef(out, x, b),
177198
ItemContainer::Union(ref x) => self.write_union(out, x),
178199
ItemContainer::OpaqueItem(ref x) => self.write_opaque_item(out, x),

tests/expectations/const_transparent.compat.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,29 @@
33
#include <stdint.h>
44
#include <stdlib.h>
55

6+
#define TransparentEnum_ASSOC_ENUM_FOO 8
7+
68
typedef uint8_t TransparentStruct;
79
#define TransparentStruct_ASSOC_STRUCT_FOO 1
810
#define TransparentStruct_ASSOC_STRUCT_BAR 2
911

1012

1113
typedef uint8_t TransparentTupleStruct;
1214

15+
typedef uint8_t TransparentEnum;
16+
1317
#define STRUCT_FOO 4
1418

1519
#define STRUCT_BAR 5
1620

1721

1822

1923

24+
25+
26+
27+
28+
29+
30+
31+

tests/expectations/const_transparent.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <ostream>
55
#include <new>
66

7+
constexpr static const int64_t TransparentEnum_ASSOC_ENUM_FOO = 8;
8+
79
template<typename T>
810
using Wrapper = T;
911

@@ -17,10 +19,23 @@ using TransparentTupleStruct = uint8_t;
1719
template<typename T>
1820
using TransparentStructWithErasedField = T;
1921

22+
using TransparentEnum = uint8_t;
23+
24+
template<typename T>
25+
using TransparentWrapperEnum = T;
26+
2027
constexpr static const TransparentStruct STRUCT_FOO = 4;
2128

2229
constexpr static const TransparentTupleStruct STRUCT_BAR = 5;
2330

2431
constexpr static const TransparentStruct STRUCT_BAZ = 6;
2532

2633
constexpr static const TransparentStructWithErasedField<TransparentStruct> COMPLEX = 7;
34+
35+
36+
37+
38+
39+
40+
41+

tests/expectations/const_transparent.pyx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,29 @@ cdef extern from *:
66

77
cdef extern from *:
88

9+
const int64_t TransparentEnum_ASSOC_ENUM_FOO # = 8
10+
911
ctypedef uint8_t TransparentStruct;
1012
const int64_t TransparentStruct_ASSOC_STRUCT_FOO # = 1
1113
const TransparentStruct TransparentStruct_ASSOC_STRUCT_BAR # = 2
1214

1315

1416
ctypedef uint8_t TransparentTupleStruct;
1517

18+
ctypedef uint8_t TransparentEnum;
19+
1620
const TransparentStruct STRUCT_FOO # = 4
1721

1822
const TransparentTupleStruct STRUCT_BAR # = 5
1923

2024

2125

2226

27+
28+
29+
30+
31+
32+
33+
34+

tests/expectations/transparent.c

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,48 +13,77 @@ typedef DummyStruct TransparentComplexWrappingStructTuple;
1313

1414
typedef uint32_t TransparentPrimitiveWrappingStructTuple;
1515

16-
typedef DummyStruct TransparentComplexWrappingStructure;
16+
typedef DummyStruct TransparentComplexWrappingStruct;
1717

18-
typedef uint32_t TransparentPrimitiveWrappingStructure;
18+
typedef uint32_t TransparentPrimitiveWrappingStruct;
1919

20-
typedef DummyStruct TransparentComplexWrapper_i32;
20+
typedef DummyStruct TransparentComplexWrapperStruct_i32;
2121

22-
typedef uint32_t TransparentPrimitiveWrapper_i32;
22+
typedef uint32_t TransparentPrimitiveWrapperStruct_i32;
2323

24-
typedef uint32_t TransparentPrimitiveWithAssociatedConstants;
25-
#define TransparentPrimitiveWithAssociatedConstants_ZERO 0
26-
#define TransparentPrimitiveWithAssociatedConstants_ONE 1
24+
typedef uint32_t TransparentPrimitiveStructWithAssociatedConstants;
25+
#define TransparentPrimitiveStructWithAssociatedConstants_STRUCT_ZERO 0
26+
#define TransparentPrimitiveStructWithAssociatedConstants_STRUCT_ONE 1
2727

28-
typedef const uint32_t *TransparentPointerWrappingStructure;
28+
typedef const uint32_t *TransparentPointerWrappingStruct;
2929

3030
typedef int32_t TransparentIntStruct;
3131

3232
typedef DummyStruct TransparentComplexStruct;
3333

34-
typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct;
34+
typedef TransparentPrimitiveWrappingStruct TransparentTransparentStruct;
3535

3636
typedef uint32_t *TransparentNonNullStruct;
3737

3838
typedef uint32_t *TransparentOptionNonNullStruct;
3939

40+
typedef DummyStruct TransparentComplexWrappingEnumTuple;
41+
42+
typedef uint32_t TransparentPrimitiveWrappingEnumTuple;
43+
44+
typedef DummyStruct TransparentComplexWrappingEnum;
45+
46+
typedef uint32_t TransparentPrimitiveWrappingEnum;
47+
48+
typedef DummyStruct TransparentComplexWrapperEnum_i32;
49+
50+
typedef uint32_t TransparentPrimitiveWrapperEnum_i32;
51+
52+
typedef uint32_t TransparentPrimitiveEnumWithAssociatedConstants;
53+
54+
typedef const uint32_t *TransparentPointerWrappingEnum;
55+
56+
typedef int32_t TransparentIntEnum;
57+
58+
typedef DummyStruct TransparentComplexEnum;
59+
60+
typedef TransparentPrimitiveWrappingEnum TransparentTransparentEnum;
61+
62+
typedef uint32_t *TransparentNonNullEnum;
63+
64+
typedef uint32_t *TransparentOptionNonNullEnum;
65+
4066
#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10
4167

68+
69+
70+
71+
4272
#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10
4373

44-
void root(TransparentComplexWrappingStructTuple a,
45-
TransparentPrimitiveWrappingStructTuple b,
46-
TransparentComplexWrappingStructure c,
47-
TransparentPrimitiveWrappingStructure d,
48-
TransparentComplexWrapper_i32 e,
49-
TransparentPrimitiveWrapper_i32 f,
50-
TransparentPrimitiveWithAssociatedConstants g,
51-
TransparentPointerWrappingStructure h,
52-
StructWithAssociatedConstantInImpl i,
53-
EnumWithAssociatedConstantInImpl j);
74+
void struct_root(TransparentComplexWrappingStructTuple a,
75+
TransparentPrimitiveWrappingStructTuple b,
76+
TransparentComplexWrappingStruct c,
77+
TransparentPrimitiveWrappingStruct d,
78+
TransparentComplexWrapperStruct_i32 e,
79+
TransparentPrimitiveWrapperStruct_i32 f,
80+
TransparentPrimitiveStructWithAssociatedConstants g,
81+
TransparentPointerWrappingStruct h,
82+
StructWithAssociatedConstantInImpl i);
5483

5584
void erased_root(uint32_t *a,
5685
uint32_t *b,
57-
TransparentPrimitiveWrappingStructure c,
86+
TransparentPrimitiveWrappingStruct c,
5887
uint32_t *d,
5988
TransparentIntStruct e,
6089
int32_t f,
@@ -66,3 +95,28 @@ void erased_root(uint32_t *a,
6695
TransparentTransparentStruct l,
6796
TransparentNonNullStruct m,
6897
TransparentOptionNonNullStruct n);
98+
99+
void enum_root(TransparentComplexWrappingEnumTuple a,
100+
TransparentPrimitiveWrappingEnumTuple b,
101+
TransparentComplexWrappingEnum c,
102+
TransparentPrimitiveWrappingEnum d,
103+
TransparentComplexWrapperEnum_i32 e,
104+
TransparentPrimitiveWrapperEnum_i32 f,
105+
TransparentPrimitiveEnumWithAssociatedConstants g,
106+
TransparentPointerWrappingEnum h,
107+
EnumWithAssociatedConstantInImpl i);
108+
109+
void erased_enum_root(uint32_t *a,
110+
uint32_t *b,
111+
TransparentPrimitiveWrappingEnum c,
112+
uint32_t *d,
113+
TransparentIntEnum e,
114+
int32_t f,
115+
DummyStruct g,
116+
uint32_t *h,
117+
int32_t i,
118+
TransparentIntEnum j,
119+
TransparentComplexEnum k,
120+
TransparentTransparentEnum l,
121+
TransparentNonNullEnum m,
122+
TransparentOptionNonNullEnum n);

0 commit comments

Comments
 (0)