Skip to content

Commit 70c75b3

Browse files
committed
Support nested flexible array members
Add support for structs where the last field is itself a struct containing a flexible array member (FAM). For example: struct Name { int count; char name[]; }; struct Field { int id; struct Name name; // Last field has a FAM }; The Name struct is generated with a generic FAM parameter: pub struct Name<FAM: ?Sized = [c_char; 0]> { pub count: c_int, pub name: FAM, } And Field propagates this parameter to its nested field: pub struct Field<FAM: ?Sized = [c_char; 0]> { pub id: c_int, pub name: Name<FAM>, } For packed structs, the nested FAM field is wrapped in ManuallyDrop since packed structs cannot directly contain DST fields: #[repr(C, packed)] pub struct FieldPacked<FAM: ?Sized = [c_char; 0]> { pub id: c_int, pub name: ::std::mem::ManuallyDrop<Name<FAM>>, } Implementation: - Enhanced flex_array_member() in ir/comp.rs to recursively detect when a struct's last field is a compound type with its own FAM - Updated field code generation to parameterize nested FAM field types with <FAM> and wrap in ManuallyDrop for packed structs - Added test cases for nested FAMs in both regular and packed structs Related to #2936
1 parent 4731d6c commit 70c75b3

File tree

4 files changed

+333
-4
lines changed

4 files changed

+333
-4
lines changed

bindgen-tests/tests/expectations/tests/nested_flexarray.rs

Lines changed: 271 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// bindgen-flags: --rust-target nightly --flexarray-dst --raw-line '#![cfg(feature = "nightly")]' --raw-line '#![feature(ptr_metadata, layout_for_ptr)]'
2+
3+
// Test for nested flexible array members
4+
struct Field {
5+
int count;
6+
int data[]; // FAM
7+
};
8+
9+
struct Name {
10+
int id;
11+
struct Field field; // Last field is a struct with FAM
12+
};
13+
14+
#pragma pack(1)
15+
struct NamePacked {
16+
int id;
17+
struct Field field; // Last field is a struct with FAM, in a packed struct
18+
};
19+
#pragma pack()

bindgen/codegen/mod.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,28 @@ impl FieldCodegen<'_> for FieldData {
15401540
syn::parse_quote! { __IncompleteArrayField<#inner> }
15411541
}
15421542
}
1543+
} else if let TypeKind::Comp(ref comp) = field_ty.kind() {
1544+
// Nested FAM: the field is a struct that itself has a FAM
1545+
// Only treat as FAM if it's the last field
1546+
if ctx.options().flexarray_dst &&
1547+
last_field &&
1548+
comp.flex_array_member(ctx).is_some()
1549+
{
1550+
let layout = parent_item.expect_type().layout(ctx);
1551+
let is_packed = parent.is_packed(ctx, layout.as_ref());
1552+
struct_layout.saw_flexible_array();
1553+
1554+
// For nested FAMs, we need to parameterize the field type with FAM
1555+
// For packed structs, wrap in ManuallyDrop
1556+
if is_packed {
1557+
let prefix = ctx.trait_prefix();
1558+
syn::parse_quote! { ::#prefix::mem::ManuallyDrop<#ty<FAM>> }
1559+
} else {
1560+
syn::parse_quote! { #ty<FAM> }
1561+
}
1562+
} else {
1563+
ty
1564+
}
15431565
} else {
15441566
ty
15451567
};

bindgen/ir/comp.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,10 @@ impl CompFields {
790790
}
791791

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

800804
match fields.last()? {
801805
Field::Bitfields(..) => None,
802-
Field::DataMember(FieldData { ty, .. }) => ctx
803-
.resolve_type(*ty)
804-
.is_incomplete_array(ctx)
805-
.map(|item| item.expect_type_id(ctx)),
806+
Field::DataMember(FieldData { ty, .. }) => {
807+
let resolved_ty = ctx.resolve_type(*ty);
808+
809+
// Check if it's an incomplete array first
810+
if let Some(item) = resolved_ty.is_incomplete_array(ctx) {
811+
return Some(item.expect_type_id(ctx));
812+
}
813+
814+
// Check if it's a compound type with a FAM (need to resolve through type refs)
815+
let canonical_ty = resolved_ty.canonical_type(ctx);
816+
if let super::ty::TypeKind::Comp(ref comp) = canonical_ty.kind()
817+
{
818+
return comp.flex_array_member(ctx);
819+
}
820+
821+
None
822+
}
806823
}
807824
}
808825
}

0 commit comments

Comments
 (0)