Skip to content

Commit 827a915

Browse files
committed
codegen: Guarantee opaque type layout across all architectures.
Fixes #3279
1 parent fc747b7 commit 827a915

File tree

7 files changed

+69
-155
lines changed

7 files changed

+69
-155
lines changed

bindgen/codegen/helpers.rs

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -80,37 +80,16 @@ pub(crate) mod attributes {
8080
/// just noise.
8181
/// TODO: Should this be `MaybeUninit`, since padding bytes are effectively
8282
/// uninitialized?
83-
pub(crate) fn blob(
84-
ctx: &BindgenContext,
85-
layout: Layout,
86-
ffi_safe: bool,
87-
) -> syn::Type {
88-
let opaque = layout.opaque();
89-
90-
// FIXME(emilio, #412): We fall back to byte alignment, but there are
91-
// some things that legitimately are more than 8-byte aligned.
92-
//
93-
// Eventually we should be able to `unwrap` here, but...
94-
let ty = opaque.known_rust_type_for_array().unwrap_or_else(|| {
95-
warn!("Found unknown alignment on code generation!");
96-
syn::parse_quote! { u8 }
97-
});
98-
99-
let data_len = opaque.array_size().unwrap_or(layout.size);
100-
101-
if data_len == 1 {
102-
ty
103-
} else if ffi_safe {
104-
ctx.generated_opaque_array();
105-
if ctx.options().enable_cxx_namespaces {
106-
syn::parse_quote! { root::__BindgenOpaqueArray<#ty, #data_len> }
107-
} else {
108-
syn::parse_quote! { __BindgenOpaqueArray<#ty, #data_len> }
109-
}
83+
pub(crate) fn blob(ctx: &BindgenContext, layout: Layout) -> syn::Type {
84+
// TODO: Maybe parameterize this on the alignment.
85+
ctx.generated_opaque_array();
86+
let ident =
87+
format_ident!("__BindgenOpaqueArray{}", std::cmp::max(1, layout.align));
88+
let size = layout.size;
89+
if ctx.options().enable_cxx_namespaces {
90+
syn::parse_quote! { root::#ident<[u8; #size]> }
11091
} else {
111-
// This is not FFI safe as an argument; the struct above is
112-
// preferable.
113-
syn::parse_quote! { [ #ty ; #data_len ] }
92+
syn::parse_quote! { #ident<[u8; #size]> }
11493
}
11594
}
11695

bindgen/codegen/mod.rs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ impl CodeGenerator for Module {
581581
utils::prepend_complex_type(&mut *result);
582582
}
583583
if ctx.need_opaque_array_type() {
584-
utils::prepend_opaque_array_type(&mut *result);
584+
utils::prepend_opaque_array_types(&mut *result);
585585
}
586586
if result.saw_objc {
587587
utils::prepend_objc_header(ctx, &mut *result);
@@ -2259,14 +2259,13 @@ impl CodeGenerator for CompInfo {
22592259

22602260
if has_address {
22612261
let layout = Layout::new(1, 1);
2262-
let ty = helpers::blob(ctx, Layout::new(1, 1), false);
22632262
struct_layout.saw_field_with_layout(
22642263
"_address",
22652264
layout,
22662265
/* offset = */ Some(0),
22672266
);
22682267
fields.push(quote! {
2269-
pub _address: #ty,
2268+
pub _address: u8,
22702269
});
22712270
}
22722271
}
@@ -2275,8 +2274,7 @@ impl CodeGenerator for CompInfo {
22752274
match layout {
22762275
Some(l) => {
22772276
explicit_align = Some(l.align);
2278-
2279-
let ty = helpers::blob(ctx, l, false);
2277+
let ty = helpers::blob(ctx, l);
22802278
fields.push(quote! {
22812279
pub _bindgen_opaque_blob: #ty ,
22822280
});
@@ -2310,9 +2308,9 @@ impl CodeGenerator for CompInfo {
23102308
explicit_align = Some(layout.align);
23112309
}
23122310
if !struct_layout.is_rust_union() {
2313-
let ty = helpers::blob(ctx, layout, false);
2311+
let ty = helpers::blob(ctx, layout);
23142312
fields.push(quote! {
2315-
pub bindgen_union_field: #ty ,
2313+
pub bindgen_union_field: #ty,
23162314
});
23172315
}
23182316
}
@@ -4069,7 +4067,7 @@ pub(crate) trait TryToOpaque {
40694067
extra: &Self::Extra,
40704068
) -> error::Result<syn::Type> {
40714069
self.try_get_layout(ctx, extra)
4072-
.map(|layout| helpers::blob(ctx, layout, true))
4070+
.map(|layout| helpers::blob(ctx, layout))
40734071
}
40744072
}
40754073

@@ -4095,7 +4093,7 @@ pub(crate) trait ToOpaque: TryToOpaque {
40954093
extra: &Self::Extra,
40964094
) -> syn::Type {
40974095
let layout = self.get_layout(ctx, extra);
4098-
helpers::blob(ctx, layout, true)
4096+
helpers::blob(ctx, layout)
40994097
}
41004098
}
41014099

@@ -4146,7 +4144,7 @@ where
41464144
) -> error::Result<syn::Type> {
41474145
self.try_to_rust_ty(ctx, extra).or_else(|_| {
41484146
if let Ok(layout) = self.try_get_layout(ctx, extra) {
4149-
Ok(helpers::blob(ctx, layout, true))
4147+
Ok(helpers::blob(ctx, layout))
41504148
} else {
41514149
Err(Error::NoLayoutForOpaqueBlob)
41524150
}
@@ -5610,23 +5608,34 @@ pub(crate) mod utils {
56105608
result.extend(old_items);
56115609
}
56125610

5613-
pub(crate) fn prepend_opaque_array_type(
5611+
pub(crate) fn prepend_opaque_array_types(
56145612
result: &mut Vec<proc_macro2::TokenStream>,
56155613
) {
5616-
let ty = quote! {
5617-
/// If Bindgen could only determine the size and alignment of a
5618-
/// type, it is represented like this.
5619-
#[derive(PartialEq, Copy, Clone, Debug, Hash)]
5620-
#[repr(C)]
5621-
pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
5622-
impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
5623-
fn default() -> Self {
5624-
Self([<T as Default>::default(); N])
5614+
let mut tys = vec![];
5615+
// If Bindgen could only determine the size and alignment of a type, it is represented like
5616+
// this. We could use const generics or so but it wouldn't support all our rust targets. We
5617+
// only expect [u8; N] as the type parameter. FIXME(emilio): Probably generate only the
5618+
// ones we want.
5619+
for align in [1usize, 2, 4, 8, 16] {
5620+
let ident = format_ident!("__BindgenOpaqueArray{}", align);
5621+
let repr = if align == 1 {
5622+
quote! { #[repr(C)] }
5623+
} else {
5624+
let explicit = super::helpers::ast_ty::int_expr(align as i64);
5625+
quote! { #[repr(C, align(#explicit))] }
5626+
};
5627+
tys.push(quote! {
5628+
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
5629+
#repr
5630+
pub struct #ident<T>(pub T);
5631+
impl<const N: usize> Default for #ident<[u8; N]> {
5632+
fn default() -> Self {
5633+
Self([0u8; N])
5634+
}
56255635
}
5626-
}
5627-
};
5628-
5629-
result.insert(0, ty);
5636+
})
5637+
}
5638+
result.splice(0..0, tys);
56305639
}
56315640

56325641
pub(crate) fn build_path(

bindgen/codegen/struct_layout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ impl<'a> StructLayoutTracker<'a> {
367367
}
368368

369369
fn padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream {
370-
let ty = helpers::blob(self.ctx, layout, false);
370+
let ty = helpers::blob(self.ctx, layout);
371371
let padding_count = self.padding_count;
372372

373373
self.padding_count += 1;

bindgen/ir/analysis/derive.rs

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -179,26 +179,11 @@ impl CannotDerive<'_> {
179179
return CanDerive::No;
180180
}
181181

182-
let layout_can_derive =
183-
ty.layout(self.ctx).map_or(CanDerive::Yes, |l| {
184-
l.opaque().array_size_within_derive_limit()
185-
});
186-
187-
match layout_can_derive {
188-
CanDerive::Yes => {
189-
trace!(
190-
" we can trivially derive {} for the layout",
191-
self.derive_trait
192-
);
193-
}
194-
_ => {
195-
trace!(
196-
" we cannot derive {} for the layout",
197-
self.derive_trait
198-
);
199-
}
200-
}
201-
return layout_can_derive;
182+
trace!(
183+
" we can trivially derive {} for the layout",
184+
self.derive_trait
185+
);
186+
return CanDerive::Yes;
202187
}
203188

204189
match *ty.kind() {
@@ -338,25 +323,11 @@ impl CannotDerive<'_> {
338323
return CanDerive::No;
339324
}
340325

341-
let layout_can_derive =
342-
ty.layout(self.ctx).map_or(CanDerive::Yes, |l| {
343-
l.opaque().array_size_within_derive_limit()
344-
});
345-
match layout_can_derive {
346-
CanDerive::Yes => {
347-
trace!(
348-
" union layout can trivially derive {}",
349-
self.derive_trait
350-
);
351-
}
352-
_ => {
353-
trace!(
354-
" union layout cannot derive {}",
355-
self.derive_trait
356-
);
357-
}
358-
}
359-
return layout_can_derive;
326+
trace!(
327+
" union layout can trivially derive {}",
328+
self.derive_trait
329+
);
330+
return CanDerive::Yes;
360331
}
361332
}
362333

bindgen/ir/item.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use super::derive::{
1212
use super::dot::DotAttributes;
1313
use super::function::{Function, FunctionKind};
1414
use super::item_kind::ItemKind;
15-
use super::layout::Opaque;
1615
use super::module::Module;
1716
use super::template::{AsTemplateParam, TemplateParameters};
1817
use super::traversal::{EdgeKind, Trace, Tracer};
@@ -451,7 +450,7 @@ impl Item {
451450
ctx: &mut BindgenContext,
452451
) -> TypeId {
453452
let location = ty.declaration().location();
454-
let ty = Opaque::from_clang_ty(ty, ctx);
453+
let ty = Type::new_opaque_from_clang_ty(ty, ctx);
455454
let kind = ItemKind::Type(ty);
456455
let parent = ctx.root_module().into();
457456
ctx.add_item(

bindgen/ir/layout.rs

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
//! Intermediate representation for the physical layout of some type.
22
3-
use super::derive::CanDerive;
4-
use super::ty::{Type, TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
5-
use crate::clang;
63
use crate::ir::context::BindgenContext;
7-
use std::cmp;
84

95
/// A type that represents the struct layout of a type.
106
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -71,56 +67,4 @@ impl Layout {
7167
pub(crate) fn for_size(ctx: &BindgenContext, size: usize) -> Self {
7268
Self::for_size_internal(ctx.target_pointer_size(), size)
7369
}
74-
75-
/// Get this layout as an opaque type.
76-
pub(crate) fn opaque(&self) -> Opaque {
77-
Opaque(*self)
78-
}
79-
}
80-
81-
/// When we are treating a type as opaque, it is just a blob with a `Layout`.
82-
#[derive(Clone, Debug, PartialEq, Eq)]
83-
pub(crate) struct Opaque(pub(crate) Layout);
84-
85-
impl Opaque {
86-
/// Construct a new opaque type from the given clang type.
87-
pub(crate) fn from_clang_ty(
88-
ty: &clang::Type,
89-
ctx: &BindgenContext,
90-
) -> Type {
91-
let layout = Layout::new(ty.size(ctx), ty.align(ctx));
92-
let ty_kind = TypeKind::Opaque;
93-
let is_const = ty.is_const();
94-
Type::new(None, Some(layout), ty_kind, is_const)
95-
}
96-
97-
/// Return the known rust type we should use to create a correctly-aligned
98-
/// field with this layout.
99-
pub(crate) fn known_rust_type_for_array(&self) -> Option<syn::Type> {
100-
Layout::known_type_for_size(self.0.align)
101-
}
102-
103-
/// Return the array size that an opaque type for this layout should have if
104-
/// we know the correct type for it, or `None` otherwise.
105-
pub(crate) fn array_size(&self) -> Option<usize> {
106-
if self.known_rust_type_for_array().is_some() {
107-
Some(self.0.size / cmp::max(self.0.align, 1))
108-
} else {
109-
None
110-
}
111-
}
112-
113-
/// Return `true` if this opaque layout's array size will fit within the
114-
/// maximum number of array elements that Rust allows deriving traits
115-
/// with. Return `false` otherwise.
116-
pub(crate) fn array_size_within_derive_limit(&self) -> CanDerive {
117-
if self
118-
.array_size()
119-
.is_some_and(|size| size <= RUST_DERIVE_IN_ARRAY_LIMIT)
120-
{
121-
CanDerive::Yes
122-
} else {
123-
CanDerive::Manually
124-
}
125-
}
12670
}

bindgen/ir/ty.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use super::dot::DotAttributes;
66
use super::enum_ty::Enum;
77
use super::function::FunctionSig;
88
use super::item::{IsOpaque, Item};
9-
use super::layout::{Layout, Opaque};
9+
use super::layout::Layout;
1010
use super::objc::ObjCInterface;
1111
use super::template::{
1212
AsTemplateParam, TemplateInstantiation, TemplateParameters,
@@ -67,6 +67,17 @@ impl Type {
6767
}
6868
}
6969

70+
/// Construct an opaque item from a clang type.
71+
pub(crate) fn new_opaque_from_clang_ty(
72+
ty: &clang::Type,
73+
ctx: &BindgenContext,
74+
) -> Self {
75+
let layout = Layout::new(ty.size(ctx), ty.align(ctx));
76+
let ty_kind = TypeKind::Opaque;
77+
let is_const = ty.is_const();
78+
Type::new(None, Some(layout), ty_kind, is_const)
79+
}
80+
7081
/// Which kind of type is this?
7182
pub(crate) fn kind(&self) -> &TypeKind {
7283
&self.kind
@@ -737,7 +748,7 @@ impl Type {
737748
opaque type instead."
738749
);
739750
return Ok(ParseResult::New(
740-
Opaque::from_clang_ty(&canonical_ty, ctx),
751+
Self::new_opaque_from_clang_ty(&canonical_ty, ctx),
741752
None,
742753
));
743754
}
@@ -868,7 +879,8 @@ impl Type {
868879
from class template or base \
869880
specifier, using opaque blob"
870881
);
871-
let opaque = Opaque::from_clang_ty(ty, ctx);
882+
let opaque =
883+
Self::new_opaque_from_clang_ty(ty, ctx);
872884
return Ok(ParseResult::New(opaque, None));
873885
}
874886
}

0 commit comments

Comments
 (0)