Skip to content

Commit 8ac787a

Browse files
committed
codegen: Track union layout more accurately.
Instead of always generating the _bindgen_union_align method (which shouldn't be needed at all for Rust structs, since the struct layout tracker already deals with adding repr(align) as necessary) make sure to visit all fields appropriately to generate the correct alignment.
1 parent 17476e9 commit 8ac787a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+143
-129
lines changed

bindgen-integration/src/lib.rs

-2
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,6 @@ fn test_macro_customintkind_path() {
260260
assert!(v.is::<MacroInteger>())
261261
}
262262

263-
// https://github.com/rust-lang/rust-bindgen/issues/1973
264-
#[cfg_attr(target_arch = "aarch64", should_panic)] // This line should be removed after the bug linked above is fixed
265263
#[test]
266264
fn test_homogeneous_aggregate_float_union() {
267265
unsafe {

src/codegen/mod.rs

+59-54
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ impl<'a> FieldCodegen<'a> for FieldData {
12191219
ty.append_implicit_template_params(ctx, field_item);
12201220

12211221
// NB: If supported, we use proper `union` types.
1222-
let ty = if parent.is_union() && !parent.can_be_rust_union(ctx) {
1222+
let ty = if parent.is_union() && !struct_layout.is_rust_union() {
12231223
result.saw_bindgen_union();
12241224
if ctx.options().enable_cxx_namespaces {
12251225
quote! {
@@ -1263,12 +1263,10 @@ impl<'a> FieldCodegen<'a> for FieldData {
12631263
.expect("Each field should have a name in codegen!");
12641264
let field_ident = ctx.rust_ident_raw(field_name.as_str());
12651265

1266-
if !parent.is_union() {
1267-
if let Some(padding_field) =
1268-
struct_layout.pad_field(&field_name, field_ty, self.offset())
1269-
{
1270-
fields.extend(Some(padding_field));
1271-
}
1266+
if let Some(padding_field) =
1267+
struct_layout.saw_field(&field_name, field_ty, self.offset())
1268+
{
1269+
fields.extend(Some(padding_field));
12721270
}
12731271

12741272
let is_private = (!self.is_public() &&
@@ -1433,7 +1431,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit {
14331431
let layout = self.layout();
14341432
let unit_field_ty = helpers::bitfield_unit(ctx, layout);
14351433
let field_ty = {
1436-
if parent.is_union() && !parent.can_be_rust_union(ctx) {
1434+
if parent.is_union() && !struct_layout.is_rust_union() {
14371435
result.saw_bindgen_union();
14381436
if ctx.options().enable_cxx_namespaces {
14391437
quote! {
@@ -1571,7 +1569,7 @@ impl<'a> FieldCodegen<'a> for Bitfield {
15711569
_accessor_kind: FieldAccessorKind,
15721570
parent: &CompInfo,
15731571
_result: &mut CodegenResult,
1574-
_struct_layout: &mut StructLayoutTracker,
1572+
struct_layout: &mut StructLayoutTracker,
15751573
_fields: &mut F,
15761574
methods: &mut M,
15771575
(unit_field_name, bitfield_representable_as_int): (&'a str, &mut bool),
@@ -1612,7 +1610,7 @@ impl<'a> FieldCodegen<'a> for Bitfield {
16121610
self.is_public() && !fields_should_be_private,
16131611
);
16141612

1615-
if parent.is_union() && !parent.can_be_rust_union(ctx) {
1613+
if parent.is_union() && !struct_layout.is_rust_union() {
16161614
methods.extend(Some(quote! {
16171615
#[inline]
16181616
#access_spec fn #getter_name(&self) -> #bitfield_ty {
@@ -1768,15 +1766,53 @@ impl CodeGenerator for CompInfo {
17681766
}
17691767
}
17701768

1771-
let is_union = self.kind() == CompKind::Union;
1772-
let layout = item.kind().expect_type().layout(ctx);
1773-
1774-
let mut explicit_align = None;
17751769
if is_opaque {
17761770
// Opaque item should not have generated methods, fields.
17771771
debug_assert!(fields.is_empty());
17781772
debug_assert!(methods.is_empty());
1773+
}
17791774

1775+
let is_union = self.kind() == CompKind::Union;
1776+
let layout = item.kind().expect_type().layout(ctx);
1777+
let zero_sized = item.is_zero_sized(ctx);
1778+
let forward_decl = self.is_forward_declaration();
1779+
1780+
let mut explicit_align = None;
1781+
1782+
// C++ requires every struct to be addressable, so what C++ compilers do
1783+
// is making the struct 1-byte sized.
1784+
//
1785+
// This is apparently not the case for C, see:
1786+
// https://github.com/rust-lang/rust-bindgen/issues/551
1787+
//
1788+
// Just get the layout, and assume C++ if not.
1789+
//
1790+
// NOTE: This check is conveniently here to avoid the dummy fields we
1791+
// may add for unused template parameters.
1792+
if !forward_decl && zero_sized {
1793+
let has_address = if is_opaque {
1794+
// Generate the address field if it's an opaque type and
1795+
// couldn't determine the layout of the blob.
1796+
layout.is_none()
1797+
} else {
1798+
layout.map_or(true, |l| l.size != 0)
1799+
};
1800+
1801+
if has_address {
1802+
let layout = Layout::new(1, 1);
1803+
let ty = helpers::blob(ctx, Layout::new(1, 1));
1804+
struct_layout.saw_field_with_layout(
1805+
"_address",
1806+
layout,
1807+
/* offset = */ Some(0),
1808+
);
1809+
fields.push(quote! {
1810+
pub _address: #ty,
1811+
});
1812+
}
1813+
}
1814+
1815+
if is_opaque {
17801816
match layout {
17811817
Some(l) => {
17821818
explicit_align = Some(l.align);
@@ -1790,7 +1826,7 @@ impl CodeGenerator for CompInfo {
17901826
warn!("Opaque type without layout! Expect dragons!");
17911827
}
17921828
}
1793-
} else if !is_union && !item.is_zero_sized(ctx) {
1829+
} else if !is_union && !zero_sized {
17941830
if let Some(padding_field) =
17951831
layout.and_then(|layout| struct_layout.pad_struct(layout))
17961832
{
@@ -1815,57 +1851,26 @@ impl CodeGenerator for CompInfo {
18151851
}
18161852
}
18171853
}
1818-
} else if is_union && !self.is_forward_declaration() {
1854+
} else if is_union && !forward_decl {
18191855
// TODO(emilio): It'd be nice to unify this with the struct path
18201856
// above somehow.
18211857
let layout = layout.expect("Unable to get layout information?");
1822-
struct_layout.saw_union(layout);
1823-
18241858
if struct_layout.requires_explicit_align(layout) {
18251859
explicit_align = Some(layout.align);
18261860
}
18271861

1828-
let ty = helpers::blob(ctx, layout);
1829-
fields.push(if self.can_be_rust_union(ctx) {
1830-
quote! {
1831-
_bindgen_union_align: #ty ,
1832-
}
1833-
} else {
1834-
quote! {
1862+
if !struct_layout.is_rust_union() {
1863+
let ty = helpers::blob(ctx, layout);
1864+
fields.push(quote! {
18351865
pub bindgen_union_field: #ty ,
1836-
}
1837-
});
1866+
})
1867+
}
18381868
}
18391869

1840-
// C++ requires every struct to be addressable, so what C++ compilers do
1841-
// is making the struct 1-byte sized.
1842-
//
1843-
// This is apparently not the case for C, see:
1844-
// https://github.com/rust-lang/rust-bindgen/issues/551
1845-
//
1846-
// Just get the layout, and assume C++ if not.
1847-
//
1848-
// NOTE: This check is conveniently here to avoid the dummy fields we
1849-
// may add for unused template parameters.
1850-
if self.is_forward_declaration() {
1870+
if forward_decl {
18511871
fields.push(quote! {
18521872
_unused: [u8; 0],
18531873
});
1854-
} else if item.is_zero_sized(ctx) {
1855-
let has_address = if is_opaque {
1856-
// Generate the address field if it's an opaque type and
1857-
// couldn't determine the layout of the blob.
1858-
layout.is_none()
1859-
} else {
1860-
layout.map_or(true, |l| l.size != 0)
1861-
};
1862-
1863-
if has_address {
1864-
let ty = helpers::blob(ctx, Layout::new(1, 1));
1865-
fields.push(quote! {
1866-
pub _address: #ty,
1867-
});
1868-
}
18691874
}
18701875

18711876
let mut generic_param_names = vec![];
@@ -1963,7 +1968,7 @@ impl CodeGenerator for CompInfo {
19631968
attributes.push(attributes::derives(&derives))
19641969
}
19651970

1966-
let mut tokens = if is_union && self.can_be_rust_union(ctx) {
1971+
let mut tokens = if is_union && struct_layout.is_rust_union() {
19671972
quote! {
19681973
#( #attributes )*
19691974
pub union #canonical_ident

src/codegen/struct_layout.rs

+25-14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub struct StructLayoutTracker<'a> {
1919
comp: &'a CompInfo,
2020
is_packed: bool,
2121
known_type_layout: Option<Layout>,
22+
is_rust_union: bool,
2223
latest_offset: usize,
2324
padding_count: usize,
2425
latest_field_layout: Option<Layout>,
@@ -89,12 +90,15 @@ impl<'a> StructLayoutTracker<'a> {
8990
) -> Self {
9091
let known_type_layout = ty.layout(ctx);
9192
let is_packed = comp.is_packed(ctx, known_type_layout.as_ref());
93+
let is_rust_union = comp.is_union() &&
94+
comp.can_be_rust_union(ctx, known_type_layout.as_ref());
9295
StructLayoutTracker {
9396
name,
9497
ctx,
9598
comp,
9699
is_packed,
97100
known_type_layout,
101+
is_rust_union,
98102
latest_offset: 0,
99103
padding_count: 0,
100104
latest_field_layout: None,
@@ -103,6 +107,10 @@ impl<'a> StructLayoutTracker<'a> {
103107
}
104108
}
105109

110+
pub fn is_rust_union(&self) -> bool {
111+
self.is_rust_union
112+
}
113+
106114
pub fn saw_vtable(&mut self) {
107115
debug!("saw vtable for {}", self.name);
108116

@@ -143,18 +151,9 @@ impl<'a> StructLayoutTracker<'a> {
143151
// actually generate the dummy alignment.
144152
}
145153

146-
pub fn saw_union(&mut self, layout: Layout) {
147-
debug!("saw union for {}: {:?}", self.name, layout);
148-
self.align_to_latest_field(layout);
149-
150-
self.latest_offset += self.padding_bytes(layout) + layout.size;
151-
self.latest_field_layout = Some(layout);
152-
self.max_field_align = cmp::max(self.max_field_align, layout.align);
153-
}
154-
155-
/// Add a padding field if necessary for a given new field _before_ adding
156-
/// that field.
157-
pub fn pad_field(
154+
/// Returns a padding field if necessary for a given new field _before_
155+
/// adding that field.
156+
pub fn saw_field(
158157
&mut self,
159158
field_name: &str,
160159
field_ty: &Type,
@@ -181,15 +180,27 @@ impl<'a> StructLayoutTracker<'a> {
181180
}
182181
}
183182
}
183+
self.saw_field_with_layout(field_name, field_layout, field_offset)
184+
}
184185

186+
pub fn saw_field_with_layout(
187+
&mut self,
188+
field_name: &str,
189+
field_layout: Layout,
190+
field_offset: Option<usize>,
191+
) -> Option<proc_macro2::TokenStream> {
185192
let will_merge_with_bitfield = self.align_to_latest_field(field_layout);
186193

194+
let is_union = self.comp.is_union();
187195
let padding_bytes = match field_offset {
188196
Some(offset) if offset / 8 > self.latest_offset => {
189197
offset / 8 - self.latest_offset
190198
}
191199
_ => {
192-
if will_merge_with_bitfield || field_layout.align == 0 {
200+
if will_merge_with_bitfield ||
201+
field_layout.align == 0 ||
202+
is_union
203+
{
193204
0
194205
} else if !self.is_packed {
195206
self.padding_bytes(field_layout)
@@ -203,7 +214,7 @@ impl<'a> StructLayoutTracker<'a> {
203214

204215
self.latest_offset += padding_bytes;
205216

206-
let padding_layout = if self.is_packed {
217+
let padding_layout = if self.is_packed || is_union {
207218
None
208219
} else {
209220
// Otherwise the padding is useless.

src/ir/comp.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1642,7 +1642,12 @@ impl CompInfo {
16421642
/// Requirements:
16431643
/// 1. Current RustTarget allows for `untagged_union`
16441644
/// 2. Each field can derive `Copy`
1645-
pub fn can_be_rust_union(&self, ctx: &BindgenContext) -> bool {
1645+
/// 3. It's not zero-sized.
1646+
pub fn can_be_rust_union(
1647+
&self,
1648+
ctx: &BindgenContext,
1649+
layout: Option<&Layout>,
1650+
) -> bool {
16461651
if !ctx.options().rust_features().untagged_union {
16471652
return false;
16481653
}
@@ -1651,12 +1656,22 @@ impl CompInfo {
16511656
return false;
16521657
}
16531658

1654-
self.fields().iter().all(|f| match *f {
1659+
let all_can_copy = self.fields().iter().all(|f| match *f {
16551660
Field::DataMember(ref field_data) => {
16561661
field_data.ty().can_derive_copy(ctx)
16571662
}
16581663
Field::Bitfields(_) => true,
1659-
})
1664+
});
1665+
1666+
if !all_can_copy {
1667+
return false;
1668+
}
1669+
1670+
if layout.map_or(false, |l| l.size == 0) {
1671+
return false;
1672+
}
1673+
1674+
true
16601675
}
16611676
}
16621677

tests/expectations/tests/16-byte-alignment.rs

-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ pub struct rte_ipv4_tuple {
1717
pub union rte_ipv4_tuple__bindgen_ty_1 {
1818
pub __bindgen_anon_1: rte_ipv4_tuple__bindgen_ty_1__bindgen_ty_1,
1919
pub sctp_tag: u32,
20-
_bindgen_union_align: u32,
2120
}
2221
#[repr(C)]
2322
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
@@ -159,7 +158,6 @@ pub struct rte_ipv6_tuple {
159158
pub union rte_ipv6_tuple__bindgen_ty_1 {
160159
pub __bindgen_anon_1: rte_ipv6_tuple__bindgen_ty_1__bindgen_ty_1,
161160
pub sctp_tag: u32,
162-
_bindgen_union_align: u32,
163161
}
164162
#[repr(C)]
165163
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
@@ -295,7 +293,6 @@ impl Default for rte_ipv6_tuple {
295293
pub union rte_thash_tuple {
296294
pub v4: rte_ipv4_tuple,
297295
pub v6: rte_ipv6_tuple,
298-
_bindgen_union_align: [u128; 3usize],
299296
}
300297
#[test]
301298
fn bindgen_test_layout_rte_thash_tuple() {

tests/expectations/tests/anon-fields-prefix.rs

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ pub union color {
1111
pub u1: color__bindgen_ty_1,
1212
pub u2: color__bindgen_ty_2,
1313
pub v3: [::std::os::raw::c_uchar; 3usize],
14-
_bindgen_union_align: [u8; 3usize],
1514
}
1615
#[repr(C)]
1716
#[derive(Debug, Default, Copy, Clone)]

tests/expectations/tests/anon_struct_in_union.rs

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub struct s {
1414
#[derive(Copy, Clone)]
1515
pub union s__bindgen_ty_1 {
1616
pub field: s__bindgen_ty_1_inner,
17-
_bindgen_union_align: u32,
1817
}
1918
#[repr(C)]
2019
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]

tests/expectations/tests/anon_union.rs

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ pub struct TErrorResult_DOMExceptionInfo {
3535
pub union TErrorResult__bindgen_ty_1 {
3636
pub mMessage: *mut TErrorResult_Message,
3737
pub mDOMExceptionInfo: *mut TErrorResult_DOMExceptionInfo,
38-
_bindgen_union_align: u64,
3938
}
4039
impl Default for TErrorResult__bindgen_ty_1 {
4140
fn default() -> Self {

tests/expectations/tests/class.rs

-1
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ impl Default for IncompleteArrayNonCopiable {
340340
pub union Union {
341341
pub d: f32,
342342
pub i: ::std::os::raw::c_int,
343-
_bindgen_union_align: u32,
344343
}
345344
#[test]
346345
fn bindgen_test_layout_Union() {

0 commit comments

Comments
 (0)