Skip to content

Commit 8d6f69c

Browse files
jsgfemilio
authored andcommitted
Add --flexarray-dst option
This option uses Rust DST types to model C structures with flexible array members. For example, if you declare: ```c struct record { int len; float values[]; } ``` this means it's a C structure with a well-defined prefix, but a tail of `values` which depends on how much memory we've allocated for it. We can model this in Rust with: ```rust struct record { len: c_int, values: [f32], } ``` which means more or less the same thing - there's a type which has a known prefix, but its suffix is not directly known.
1 parent 2126d83 commit 8d6f69c

File tree

6 files changed

+213
-12
lines changed

6 files changed

+213
-12
lines changed

bindgen-cli/options.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,9 @@ struct BindgenCommand {
406406
/// Set path for temporary files generated by fallback for clang macro parsing.
407407
#[arg(long)]
408408
clang_macro_fallback_build_dir: Option<PathBuf>,
409+
/// Use DSTs to represent structures with flexible array members.
410+
#[arg(long)]
411+
flexarray_dst: bool,
409412
/// Derive custom traits on any kind of type. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros.
410413
#[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)]
411414
with_derive_custom: Vec<(Vec<String>, String)>,
@@ -562,6 +565,7 @@ where
562565
wrap_unsafe_ops,
563566
clang_macro_fallback,
564567
clang_macro_fallback_build_dir,
568+
flexarray_dst,
565569
with_derive_custom,
566570
with_derive_custom_struct,
567571
with_derive_custom_enum,
@@ -1039,6 +1043,10 @@ where
10391043
builder = builder.clang_macro_fallback_build_dir(path);
10401044
}
10411045

1046+
if flexarray_dst {
1047+
builder = builder.flexarray_dst(true);
1048+
}
1049+
10421050
#[derive(Debug)]
10431051
struct CustomDeriveCallback {
10441052
derives: Vec<String>,

bindgen/codegen/mod.rs

Lines changed: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,10 +1454,13 @@ impl<'a> FieldCodegen<'a> for FieldData {
14541454
wrap_union_field_if_needed(ctx, struct_layout, ty, result)
14551455
} else if let Some(item) = field_ty.is_incomplete_array(ctx) {
14561456
result.saw_incomplete_array();
1457+
struct_layout.saw_flexible_array();
14571458

14581459
let inner = item.to_rust_ty_or_opaque(ctx, &());
14591460

1460-
if ctx.options().enable_cxx_namespaces {
1461+
if ctx.options().flexarray_dst {
1462+
syn::parse_quote! { FAM }
1463+
} else if ctx.options().enable_cxx_namespaces {
14611464
syn::parse_quote! { root::__IncompleteArrayField<#inner> }
14621465
} else {
14631466
syn::parse_quote! { __IncompleteArrayField<#inner> }
@@ -2208,20 +2211,60 @@ impl CodeGenerator for CompInfo {
22082211
});
22092212
}
22102213

2211-
let generics = if !generic_param_names.is_empty() {
2212-
let generic_param_names = generic_param_names.clone();
2213-
quote! {
2214-
< #( #generic_param_names ),* >
2214+
let (flex_array_generic, flex_inner_ty) = if ctx.options().flexarray_dst
2215+
{
2216+
match self.flex_array_member(ctx) {
2217+
Some(ty) => {
2218+
let inner = ty.to_rust_ty_or_opaque(ctx, &());
2219+
(
2220+
Some(quote! { FAM: ?Sized = [ #inner; 0 ] }),
2221+
Some(quote! { #inner }),
2222+
)
2223+
}
2224+
None => (None, None),
22152225
}
22162226
} else {
2217-
quote! {}
2227+
(None, None)
22182228
};
22192229

2230+
// Generics, including the flexible array member.
2231+
//
2232+
// generics - generic parameters for the struct declaration
2233+
// impl_generics_labels - generic parameters for `impl<...>`
2234+
// impl_generics_params - generic parameters for `impl structname<...>`
2235+
//
2236+
// `impl` blocks are for non-FAM related impls like Default, etc
2237+
let (generics, impl_generics_labels, impl_generics_params) =
2238+
if !generic_param_names.is_empty() || flex_array_generic.is_some() {
2239+
let (flex_sized, flex_fam) = match flex_inner_ty.as_ref() {
2240+
None => (None, None),
2241+
Some(ty) => (
2242+
Some(quote! { [ #ty; 0 ] }),
2243+
Some(quote! { FAM: ?Sized = [ #ty; 0 ] }),
2244+
),
2245+
};
2246+
2247+
(
2248+
quote! {
2249+
< #( #generic_param_names , )* #flex_fam >
2250+
},
2251+
quote! {
2252+
< #( #generic_param_names , )* >
2253+
},
2254+
quote! {
2255+
< #( #generic_param_names , )* #flex_sized >
2256+
},
2257+
)
2258+
} else {
2259+
(quote! {}, quote! {}, quote! {})
2260+
};
2261+
22202262
let mut attributes = vec![];
22212263
let mut needs_clone_impl = false;
22222264
let mut needs_default_impl = false;
22232265
let mut needs_debug_impl = false;
22242266
let mut needs_partialeq_impl = false;
2267+
let needs_flexarray_impl = flex_array_generic.is_some();
22252268
if let Some(comment) = item.comment(ctx) {
22262269
attributes.push(attributes::doc(comment));
22272270
}
@@ -2525,17 +2568,112 @@ impl CodeGenerator for CompInfo {
25252568
// NB: We can't use to_rust_ty here since for opaque types this tries to
25262569
// use the specialization knowledge to generate a blob field.
25272570
let ty_for_impl = quote! {
2528-
#canonical_ident #generics
2571+
#canonical_ident #impl_generics_params
25292572
};
25302573

25312574
if needs_clone_impl {
25322575
result.push(quote! {
2533-
impl #generics Clone for #ty_for_impl {
2576+
impl #impl_generics_labels Clone for #ty_for_impl {
25342577
fn clone(&self) -> Self { *self }
25352578
}
25362579
});
25372580
}
25382581

2582+
if needs_flexarray_impl {
2583+
let prefix = ctx.trait_prefix();
2584+
2585+
let flex_array =
2586+
flex_inner_ty.as_ref().map(|ty| quote! { [ #ty ] });
2587+
2588+
let dst_ty_for_impl = quote! {
2589+
#canonical_ident < #( #generic_param_names , )* #flex_array >
2590+
2591+
};
2592+
let sized_ty_for_impl = quote! {
2593+
#canonical_ident < #( #generic_param_names , )* [ #flex_inner_ty; 0 ] >
2594+
};
2595+
2596+
let turbo_dst_ty = quote! {
2597+
#canonical_ident :: < #( #generic_param_names , )* [ #flex_inner_ty ] >
2598+
};
2599+
2600+
let layout = if ctx.options().rust_features().layout_for_ptr {
2601+
quote! {
2602+
pub fn layout(len: usize) -> ::#prefix::alloc::Layout {
2603+
// SAFETY: Null pointers are OK if we don't deref them
2604+
unsafe {
2605+
let p: *const Self = ::#prefix::ptr::from_raw_parts(::#prefix::ptr::null(), len);
2606+
::#prefix::alloc::Layout::for_value_raw(p)
2607+
}
2608+
}
2609+
}
2610+
} else {
2611+
quote!()
2612+
};
2613+
2614+
let (from_ptr_dst, from_ptr_sized) = if ctx
2615+
.options()
2616+
.rust_features()
2617+
.ptr_metadata
2618+
{
2619+
(
2620+
quote! {
2621+
/// Construct a DST for `#canonical_ident` from a thin
2622+
/// pointer.
2623+
///
2624+
/// SAFETY: the `len` must be <= the underlying storage.
2625+
/// Note: returned lifetime is unbounded.
2626+
pub unsafe fn from_ptr<'a>(ptr: *const #sized_ty_for_impl, len: usize) -> &'a Self {
2627+
let ptr: *const Self = ::#prefix::ptr::from_raw_parts(ptr as *const (), len);
2628+
&*ptr
2629+
}
2630+
2631+
/// Construct a mutable DST for `#canonical_ident` from
2632+
/// a thin pointer. This is `MaybeUninit` to allow for
2633+
/// initialization.
2634+
///
2635+
/// SAFETY: the `len` must be <= the underlying storage.
2636+
/// Note: returned lifetime is unbounded.
2637+
pub unsafe fn from_ptr_mut<'a>(ptr: *mut #sized_ty_for_impl, len: usize) -> ::#prefix::mem::MaybeUninit<&'a mut Self> {
2638+
let ptr: *mut Self = ::#prefix::ptr::from_raw_parts_mut(ptr as *mut (), len);
2639+
::#prefix::mem::MaybeUninit::new(&mut *ptr)
2640+
}
2641+
},
2642+
quote! {
2643+
/// Turn a sized reference for `#canonical_ident` into
2644+
/// DST with the given `len`.
2645+
///
2646+
/// SAFETY: the `len` must be <= the underlying storage.
2647+
pub unsafe fn from_ref(&self, len: usize) -> & #dst_ty_for_impl {
2648+
// SAFETY: caller guarantees `len` is good
2649+
unsafe { #turbo_dst_ty :: from_ptr(self, len) }
2650+
}
2651+
2652+
/// Turn a mutable sized reference for
2653+
/// `#canonical_ident` into DST with the given `len`.
2654+
///
2655+
/// SAFETY: the `len` must be <= the underlying storage.
2656+
pub unsafe fn from_ref_mut(&mut self, len: usize) -> &mut #dst_ty_for_impl {
2657+
unsafe { #turbo_dst_ty :: from_ptr_mut(self, len).assume_init() }
2658+
}
2659+
},
2660+
)
2661+
} else {
2662+
(quote!(), quote!())
2663+
};
2664+
2665+
result.push(quote! {
2666+
impl #impl_generics_labels #dst_ty_for_impl {
2667+
#layout
2668+
#from_ptr_dst
2669+
}
2670+
2671+
impl #impl_generics_labels #sized_ty_for_impl {
2672+
#from_ptr_sized
2673+
}
2674+
});
2675+
}
2676+
25392677
if needs_default_impl {
25402678
let prefix = ctx.trait_prefix();
25412679
let body = if ctx.options().rust_features().maybe_uninit {
@@ -2560,7 +2698,7 @@ impl CodeGenerator for CompInfo {
25602698
// non-zero padding bytes, especially when forwards/backwards compatibility is
25612699
// involved.
25622700
result.push(quote! {
2563-
impl #generics Default for #ty_for_impl {
2701+
impl #impl_generics_labels Default for #ty_for_impl {
25642702
fn default() -> Self {
25652703
#body
25662704
}
@@ -2579,7 +2717,7 @@ impl CodeGenerator for CompInfo {
25792717
let prefix = ctx.trait_prefix();
25802718

25812719
result.push(quote! {
2582-
impl #generics ::#prefix::fmt::Debug for #ty_for_impl {
2720+
impl #impl_generics_labels ::#prefix::fmt::Debug for #ty_for_impl {
25832721
#impl_
25842722
}
25852723
});
@@ -2603,7 +2741,7 @@ impl CodeGenerator for CompInfo {
26032741

26042742
let prefix = ctx.trait_prefix();
26052743
result.push(quote! {
2606-
impl #generics ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds {
2744+
impl #impl_generics_labels ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds {
26072745
#impl_
26082746
}
26092747
});
@@ -2612,7 +2750,7 @@ impl CodeGenerator for CompInfo {
26122750

26132751
if !methods.is_empty() {
26142752
result.push(quote! {
2615-
impl #generics #ty_for_impl {
2753+
impl #impl_generics_labels #ty_for_impl {
26162754
#( #methods )*
26172755
}
26182756
});
@@ -5130,6 +5268,9 @@ pub(crate) mod utils {
51305268
ctx: &BindgenContext,
51315269
result: &mut Vec<proc_macro2::TokenStream>,
51325270
) {
5271+
if ctx.options().flexarray_dst {
5272+
return;
5273+
}
51335274
let prefix = ctx.trait_prefix();
51345275

51355276
// If the target supports `const fn`, declare eligible functions

bindgen/codegen/struct_layout.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub(crate) struct StructLayoutTracker<'a> {
2828
max_field_align: usize,
2929
last_field_was_bitfield: bool,
3030
visibility: FieldVisibilityKind,
31+
last_field_was_flexible_array: bool,
3132
}
3233

3334
/// Returns a size aligned to a given value.
@@ -110,6 +111,7 @@ impl<'a> StructLayoutTracker<'a> {
110111
latest_field_layout: None,
111112
max_field_align: 0,
112113
last_field_was_bitfield: false,
114+
last_field_was_flexible_array: false,
113115
}
114116
}
115117

@@ -121,6 +123,10 @@ impl<'a> StructLayoutTracker<'a> {
121123
self.is_rust_union
122124
}
123125

126+
pub(crate) fn saw_flexible_array(&mut self) {
127+
self.last_field_was_flexible_array = true;
128+
}
129+
124130
pub(crate) fn saw_vtable(&mut self) {
125131
debug!("saw vtable for {}", self.name);
126132

@@ -295,6 +301,11 @@ impl<'a> StructLayoutTracker<'a> {
295301
return None;
296302
}
297303

304+
// Also doesn't make sense for structs with flexible array members
305+
if self.last_field_was_flexible_array {
306+
return None;
307+
}
308+
298309
if self.latest_offset == comp_layout.size {
299310
// This struct does not contain tail padding.
300311
return None;

bindgen/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ macro_rules! define_rust_targets {
9696
define_rust_targets! {
9797
Nightly => {
9898
vectorcall_abi,
99+
ptr_metadata: #81513,
100+
layout_for_ptr: #69835,
99101
},
100102
Stable_1_77(77) => { offset_of: #106655 },
101103
Stable_1_73(73) => { thiscall_abi: #42202 },

bindgen/ir/comp.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,24 @@ impl CompFields {
825825
}
826826
}
827827
}
828+
829+
/// Return the flex array member for the struct/class, if any.
830+
fn flex_array_member(&self, ctx: &BindgenContext) -> Option<TypeId> {
831+
let fields = match self {
832+
CompFields::Before(_) => panic!("raw fields"),
833+
CompFields::After { fields, .. } => fields,
834+
CompFields::Error => return None, // panic?
835+
};
836+
837+
// XXX correct with padding on end?
838+
match fields.last() {
839+
None | Some(Field::Bitfields(..)) => None,
840+
Some(Field::DataMember(FieldData { ty, .. })) => ctx
841+
.resolve_type(*ty)
842+
.is_incomplete_array(ctx)
843+
.map(|item| item.expect_type_id(ctx)),
844+
}
845+
}
828846
}
829847

830848
impl Trace for CompFields {
@@ -1122,6 +1140,14 @@ impl CompInfo {
11221140
}
11231141
}
11241142

1143+
/// Return the flex array member and its element type if any
1144+
pub(crate) fn flex_array_member(
1145+
&self,
1146+
ctx: &BindgenContext,
1147+
) -> Option<TypeId> {
1148+
self.fields.flex_array_member(ctx)
1149+
}
1150+
11251151
fn has_fields(&self) -> bool {
11261152
match self.fields {
11271153
CompFields::Error => false,

bindgen/options/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,6 +1981,19 @@ options! {
19811981
},
19821982
as_args: "--wrap-unsafe-ops",
19831983
},
1984+
/// Use DSTs to represent structures with flexible array members.
1985+
flexarray_dst: bool {
1986+
methods: {
1987+
/// Use DSTs to represent structures with flexible array members.
1988+
///
1989+
/// This option is disabled by default.
1990+
pub fn flexarray_dst(mut self, doit: bool) -> Self {
1991+
self.options.flexarray_dst = doit;
1992+
self
1993+
}
1994+
},
1995+
as_args: "--flexarray-dst",
1996+
},
19841997
/// Patterns for functions whose ABI should be overridden.
19851998
abi_overrides: HashMap<Abi, RegexSet> {
19861999
methods: {

0 commit comments

Comments
 (0)