Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 271 additions & 0 deletions bindgen-tests/tests/expectations/tests/nested_flexarray.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions bindgen-tests/tests/headers/nested_flexarray.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// bindgen-flags: --rust-target nightly --flexarray-dst --raw-line '#![cfg(feature = "nightly")]' --raw-line '#![feature(ptr_metadata, layout_for_ptr)]'

// Test for nested flexible array members
struct Field {
int count;
int data[]; // FAM
};

struct Name {
int id;
struct Field field; // Last field is a struct with FAM
};

#pragma pack(1)
struct NamePacked {
int id;
struct Field field; // Last field is a struct with FAM, in a packed struct
};
#pragma pack()
22 changes: 22 additions & 0 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,28 @@ impl FieldCodegen<'_> for FieldData {
syn::parse_quote! { __IncompleteArrayField<#inner> }
}
}
} else if let TypeKind::Comp(ref comp) = field_ty.kind() {
// Nested FAM: the field is a struct that itself has a FAM
// Only treat as FAM if it's the last field
if ctx.options().flexarray_dst &&
last_field &&
comp.flex_array_member(ctx).is_some()
{
let layout = parent_item.expect_type().layout(ctx);
let is_packed = parent.is_packed(ctx, layout.as_ref());
struct_layout.saw_flexible_array();

// For nested FAMs, we need to parameterize the field type with FAM
// For packed structs, wrap in ManuallyDrop
if is_packed {
let prefix = ctx.trait_prefix();
syn::parse_quote! { ::#prefix::mem::ManuallyDrop<#ty<FAM>> }
} else {
syn::parse_quote! { #ty<FAM> }
}
} else {
ty
}
} else {
ty
};
Expand Down
25 changes: 21 additions & 4 deletions bindgen/ir/comp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,10 @@ impl CompFields {
}

/// Return the flex array member for the struct/class, if any.
///
/// This method recursively checks if the last field is either:
/// 1. An incomplete array (direct FAM)
/// 2. A struct/union that itself has a FAM (nested FAM)
fn flex_array_member(&self, ctx: &BindgenContext) -> Option<TypeId> {
let fields = match self {
CompFields::Before(_) => panic!("raw fields"),
Expand All @@ -799,10 +803,23 @@ impl CompFields {

match fields.last()? {
Field::Bitfields(..) => None,
Field::DataMember(FieldData { ty, .. }) => ctx
.resolve_type(*ty)
.is_incomplete_array(ctx)
.map(|item| item.expect_type_id(ctx)),
Field::DataMember(FieldData { ty, .. }) => {
let resolved_ty = ctx.resolve_type(*ty);

// Check if it's an incomplete array first
if let Some(item) = resolved_ty.is_incomplete_array(ctx) {
return Some(item.expect_type_id(ctx));
}

// Check if it's a compound type with a FAM (need to resolve through type refs)
let canonical_ty = resolved_ty.canonical_type(ctx);
if let super::ty::TypeKind::Comp(ref comp) = canonical_ty.kind()
{
return comp.flex_array_member(ctx);
}

None
}
}
}
}
Expand Down