Skip to content

Commit 3ed9434

Browse files
scovichemilio
authored andcommitted
Deduplicate the code that turns transparent structs into typedefs
All zero-sized structs now trigger a warning about undefined behavior, and an empty struct definition is emitted regardless of whether the user asked for repr(C) or repr(transparent).
1 parent 58c6156 commit 3ed9434

File tree

15 files changed

+119
-51
lines changed

15 files changed

+119
-51
lines changed

src/bindgen/ir/structure.rs

Lines changed: 26 additions & 2 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, Constant, Documentation, Field, GenericArgument, GenericParams, Item,
14-
ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type,
14+
ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type, Typedef,
1515
};
1616
use crate::bindgen::library::Library;
1717
use crate::bindgen::mangle;
@@ -122,11 +122,27 @@ impl Struct {
122122
has_tag_field: bool,
123123
is_enum_variant_body: bool,
124124
alignment: Option<ReprAlign>,
125-
is_transparent: bool,
125+
mut is_transparent: bool,
126126
cfg: Option<Cfg>,
127127
annotations: AnnotationSet,
128128
documentation: Documentation,
129129
) -> Self {
130+
// WARNING: Zero-sized transparent structs are legal rust [1], but zero-sized types of any
131+
// repr are "best avoided entirely" [2] because they "will be nonsensical or problematic if
132+
// passed through the FFI boundary" [3]. Further, because no well-defined underlying native
133+
// type exists for a ZST, we cannot emit a typedef and must define an empty struct instead.
134+
//
135+
// [1] https://github.com/rust-lang/rust/issues/77841#issuecomment-716575747
136+
// [2] https://github.com/rust-lang/rust/issues/77841#issuecomment-716796313
137+
// [3] https://doc.rust-lang.org/nomicon/other-reprs.html
138+
if fields.is_empty() {
139+
warn!(
140+
"Passing zero-sized struct {} across the FFI boundary is undefined behavior",
141+
&path
142+
);
143+
is_transparent = false;
144+
}
145+
130146
let export_name = path.name().to_owned();
131147
Self {
132148
path,
@@ -150,6 +166,14 @@ impl Struct {
150166
}
151167
}
152168

169+
/// Attempts to convert this struct to a typedef (only works for transparent structs).
170+
pub fn as_typedef(&self) -> Option<Typedef> {
171+
match self.fields.first() {
172+
Some(field) if self.is_transparent => Some(Typedef::new_from_struct_field(self, field)),
173+
_ => None,
174+
}
175+
}
176+
153177
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
154178
// Generic structs can instantiate monomorphs only once they've been
155179
// instantiated. See `instantiate_monomorph` for more details.

src/bindgen/ir/typedef.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::bindgen::config::Config;
1010
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
1111
use crate::bindgen::dependencies::Dependencies;
1212
use crate::bindgen::ir::{
13-
AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path,
14-
Type,
13+
AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer,
14+
Path, Struct, Type,
1515
};
1616
use crate::bindgen::library::Library;
1717
use crate::bindgen::mangle;
@@ -70,6 +70,19 @@ impl Typedef {
7070
self.aliased.simplify_standard_types(config);
7171
}
7272

73+
// Used to convert a transparent Struct to a Typedef.
74+
pub fn new_from_struct_field(item: &Struct, field: &Field) -> Self {
75+
Self {
76+
path: item.path().clone(),
77+
export_name: item.export_name().to_string(),
78+
generic_params: item.generic_params.clone(),
79+
aliased: field.ty.clone(),
80+
cfg: item.cfg().cloned(),
81+
annotations: item.annotations().clone(),
82+
documentation: item.documentation().clone(),
83+
}
84+
}
85+
7386
pub fn transfer_annotations(&mut self, out: &mut HashMap<Path, AnnotationSet>) {
7487
if self.annotations.is_empty() {
7588
return;

src/bindgen/language_backend/clike.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -540,24 +540,6 @@ impl LanguageBackend for CLikeLanguageBackend<'_> {
540540
}
541541

542542
fn write_struct<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Struct) {
543-
if s.is_transparent {
544-
let typedef = Typedef {
545-
path: s.path.clone(),
546-
export_name: s.export_name.to_owned(),
547-
generic_params: s.generic_params.clone(),
548-
aliased: s.fields[0].ty.clone(),
549-
cfg: s.cfg.clone(),
550-
annotations: s.annotations.clone(),
551-
documentation: s.documentation.clone(),
552-
};
553-
self.write_type_def(out, &typedef);
554-
for constant in &s.associated_constants {
555-
out.new_line();
556-
constant.write(self.config, self, out, Some(s));
557-
}
558-
return;
559-
}
560-
561543
let condition = s.cfg.to_condition(self.config);
562544
condition.write_before(self.config, out);
563545

src/bindgen/language_backend/cython.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -159,24 +159,6 @@ impl LanguageBackend for CythonLanguageBackend<'_> {
159159
}
160160

161161
fn write_struct<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Struct) {
162-
if s.is_transparent {
163-
let typedef = Typedef {
164-
path: s.path.clone(),
165-
export_name: s.export_name.to_owned(),
166-
generic_params: s.generic_params.clone(),
167-
aliased: s.fields[0].ty.clone(),
168-
cfg: s.cfg.clone(),
169-
annotations: s.annotations.clone(),
170-
documentation: s.documentation.clone(),
171-
};
172-
self.write_type_def(out, &typedef);
173-
for constant in &s.associated_constants {
174-
out.new_line();
175-
constant.write(self.config, self, out, Some(s));
176-
}
177-
return;
178-
}
179-
180162
let condition = s.cfg.to_condition(self.config);
181163
condition.write_before(self.config, out);
182164

src/bindgen/language_backend/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,24 @@ pub trait LanguageBackend: Sized {
139139
}
140140
}
141141

142+
/// If the struct is transparent, emit a typedef of its NZST field type instead.
143+
fn write_struct_or_typedef<W: Write>(
144+
&mut self,
145+
out: &mut SourceWriter<W>,
146+
s: &Struct,
147+
b: &Bindings,
148+
) {
149+
if let Some(typedef) = s.as_typedef() {
150+
self.write_type_def(out, &typedef);
151+
for constant in &s.associated_constants {
152+
out.new_line();
153+
constant.write(&b.config, self, out, Some(s));
154+
}
155+
} else {
156+
self.write_struct(out, s);
157+
}
158+
}
159+
142160
fn write_items<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
143161
for item in &b.items {
144162
if item
@@ -155,7 +173,7 @@ pub trait LanguageBackend: Sized {
155173
ItemContainer::Constant(..) => unreachable!(),
156174
ItemContainer::Static(..) => unreachable!(),
157175
ItemContainer::Enum(ref x) => self.write_enum(out, x),
158-
ItemContainer::Struct(ref x) => self.write_struct(out, x),
176+
ItemContainer::Struct(ref x) => self.write_struct_or_typedef(out, x, b),
159177
ItemContainer::Union(ref x) => self.write_union(out, x),
160178
ItemContainer::OpaqueItem(ref x) => self.write_opaque_item(out, x),
161179
ItemContainer::Typedef(ref x) => self.write_type_def(out, x),

tests/expectations/transparent.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ typedef uint32_t TransparentPrimitiveWithAssociatedConstants;
2323
#define TransparentPrimitiveWithAssociatedConstants_ZERO 0
2424
#define TransparentPrimitiveWithAssociatedConstants_ONE 1
2525

26+
typedef struct {
27+
28+
} TransparentEmptyStructure;
29+
2630
#define EnumWithAssociatedConstantInImpl_TEN 10
2731

2832
void root(TransparentComplexWrappingStructTuple a,
@@ -32,4 +36,5 @@ void root(TransparentComplexWrappingStructTuple a,
3236
TransparentComplexWrapper_i32 e,
3337
TransparentPrimitiveWrapper_i32 f,
3438
TransparentPrimitiveWithAssociatedConstants g,
35-
EnumWithAssociatedConstantInImpl h);
39+
TransparentEmptyStructure h,
40+
EnumWithAssociatedConstantInImpl i);

tests/expectations/transparent.compat.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ typedef uint32_t TransparentPrimitiveWithAssociatedConstants;
2323
#define TransparentPrimitiveWithAssociatedConstants_ZERO 0
2424
#define TransparentPrimitiveWithAssociatedConstants_ONE 1
2525

26+
typedef struct {
27+
28+
} TransparentEmptyStructure;
29+
2630
#define EnumWithAssociatedConstantInImpl_TEN 10
2731

2832
#ifdef __cplusplus
@@ -36,7 +40,8 @@ void root(TransparentComplexWrappingStructTuple a,
3640
TransparentComplexWrapper_i32 e,
3741
TransparentPrimitiveWrapper_i32 f,
3842
TransparentPrimitiveWithAssociatedConstants g,
39-
EnumWithAssociatedConstantInImpl h);
43+
TransparentEmptyStructure h,
44+
EnumWithAssociatedConstantInImpl i);
4045

4146
#ifdef __cplusplus
4247
} // extern "C"

tests/expectations/transparent.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ using TransparentPrimitiveWithAssociatedConstants = uint32_t;
2626
constexpr static const TransparentPrimitiveWithAssociatedConstants TransparentPrimitiveWithAssociatedConstants_ZERO = 0;
2727
constexpr static const TransparentPrimitiveWithAssociatedConstants TransparentPrimitiveWithAssociatedConstants_ONE = 1;
2828

29+
struct TransparentEmptyStructure {
30+
31+
};
32+
2933
constexpr static const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_TEN = 10;
3034

3135
extern "C" {
@@ -37,6 +41,7 @@ void root(TransparentComplexWrappingStructTuple a,
3741
TransparentComplexWrapper<int32_t> e,
3842
TransparentPrimitiveWrapper<int32_t> f,
3943
TransparentPrimitiveWithAssociatedConstants g,
40-
EnumWithAssociatedConstantInImpl h);
44+
TransparentEmptyStructure h,
45+
EnumWithAssociatedConstantInImpl i);
4146

4247
} // extern "C"

tests/expectations/transparent.pyx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ cdef extern from *:
2828
const TransparentPrimitiveWithAssociatedConstants TransparentPrimitiveWithAssociatedConstants_ZERO # = 0
2929
const TransparentPrimitiveWithAssociatedConstants TransparentPrimitiveWithAssociatedConstants_ONE # = 1
3030

31+
ctypedef struct TransparentEmptyStructure:
32+
pass
33+
3134
const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_TEN # = 10
3235

3336
void root(TransparentComplexWrappingStructTuple a,
@@ -37,4 +40,5 @@ cdef extern from *:
3740
TransparentComplexWrapper_i32 e,
3841
TransparentPrimitiveWrapper_i32 f,
3942
TransparentPrimitiveWithAssociatedConstants g,
40-
EnumWithAssociatedConstantInImpl h);
43+
TransparentEmptyStructure h,
44+
EnumWithAssociatedConstantInImpl i);

tests/expectations/transparent_both.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ typedef uint32_t TransparentPrimitiveWithAssociatedConstants;
2323
#define TransparentPrimitiveWithAssociatedConstants_ZERO 0
2424
#define TransparentPrimitiveWithAssociatedConstants_ONE 1
2525

26+
typedef struct TransparentEmptyStructure {
27+
28+
} TransparentEmptyStructure;
29+
2630
#define EnumWithAssociatedConstantInImpl_TEN 10
2731

2832
void root(TransparentComplexWrappingStructTuple a,
@@ -32,4 +36,5 @@ void root(TransparentComplexWrappingStructTuple a,
3236
TransparentComplexWrapper_i32 e,
3337
TransparentPrimitiveWrapper_i32 f,
3438
TransparentPrimitiveWithAssociatedConstants g,
35-
struct EnumWithAssociatedConstantInImpl h);
39+
struct TransparentEmptyStructure h,
40+
struct EnumWithAssociatedConstantInImpl i);

0 commit comments

Comments
 (0)