Skip to main content

prebindgen_c/
trait_impl.rs

1use prebindgen_registry::{Building, Conversions, Crossing, RegistryBuilder};
2
3use super::{builder::callback_fn_type, *};
4
5/// Per-category **input** terminal converter builders. Each returns
6/// `Some(ConverterImpl)` only for the type category it claims (and `None`
7/// otherwise); [`Prebindgen::on_input_type`] chains them in priority order
8/// before the wrapper shapes. The categories are mutually exclusive, so the
9/// chain's fall-through is equivalent to a sequential `if … return` block.
10impl CbindgenBuilder {
11    /// Opaque handle, by-value consume: `*Box::from_raw(v)` — fallible (null
12    /// handle → message). The wire is the bare handle pointer `*mut #c_struct`.
13    pub(crate) fn in_opaque_handle(&self, ty: &TypeRef) -> Option<ConverterImpl<()>> {
14        let key = ty.key();
15        if !self.opaque.contains_key(&key) {
16            return None;
17        }
18        let name = Self::in_name_of(&ty.key());
19        let c_struct = self.c_type_ident(&ty.key());
20        let src = self.src_ty_of(&ty.key());
21        let short = type_short(&ty.key());
22        let null_msg = format!("null {short} handle passed by value");
23        let function: syn::ItemFn = syn::parse_quote!(
24            #[allow(non_snake_case, unused_variables, dead_code)]
25            pub(crate) unsafe fn #name(
26                v: *mut #c_struct,
27            ) -> ::core::result::Result<#src, ::std::string::String> {
28                if v.is_null() {
29                    return ::core::result::Result::Err(
30                        ::std::string::String::from(#null_msg),
31                    );
32                }
33                ::core::result::Result::Ok(*::std::boxed::Box::from_raw(v as *mut #src))
34            }
35        );
36        Some(ConverterImpl {
37            subs: vec![],
38            destination: syn::parse_quote!(*mut #c_struct),
39            function,
40            pre_stages: vec![],
41            niches: Niches::empty(),
42            metadata: (),
43        })
44    }
45
46    /// Data struct: decode each field from its C wire — infallible.
47    pub(crate) fn in_data_struct(
48        &self,
49        ty: &TypeRef,
50        r: &impl Conversions<()>,
51    ) -> Option<ConverterImpl<()>> {
52        let key = ty.key();
53        if !self.data.contains_key(&key) {
54            return None;
55        }
56        let fields = self.struct_fields(r, &ty.key())?;
57        let name = Self::in_name_of(&ty.key());
58        let c_struct = self.c_type_ident(&ty.key());
59        let src = self.src_ty_of(&ty.key());
60        let mut inits: Vec<TokenStream> = Vec::new();
61        let mut subs: Vec<TypeKey> = Vec::new();
62        let mut fallible = false;
63        for (fname, fty) in &fields {
64            if r_is_string(fty) {
65                inits.push(quote!(#fname: if v.#fname.is_null() {
66                    ::std::string::String::new()
67                } else {
68                    ::std::ffi::CStr::from_ptr(v.#fname).to_string_lossy().into_owned()
69                }));
70            } else if self.tagged_unions.contains_key(&fty.key()) {
71                // A sum field crosses by value as its mirror; its own converter
72                // validates the tag and rebuilds the live arm, which is what
73                // makes this whole decode fallible.
74                let conv = Self::in_name_of(&fty.key());
75                subs.push(fty.key());
76                fallible = true;
77                inits.push(quote!(#fname: #conv(v.#fname)?));
78            } else if r_is_bool(fty) {
79                // #170 instance 2: the field's wire is `MaybeUninit<bool>`, so
80                // the byte C wrote is normalised here — a Rust `bool` never
81                // holds it unchecked.
82                let read = bool_in_expr(quote!(v.#fname));
83                inits.push(quote!(#fname: #read));
84            } else {
85                inits.push(quote!(#fname: v.#fname));
86            }
87        }
88        // Only a union field can fail; a struct of strings and scalars keeps
89        // its infallible signature (and its callers keep theirs).
90        let function: syn::ItemFn = if fallible {
91            syn::parse_quote!(
92                #[allow(non_snake_case, unused_variables, dead_code)]
93                pub(crate) unsafe fn #name(
94                    v: #c_struct,
95                ) -> ::core::result::Result<#src, ::std::string::String> {
96                    ::core::result::Result::Ok(#src { #(#inits),* })
97                }
98            )
99        } else {
100            syn::parse_quote!(
101                #[allow(non_snake_case, unused_variables, dead_code)]
102                pub(crate) unsafe fn #name(v: #c_struct) -> #src {
103                    #src { #(#inits),* }
104                }
105            )
106        };
107        Some(ConverterImpl {
108            subs,
109            destination: syn::parse_quote!(#c_struct),
110            function,
111            pre_stages: vec![],
112            niches: Niches::empty(),
113            metadata: (),
114        })
115    }
116
117    /// The mirror field idents to null in a by-value consume's gravestone write-back,
118    /// for a `repr_c_struct` (`generate_mirror`) whose owned-pointer fields are all
119    /// **nullable** (`Option<Box<T>>`). `Some(idents)` (possibly empty — a pure
120    /// scalar/enum mirror needs no write-back) enables the cheap field-nulling path;
121    /// `None` (not a generate_mirror, or a bare `Box<T>` field whose NULL would be an
122    /// invalid `Box`) forces the full `gravestone()` write.
123    fn nullable_owned_ptr_fields(
124        &self,
125        registry: &impl Conversions<()>,
126        key: &TypeKey,
127    ) -> Option<Vec<syn::Ident>> {
128        let cfg = self.value_opaque.get(key)?;
129        if !cfg.generate_mirror {
130            return None;
131        }
132        let mut idents = Vec::new();
133        for (fname, fty) in self.struct_fields(registry, key)? {
134            // An owned-pointer field is one whose mirror wire is a raw pointer
135            // (`Option<Box<T>>` / `Box<T>` → `*mut t_t`); scalars/enums are not.
136            if matches!(self.mirror_field_wire(fty), Some(syn::Type::Ptr(_))) {
137                // Bare `Box<T>`: cannot be nulled (an invalid `Box`).
138                fty.optional_inner()?;
139                idents.push(fname);
140            }
141        }
142        Some(idents)
143    }
144
145    /// The gravestone write-back statements for a by-value **consume** / `_take` of a
146    /// value-opaque type, writing into the slot pointed to by `slot` (a `*mut #opaque`).
147    /// `None` ⇒ no write-back needed (plain data — the moved-from bitwise copy drops
148    /// harmlessly). Owned-ness is **inferred** for a `repr_c_struct` mirror (the
149    /// generator knows the fields): nullable owned-pointer fields are nulled in place
150    /// (cheap, no `Default`); a bare `Box<T>` field falls back to the full `gravestone()`
151    /// write. A non-mirror (`opaque_data_struct`/`opaque_owned_struct`) uses its explicit
152    /// declared `kind` (its fields are an opaque blob the generator can't introspect).
153    fn value_opaque_writeback(
154        &self,
155        registry: &impl Conversions<()>,
156        key: &TypeKey,
157        slot: &syn::Ident,
158    ) -> Option<TokenStream> {
159        let cfg = self.value_opaque.get(key)?;
160        let opaque = &cfg.opaque;
161        if cfg.generate_mirror {
162            match self.nullable_owned_ptr_fields(registry, key) {
163                // No owned-pointer fields ⇒ plain data, nothing to clean up.
164                Some(fields) if fields.is_empty() => None,
165                // All owned-pointer fields nullable ⇒ null them in place (drop-safe).
166                Some(fields) => Some(quote!(#( (*#slot).#fields = ::core::ptr::null_mut(); )*)),
167                // Bare `Box<T>` field ⇒ a NULL would be an invalid `Box`; full gravestone.
168                None => Some(
169                    quote!(::core::ptr::write(#slot, <#opaque as ::prebindgen_c_runtime::Gravestone>::gravestone());),
170                ),
171            }
172        } else {
173            // Non-mirror opaque: the consumer chose the kind explicitly.
174            match cfg.kind {
175                OpaqueKind::Owned => Some(
176                    quote!(::core::ptr::write(#slot, <#opaque as ::prebindgen_c_runtime::Gravestone>::gravestone());),
177                ),
178                OpaqueKind::Data => None,
179            }
180        }
181    }
182
183    /// Whether the auto-generated `Gravestone` impl is needed for a `repr_c_struct`
184    /// mirror: only when its consume/`_take` write-back uses `gravestone()` — i.e. it
185    /// has a bare `Box<T>` owned-pointer field (a null `Box` is invalid). Nullable
186    /// (`Option<Box<T>>`) mirrors null in place and need no `Gravestone`/`Default`;
187    /// non-mirror owned types get their `Gravestone` impl from the consumer.
188    fn mirror_needs_gravestone_impl(&self, registry: &Registry<()>, key: &TypeKey) -> bool {
189        match self.value_opaque.get(key) {
190            Some(cfg) if cfg.generate_mirror => {
191                self.nullable_owned_ptr_fields(registry, key).is_none()
192            }
193            _ => false,
194        }
195    }
196
197    /// Inline-opaque, by-`*mut` consume: read the live Rust value out by
198    /// transmute (move). For an `opaque_owned_struct` type, write a gravestone back so a
199    /// later `_drop` is a no-op (safe drop-after-move); an `opaque_data_struct` type
200    /// owns no external resource, so the moved-from bitwise duplicate is
201    /// harmlessly droppable and no write-back is needed. Only the C pointer is
202    /// null-checked — NULL ⇒ Err, and the `Option<_>` wrapper maps a NULL pointer
203    /// wire → None. (We do NOT reject gravestone values: for types whose
204    /// gravestone coincides with a legitimate value — e.g. an *empty* `ZBytes` —
205    /// that would wrongly reject valid inputs; the move + write-back is safe.)
206    ///
207    /// **Write-back optimization for a `repr_c_struct` mirror:** the generator knows
208    /// the mirror's fields, so when all its owned-pointer fields are nullable
209    /// (`Option<Box<T>>`) it nulls just those fields (`(*v).label = null`) instead of
210    /// rebuilding+writing the whole `Default` gravestone — drop-safe (scalars are
211    /// `Copy`; nulling the owned pointers prevents the double-free) and far cheaper.
212    /// Non-mirror types (`opaque_owned_struct` blobs) and mirrors with a bare `Box<T>`
213    /// field (NULL would be an invalid `Box`) keep the full `gravestone()` write.
214    pub(crate) fn in_value_opaque(
215        &self,
216        ty: &TypeRef,
217        registry: &impl Conversions<()>,
218    ) -> Option<ConverterImpl<()>> {
219        let opaque = self.value_opaque_ty_of(&ty.key())?.clone();
220        let name = Self::in_name_of(&ty.key());
221        let src = self.src_ty_of(&ty.key());
222        let short = type_short(&ty.key());
223        let null_msg = format!("null {short} value passed by value");
224        // Owned-ness (whether to clean up the moved-from slot) is inferred from the
225        // mirror's fields for a `repr_c_struct`, or the explicit kind for a non-mirror.
226        let writeback = self.value_opaque_writeback(registry, &ty.key(), &format_ident!("v"));
227        let function: syn::ItemFn = syn::parse_quote!(
228            #[allow(non_snake_case, unused_variables, dead_code)]
229            pub(crate) unsafe fn #name(
230                v: *mut #opaque,
231            ) -> ::core::result::Result<#src, ::std::string::String> {
232                if v.is_null() {
233                    return ::core::result::Result::Err(
234                        ::std::string::String::from(#null_msg),
235                    );
236                }
237                let __live = <#opaque as ::prebindgen_c_runtime::Transmute>::into_rust(
238                    ::core::ptr::read(v),
239                );
240                #writeback
241                ::core::result::Result::Ok(__live)
242            }
243        );
244        Some(ConverterImpl {
245            subs: vec![],
246            destination: syn::parse_quote!(*mut #opaque),
247            function,
248            pre_stages: vec![],
249            niches: Niches::empty(),
250            metadata: (),
251        })
252    }
253
254    /// Enum input: read the C-supplied discriminant as a plain integer,
255    /// **validate** it, then build the source enum — fallible.
256    ///
257    /// A C `enum` is an `int` at the ABI, so nothing stops a caller passing a
258    /// value no variant has. Taking the mirror `#[repr(C)]` enum by value would
259    /// **materialise** that invalid discriminant at the boundary — undefined
260    /// behaviour *before* any `match` in this converter could inspect it, which
261    /// is why validating an already-materialised enum is not a fix (#158).
262    ///
263    /// So the wire is `::core::mem::MaybeUninit<mirror>`, which is
264    /// `#[repr(transparent)]` over the mirror (identical ABI, identical C
265    /// spelling — cbindgen renders `MaybeUninit<T>` as `T`) and, unlike the
266    /// mirror itself, may legally hold **any** bit pattern. The discriminant is
267    /// then read out as `c_int` — the representation a `#[repr(C)]` fieldless
268    /// enum has by definition, asserted below — and compared against the
269    /// mirror's own variants, so a `const`- or `cfg`-driven discriminant needs
270    /// no generator-side evaluation. An unmatched value is a binding error
271    /// through the wrapper's error channel; no Rust enum is ever constructed
272    /// from it.
273    pub(crate) fn in_enum(
274        &self,
275        ty: &TypeRef,
276        r: &impl Conversions<()>,
277    ) -> Option<ConverterImpl<()>> {
278        let key = ty.key();
279        if !self.enums.contains_key(&key) {
280            return None;
281        }
282        let e = unit_enum(r, &ty.key())?;
283        let name = Self::in_name_of(&ty.key());
284        let cname = self.c_type_ident(&ty.key());
285        let src = self.src_ty_of(&ty.key());
286        let cname_str = cname.to_string();
287        let arms = e.values.iter().map(|v| {
288            let id = &v.name;
289            quote!(
290                if __raw == #cname::#id as ::core::ffi::c_int {
291                    return ::core::result::Result::Ok(#src::#id);
292                }
293            )
294        });
295        let bad_msg = format!("invalid discriminant {{}} for `{cname_str}`");
296        let size_msg = format!("`{cname_str}`: a #[repr(C)] enum must have the size of a C `int`");
297        let align_msg =
298            format!("`{cname_str}`: a #[repr(C)] enum must have the alignment of a C `int`");
299        let function: syn::ItemFn = syn::parse_quote!(
300            #[allow(non_snake_case, unused_variables, dead_code)]
301            pub(crate) unsafe fn #name(
302                v: ::core::mem::MaybeUninit<#cname>,
303            ) -> ::core::result::Result<#src, ::std::string::String> {
304                const _: () = {
305                    assert!(
306                        ::core::mem::size_of::<#cname>()
307                            == ::core::mem::size_of::<::core::ffi::c_int>(),
308                        #size_msg
309                    );
310                    assert!(
311                        ::core::mem::align_of::<#cname>()
312                            == ::core::mem::align_of::<::core::ffi::c_int>(),
313                        #align_msg
314                    );
315                };
316                let __raw: ::core::ffi::c_int =
317                    ::core::ptr::read(v.as_ptr() as *const ::core::ffi::c_int);
318                #(#arms)*
319                ::core::result::Result::Err(::std::format!(#bad_msg, __raw))
320            }
321        );
322        Some(ConverterImpl {
323            subs: vec![],
324            destination: syn::parse_quote!(::core::mem::MaybeUninit<#cname>),
325            function,
326            pre_stages: vec![],
327            niches: Niches::empty(),
328            metadata: (),
329        })
330    }
331
332    /// `String` input: `*const c_char` → owned `String` — fallible.
333    pub(crate) fn in_string(&self, ty: &TypeRef) -> Option<ConverterImpl<()>> {
334        if !r_is_string(ty) {
335            return None;
336        }
337        let name = Self::in_name_of(&ty.key());
338        let function: syn::ItemFn = syn::parse_quote!(
339            #[allow(non_snake_case, unused_variables, dead_code)]
340            pub(crate) unsafe fn #name(
341                v: *const ::core::ffi::c_char,
342            ) -> ::core::result::Result<::std::string::String, ::std::string::String> {
343                if v.is_null() {
344                    return ::core::result::Result::Err(
345                        ::std::string::String::from("null pointer passed for String argument"),
346                    );
347                }
348                match ::std::ffi::CStr::from_ptr(v).to_str() {
349                    ::core::result::Result::Ok(s) => {
350                        ::core::result::Result::Ok(s.to_owned())
351                    }
352                    ::core::result::Result::Err(_) => {
353                        ::core::result::Result::Err(
354                            ::std::string::String::from("invalid UTF-8 in String argument"),
355                        )
356                    }
357                }
358            }
359        );
360        Some(ConverterImpl {
361            subs: vec![],
362            destination: syn::parse_quote!(*const ::core::ffi::c_char),
363            function,
364            pre_stages: vec![],
365            niches: Niches::empty(),
366            metadata: (),
367        })
368    }
369
370    /// Bare `str` never crosses the C ABI directly, but resolving `&str`
371    /// inputs requires its inner node to have a filled rank-0 cell.
372    pub(crate) fn in_str(&self, ty: &TypeRef) -> Option<ConverterImpl<()>> {
373        if !r_is_str(ty) {
374            return None;
375        }
376        let name = Self::in_name_of(&ty.key());
377        let function: syn::ItemFn = syn::parse_quote!(
378            #[allow(non_snake_case, dead_code, unused_variables)]
379            pub(crate) fn #name() {}
380        );
381        Some(ConverterImpl {
382            subs: vec![],
383            destination: syn::parse_quote!(*const ::core::ffi::c_char),
384            function,
385            pre_stages: vec![],
386            niches: Niches::empty(),
387            metadata: (),
388        })
389    }
390
391    /// `bool` input: the one scalar that is **not** a pass-through (#170).
392    ///
393    /// A `bool` parameter is the broadest place C hands over a byte that no
394    /// Rust `bool` may hold, so it crosses as [`bool_wire`] and is normalised
395    /// by [`bool_in_expr`] before a `bool` exists. The C prototype is
396    /// unchanged — cbindgen simplifies `MaybeUninit<T>` to `T`.
397    pub(crate) fn in_bool(&self, ty: &TypeRef) -> Option<ConverterImpl<()>> {
398        if !r_is_bool(ty) {
399            return None;
400        }
401        let name = Self::in_name_of(&ty.key());
402        let wire = bool_wire();
403        let read = bool_in_expr(quote!(v));
404        let function: syn::ItemFn = syn::parse_quote!(
405            #[allow(non_snake_case, unused_variables, dead_code)]
406            pub(crate) unsafe fn #name(v: #wire) -> bool {
407                #read
408            }
409        );
410        Some(ConverterImpl {
411            subs: vec![],
412            destination: wire,
413            function,
414            pre_stages: vec![],
415            niches: Niches::empty(),
416            metadata: (),
417        })
418    }
419
420    /// FFI-safe scalar (integers, floats): identity pass-through. `bool` is
421    /// claimed earlier by [`Self::in_bool`] and never reaches here.
422    pub(crate) fn in_scalar(&self, ty: &TypeRef) -> Option<ConverterImpl<()>> {
423        if !r_is_scalar(ty) || r_is_bool(ty) {
424            return None;
425        }
426        let name = Self::in_name_of(&ty.key());
427        // A scalar's spelling is its name, so this needs no captured syntax.
428        let spelled = scalar_ty(ty)?;
429        let function: syn::ItemFn = syn::parse_quote!(
430            #[allow(non_snake_case, unused_variables, dead_code)]
431            pub(crate) fn #name(v: #spelled) -> #spelled {
432                v
433            }
434        );
435        Some(ConverterImpl {
436            subs: vec![],
437            destination: spelled.clone(),
438            function,
439            pre_stages: vec![],
440            niches: Niches::empty(),
441            metadata: (),
442        })
443    }
444}
445
446/// Per-section [`CbindgenBuilder::prerequisites`] emitters. Each returns the runtime-
447/// support items for one concern; the trait method concatenates them in order,
448/// so the emitted preamble is identical to the former single function.
449impl CbindgenBuilder {
450    /// C allocator extern + raw C-string allocator + the universal memory freer.
451    /// Emitted when the layer hands `char*`/array memory to C. Panics if such
452    /// memory is produced but no `.free_memory_function` is declared.
453    fn prereq_alloc_free(&self, registry: &Registry<()>, produces_array: bool) -> Vec<syn::Item> {
454        let mut items: Vec<syn::Item> = Vec::new();
455        if !(self.needs_free(registry) || produces_array) {
456            return items;
457        }
458        let free_ident = match &self.free_fn {
459            Some(name) => format_ident!("{}", name),
460            None => panic!(
461                "Cbindgen: the generated layer hands `char*` string memory to C \
462                 (a `String` return or a `String` data-struct field) but no \
463                 memory-freeing function is declared — add \
464                 `.free_memory_function(\"z_free\")`"
465            ),
466        };
467        // C allocator (linked from the C runtime; no crate dependency).
468        items.push(syn::parse_quote!(
469            extern "C" {
470                fn malloc(size: usize) -> *mut ::core::ffi::c_void;
471                fn free(ptr: *mut ::core::ffi::c_void);
472            }
473        ));
474        // Raw, destructor-free C-string block. `CString::new` drops interior
475        // NULs so the terminator marks the true end for C consumers.
476        items.push(syn::parse_quote!(
477            #[allow(non_snake_case, dead_code)]
478            pub(crate) fn __cbg_alloc_cstr(s: ::std::string::String) -> *mut ::core::ffi::c_char {
479                let c = ::std::ffi::CString::new(s).unwrap_or_default();
480                let bytes = c.as_bytes_with_nul();
481                unsafe {
482                    let p = malloc(bytes.len()) as *mut u8;
483                    if p.is_null() {
484                        return ::core::ptr::null_mut();
485                    }
486                    ::core::ptr::copy_nonoverlapping(bytes.as_ptr(), p, bytes.len());
487                    p as *mut ::core::ffi::c_char
488                }
489            }
490        ));
491        // Universal raw memory freer: type-agnostic C `free`, no length, no
492        // destructor (NULL-safe via C `free`).
493        items.push(syn::parse_quote!(
494            #[no_mangle]
495            #[allow(non_snake_case, unused_variables)]
496            pub unsafe extern "C" fn #free_ident(p: *mut ::core::ffi::c_void) {
497                free(p);
498            }
499        ));
500        items
501    }
502
503    /// Array builder: copy a `Vec<W>` into a C-`malloc`'d block of `W` and
504    /// return `(ptr, len)` (empty ⇒ `(NULL, 0)`). The block is freed C-side
505    /// via the `z_free_array` macro (per-element drop + the universal freer).
506    fn prereq_array_builder(&self, produces_array: bool) -> Vec<syn::Item> {
507        let mut items: Vec<syn::Item> = Vec::new();
508        if !produces_array {
509            return items;
510        }
511        items.push(syn::parse_quote!(
512            #[allow(non_snake_case, dead_code)]
513            pub(crate) unsafe fn __cbg_alloc_array<W>(v: ::std::vec::Vec<W>) -> (*mut W, usize) {
514                let n = v.len();
515                if n == 0 {
516                    return (::core::ptr::null_mut(), 0);
517                }
518                let p = malloc(n.wrapping_mul(::core::mem::size_of::<W>())) as *mut W;
519                if p.is_null() {
520                    return (::core::ptr::null_mut(), 0);
521                }
522                for (i, e) in v.into_iter().enumerate() {
523                    ::core::ptr::write(p.add(i), e);
524                }
525                (p, n)
526            }
527        ));
528        items
529    }
530
531    /// Opaque handles: bare-pointer C type (`z_*_t*` = `Box::into_raw`) + typed
532    /// `_drop`. The C type is an opaque/incomplete struct.
533    fn prereq_opaque_handles(&self, registry: &Registry<()>) -> Vec<syn::Item> {
534        let mut items: Vec<syn::Item> = Vec::new();
535        for (key, _cfg) in sorted_by_key(&self.opaque) {
536            // Keyed directly: this used to spell the key into tokens purely so
537            // `reading_of` could re-key them, twice (#291).
538            let Some(reading) = registry.reading(key) else {
539                continue;
540            };
541            if registry.input_entry(&reading).is_none() && registry.output_entry(&reading).is_none()
542            {
543                continue;
544            }
545            let c_struct = self.c_type_ident(&reading.key());
546            // Opaque/incomplete C type: the handle is `#c_struct *`, which IS the
547            // `Box::into_raw` pointer to the source value.
548            items.push(syn::parse_quote!(
549                #[repr(C)]
550                #[allow(non_camel_case_types)]
551                pub struct #c_struct {
552                    _private: [u8; 0],
553                }
554            ));
555            let src = self.src_ty_of(&reading.key());
556            let drop_ident = self.destructor_symbol(&reading.key());
557            items.push(syn::parse_quote!(
558                #[no_mangle]
559                #[allow(non_snake_case, unused_variables)]
560                pub unsafe extern "C" fn #drop_ident(this_: *mut #c_struct) {
561                    if !this_.is_null() {
562                        drop(::std::boxed::Box::from_raw(this_ as *mut #src));
563                    }
564                }
565            ));
566        }
567        items
568    }
569
570    /// Data structs: `#[repr(C)]` mirror only. Heap (`String`) fields are
571    /// `char*` raw blocks the C user releases individually via the
572    /// `free_memory_function` — no per-struct destructor.
573    fn prereq_data_structs(&self, registry: &Registry<()>) -> Vec<syn::Item> {
574        let mut items: Vec<syn::Item> = Vec::new();
575        for (key, _cfg) in sorted_by_key(&self.data) {
576            let Some(reading) = registry.reading(key) else {
577                continue;
578            };
579            if registry.input_entry(&reading).is_none() && registry.output_entry(&reading).is_none()
580            {
581                continue;
582            }
583            let Some(fields) = self.struct_fields(registry, &reading.key()) else {
584                continue;
585            };
586            let c_struct = self.c_type_ident(&reading.key());
587            let mut field_defs: Vec<TokenStream> = Vec::new();
588            for (fname, fty) in &fields {
589                let wire = self.data_field_wire(fty).unwrap_or_else(|| {
590                    panic!(
591                        "Cbindgen: field `{}` of data struct `{}` has unsupported type `{}`",
592                        fname,
593                        type_short(&reading.key()),
594                        fty
595                    )
596                });
597                field_defs.push(quote!(pub #fname: #wire));
598            }
599            items.push(syn::parse_quote!(
600                #[repr(C)]
601                #[allow(non_camel_case_types)]
602                pub struct #c_struct {
603                    #(#field_defs,)*
604                }
605            ));
606        }
607        items
608    }
609
610    /// Value-opaque types: the opaque `#[repr(C, align(_))]` counterpart is
611    /// defined elsewhere (e.g. a size/align probe generator). Here we emit only
612    /// the fail-closed size+align equality asserts and the typed `_drop` (drops
613    /// the live Rust value in place; NULL/gravestone ⇒ no-op), plus a `_take`
614    /// for types delivered as takeable callback params.
615    fn prereq_value_opaque(&self, registry: &Registry<()>) -> Vec<syn::Item> {
616        let mut items: Vec<syn::Item> = Vec::new();
617        let takeable_keys = self.takeable_type_keys();
618        let mut vo: Vec<(&TypeKey, &ValueOpaqueCfg)> = self.value_opaque.iter().collect();
619        vo.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
620        for (key, cfg) in vo {
621            let Some(reading) = registry.reading(key) else {
622                continue;
623            };
624            if registry.input_entry(&reading).is_none() && registry.output_entry(&reading).is_none()
625            {
626                continue;
627            }
628            let src = self.src_ty_of(&reading.key());
629            let opaque = &cfg.opaque;
630            // `repr_c_struct`: the opaque counterpart is an auto-generated
631            // **visible-field** `#[repr(C)]` mirror (so C reads the fields directly),
632            // not an externally-provided blob. Each field is lowered by
633            // `mirror_field_wire` (scalar / enum / opaque pointer). The size/align
634            // assert below then proves the whole-struct reinterpret sound.
635            if cfg.generate_mirror {
636                let mirror_ident = self.c_type_ident(&reading.key());
637                let fields = self
638                    .struct_fields(registry, &reading.key())
639                    .unwrap_or_else(|| {
640                        panic!(
641                            "Cbindgen::repr_c_struct: `{}` is not a named struct",
642                            type_short(&reading.key())
643                        )
644                    });
645                // Restricted-validity audit (#170 instance 3, #158 instance 3):
646                // a mirror is reinterpreted whole, so a field whose Rust type
647                // rejects some bit patterns is UB the moment C writes one and
648                // hands the struct back.
649                //
650                // Not narrowed to inbound mirrors, though only those are
651                // reachable: converter reachability is not derived from use
652                // today (a declared type resolves BOTH directions whether or
653                // not either is called — the accounting #194/#196 replace), so
654                // "does it cross in" has no truthful answer here. Over-
655                // reporting is the safe direction, and the acknowledgement
656                // below is the escape for a genuinely write-only mirror.
657                let restricted = self.restricted_validity_fields(registry, &reading.key());
658                if !restricted.is_empty() && !cfg.assume_c_field_validity {
659                    let listed: Vec<String> = restricted
660                        .iter()
661                        .map(|(fname, reason)| format!("  `{fname}`: {reason}"))
662                        .collect();
663                    panic!(
664                        "Cbindgen::repr_c_struct: `{}` crosses C's memory by whole-struct \
665                         reinterpret, but these fields have restricted-validity Rust types:\n\
666                         {}\n\
667                         A C caller can write a byte outside those domains, and the reinterpret \
668                         materialises it with no hook to normalise or validate it first (#170, \
669                         #158). Move the field to a `data_struct` (per-field wires), pass it as \
670                         a separate parameter, or widen it to an integer. If this binding's C \
671                         side is trusted to write only in-domain bytes — or never hands the \
672                         mirror back at all — acknowledge it with `.assume_c_field_validity()`.",
673                        type_short(&reading.key()),
674                        listed.join("\n"),
675                    );
676                }
677                let field_defs: Vec<TokenStream> = fields
678                    .iter()
679                    .map(|(fname, fty)| {
680                        let wire = self.mirror_field_wire(fty).unwrap_or_else(|| {
681                            panic!(
682                                "Cbindgen::repr_c_struct: field `{}` of `{}` has unsupported \
683                                 type `{}` (expected a scalar, a declared `enum_type`, or an \
684                                 opaque pointer `Option<Box<T>>`/`Box<T>` with `T` an `opaque_ptr`)",
685                                fname,
686                                type_short(&reading.key()),
687                                fty
688                            )
689                        });
690                        quote!(pub #fname: #wire)
691                    })
692                    .collect();
693                items.push(syn::parse_quote!(
694                    #[repr(C)]
695                    #[allow(non_camel_case_types)]
696                    pub struct #mirror_ident {
697                        #(#field_defs,)*
698                    }
699                ));
700                // A mirror that needs `gravestone()` (only the bare-`Box<T>` fallback —
701                // nullable owned-pointer fields are nulled in place) gets an
702                // auto-generated `Gravestone` from the source type's `Default`. Nullable
703                // mirrors emit nothing here, so they impose no `Default` requirement.
704                if self.mirror_needs_gravestone_impl(registry, &reading.key()) {
705                    items.push(syn::parse_quote!(
706                        impl ::prebindgen_c_runtime::Gravestone for #mirror_ident {
707                            #[inline]
708                            fn rust_gravestone() -> #src {
709                                <#src as ::core::default::Default>::default()
710                            }
711                        }
712                    ));
713                }
714            }
715            // Fail-closed size/align equality guard (proves the transmute sound).
716            items.push(syn::parse_quote!(
717                const _: () = {
718                    assert!(
719                        ::core::mem::size_of::<#src>() == ::core::mem::size_of::<#opaque>(),
720                        "value_opaque: Rust type and opaque counterpart differ in size"
721                    );
722                    assert!(
723                        ::core::mem::align_of::<#src>() == ::core::mem::align_of::<#opaque>(),
724                        "value_opaque: Rust type and opaque counterpart differ in alignment"
725                    );
726                };
727            ));
728            // Autogenerated transmute glue: the single place that owns the
729            // unsafe rust<->opaque reinterpretation. `Gravestone` (user logic)
730            // and the converters below are all expressed via these methods.
731            items.push(syn::parse_quote!(
732                impl ::prebindgen_c_runtime::Transmute for #opaque {
733                    type Rust = #src;
734                    #[inline]
735                    fn from_rust(value: Self::Rust) -> Self {
736                        let __v = ::core::mem::ManuallyDrop::new(value);
737                        unsafe {
738                            ::core::ptr::read(&*__v as *const Self::Rust as *const Self)
739                        }
740                    }
741                    #[inline]
742                    fn into_rust(self) -> Self::Rust {
743                        let __v = ::core::mem::ManuallyDrop::new(self);
744                        unsafe {
745                            ::core::ptr::read(&*__v as *const Self as *const Self::Rust)
746                        }
747                    }
748                    #[inline]
749                    fn as_rust(&self) -> &Self::Rust {
750                        unsafe { &*(self as *const Self as *const Self::Rust) }
751                    }
752                    #[inline]
753                    fn as_rust_mut(&mut self) -> &mut Self::Rust {
754                        unsafe { &mut *(self as *mut Self as *mut Self::Rust) }
755                    }
756                }
757            ));
758            let drop_ident = self.destructor_symbol(&reading.key());
759            // Unconditional drop: safe because a moved-from slot holds a
760            // gravestone (a valid, safely-droppable empty value), so dropping
761            // it is a harmless no-op; a live slot drops normally.
762            items.push(syn::parse_quote!(
763                #[no_mangle]
764                #[allow(non_snake_case, unused_variables)]
765                pub unsafe extern "C" fn #drop_ident(this_: *mut #opaque) {
766                    if !this_.is_null() {
767                        ::core::ptr::drop_in_place(
768                            <#opaque as ::prebindgen_c_runtime::Transmute>::as_rust_mut(&mut *this_),
769                        );
770                    }
771                }
772            ));
773            // For a type delivered as a takeable callback param, also emit a
774            // public `<base>_take(dst, src)`: move `src`'s value into `dst`. For
775            // an `opaque_owned_struct` type, leave `src` a gravestone (so the
776            // trampoline's post-call drop is a no-op); an `opaque_data_struct` type owns
777            // nothing, so the leftover bitwise copy in `src` drops harmlessly and
778            // no write-back is needed. This is the C user's "take" operation.
779            if takeable_keys.contains(key) {
780                let take_ident = self.take_symbol(&reading.key());
781                // Same inferred write-back as a consume (field-null for a nullable
782                // mirror, `gravestone()` for a bare-`Box` mirror / non-mirror owned).
783                let writeback =
784                    self.value_opaque_writeback(registry, &reading.key(), &format_ident!("src"));
785                items.push(syn::parse_quote!(
786                    #[no_mangle]
787                    #[allow(non_snake_case, unused_variables)]
788                    pub unsafe extern "C" fn #take_ident(
789                        dst: *mut #opaque,
790                        src: *mut #opaque,
791                    ) {
792                        if dst.is_null() || src.is_null() {
793                            return;
794                        }
795                        ::core::ptr::write(dst, ::core::ptr::read(src));
796                        #writeback
797                    }
798                ));
799            }
800        }
801        items
802    }
803
804    /// Enums: `#[repr(C)]` mirror — variant idents with each discriminant
805    /// **re-emitted verbatim**, exactly as the source wrote it.
806    ///
807    /// Deliberately NOT routed through the shared
808    /// [`enum_discriminant_values`](prebindgen_registry::types_util::enum_discriminant_values).
809    /// That helper resolves each variant to a concrete `i64`, which is what an
810    /// adapter needs when it must *know the number* — JniGenBuilder's `jint` decode
811    /// and the Kotlin `value(N)` constants. This mirror needs no number: it is
812    /// Rust source that cbindgen re-reads, so passing the expression through
813    /// keeps every discriminant C already accepted — a `const` or `cfg`-driven
814    /// expression, and any value the source's own `repr` admits, including
815    /// ones outside `i64`. Resolving here would narrow that domain to what
816    /// `i64` and a literal can express, for no gain.
817    ///
818    /// The two adapters therefore agree on the *rule* (Rust's own assignment
819    /// order, which the shared helper encodes) while differing on what they
820    /// need from it — a number versus a spelling.
821    fn prereq_enums(
822        &self,
823        registry: &Registry<()>,
824        emit: &prebindgen_registry::Emit,
825    ) -> Vec<syn::Item> {
826        let mut items: Vec<syn::Item> = Vec::new();
827        for (key, _cfg) in sorted_by_key(&self.enums) {
828            let Some(reading) = registry.reading(key) else {
829                continue;
830            };
831            if registry.input_entry(&reading).is_none() && registry.output_entry(&reading).is_none()
832            {
833                continue;
834            }
835            let Some(e) = unit_enum(registry, &reading.key()) else {
836                continue;
837            };
838            let cname = self.c_type_ident(&reading.key());
839            // The C mirror re-states the discriminant **as written** — `= 0x07`
840            // stays `0x07` — which is the one consumer `EnumValue`'s retained
841            // syntax exists for, and the model's own docs name it.
842            let variants = e.values.iter().map(|v| {
843                let id = &v.name;
844                match emit.discriminant(v) {
845                    Some(expr) => quote!(#id = #expr),
846                    None => quote!(#id),
847                }
848            });
849            items.push(syn::parse_quote!(
850                #[repr(C)]
851                #[derive(Copy, Clone, Debug, Eq, PartialEq)]
852                #[allow(non_camel_case_types)]
853                pub enum #cname {
854                    #(#variants),*
855                }
856            ));
857        }
858        items
859    }
860
861    /// Tagged unions: the `#[repr(C)]` mirror with payload variants, which
862    /// cbindgen renders as a tag enum plus a `union` of the variant bodies —
863    /// the idiomatic C tagged union, with no hand-written header fragment.
864    /// Variant shape is mirrored faithfully (named stays named, tuple stays
865    /// tuple, unit stays unit); each payload field takes the wire chosen by
866    /// [`CbindgenBuilder::payload_field_wire`].
867    ///
868    /// A union whose payload wires own memory also gets a typed
869    /// `<base>_drop(t_t *)` that frees the **active arm** and nulls the freed
870    /// slots, so a second drop is a no-op. A union of plain data owns nothing
871    /// and gets no drop.
872    fn prereq_tagged_unions(
873        &self,
874        registry: &Registry<()>,
875        emit: &prebindgen_registry::Emit,
876    ) -> Vec<syn::Item> {
877        let mut items: Vec<syn::Item> = Vec::new();
878        for (key, _cfg) in sorted_by_key(&self.tagged_unions) {
879            let Some(reading) = registry.reading(key) else {
880                continue;
881            };
882            if registry.input_entry(&reading).is_none() && registry.output_entry(&reading).is_none()
883            {
884                continue;
885            }
886            let Some(e) = payload_enum(registry, &reading.key()) else {
887                continue;
888            };
889            let cname = self.c_type_ident(&reading.key());
890
891            let mut variant_defs: Vec<TokenStream> = Vec::new();
892            // Per-variant drop arm, collected only for variants that own
893            // something; the rest fall to a single wildcard arm.
894            let mut drop_arms: Vec<TokenStream> = Vec::new();
895            for a in &e.alternatives {
896                let vident = &a.name;
897                let wires: Vec<syn::Type> = a
898                    .fields
899                    .iter()
900                    .map(|f| self.payload_wire_of(&reading.key(), vident, f, registry))
901                    .collect();
902                // `Alternative::spell` writes the delimiters the source wrote,
903                // which is what the three-armed `syn::Fields` match was doing —
904                // and `Field::bind` decides `name: wire` or `wire` per field.
905                let defs: Vec<TokenStream> = a
906                    .fields
907                    .iter()
908                    .zip(&wires)
909                    .map(|(f, w)| f.bind(w))
910                    .collect();
911                variant_defs.push(emit.shape(a, quote!(#vident), &defs));
912
913                // Drop arm: bind every field, free the owning ones.
914                let owning: Vec<(usize, &Field, &syn::Type)> = a
915                    .fields
916                    .iter()
917                    .zip(&wires)
918                    .enumerate()
919                    .filter(|(_, (f, w))| self.payload_wire_owns(&f.ty, w, registry))
920                    .map(|(i, (f, w))| (i, f, w))
921                    .collect();
922                if owning.is_empty() {
923                    continue;
924                }
925                let binds: Vec<syn::Ident> = (0..a.fields.len())
926                    .map(|i| format_ident!("__f{}", i))
927                    .collect();
928                let parts: Vec<TokenStream> = a
929                    .fields
930                    .iter()
931                    .zip(&binds)
932                    .map(|(f, b)| f.bind(b))
933                    .collect();
934                let pattern = emit.shape(a, quote!(#cname::#vident), &parts);
935                let frees = owning.iter().map(|(i, f, _)| {
936                    let b = &binds[*i];
937                    self.payload_free_stmt(&f.ty, b, registry)
938                });
939                drop_arms.push(quote!(#pattern => { #(#frees)* }));
940            }
941
942            items.push(syn::parse_quote!(
943                #[repr(C)]
944                #[allow(non_camel_case_types)]
945                pub enum #cname {
946                    #(#variant_defs),*
947                }
948            ));
949
950            // The same predicate a CONTAINING struct uses to decide whether to
951            // call this drop, so a nested union can never be freed through a
952            // symbol that was not emitted.
953            if self.tagged_union_has_drop(&reading, registry) {
954                debug_assert!(!drop_arms.is_empty(), "has_drop implies an owning arm");
955                let drop_ident = self.destructor_symbol(&reading.key());
956                // The drop is a second C entry point into the same bytes, so it
957                // owes the same tag check as the input converter — `&mut *this_`
958                // on an out-of-range tag would be the very UB that check exists
959                // to prevent. It emits that check from the same place, and,
960                // having nowhere to report to, ignores the value (there is no
961                // live arm to release), which keeps `_drop` the always-safe
962                // no-op it is everywhere else.
963                let tag_guard = self.tag_guard(
964                    &cname,
965                    e.alternatives.len(),
966                    quote!((*this_)),
967                    quote!(return;),
968                );
969                items.push(syn::parse_quote!(
970                    #[no_mangle]
971                    #[allow(non_snake_case, unused_variables)]
972                    pub unsafe extern "C" fn #drop_ident(
973                        this_: *mut ::core::mem::MaybeUninit<#cname>,
974                    ) {
975                        if this_.is_null() {
976                            return;
977                        }
978                        #tag_guard
979                        match (*this_).assume_init_mut() {
980                            #(#drop_arms)*
981                            _ => {}
982                        }
983                    }
984                ));
985            }
986        }
987        items
988    }
989
990    /// The wire of one payload field, or a generation error naming the
991    /// offending variant field and the supported set.
992    fn payload_wire_of(
993        &self,
994        key: &TypeKey,
995        variant: &syn::Ident,
996        field: &Field,
997        registry: &Registry<()>,
998    ) -> syn::Type {
999        self.payload_field_wire(&field.ty, registry)
1000            .unwrap_or_else(|reason| {
1001                panic!(
1002                    "Cbindgen::tagged_union: payload `{}::{}{}` of type `{}` cannot cross: {}",
1003                    type_short(key),
1004                    variant,
1005                    match &field.name {
1006                        Some(n) => format!(".{n}"),
1007                        None => String::new(),
1008                    },
1009                    field.ty,
1010                    reason,
1011                )
1012            })
1013    }
1014
1015    /// Release one owning payload slot held behind `binding` (a `&mut` to the
1016    /// wire, from a `match &mut *this_` arm) and null it, so a second drop of
1017    /// the same union is a no-op. A `char *` block goes back to the C
1018    /// allocator; an opaque pointer is re-boxed and dropped, running the Rust
1019    /// destructor.
1020    fn payload_free_stmt(
1021        &self,
1022        fty: &TypeRef,
1023        binding: &syn::Ident,
1024        registry: &Registry<()>,
1025    ) -> TokenStream {
1026        if r_is_string(fty) {
1027            return quote!(
1028                free(*#binding as *mut ::core::ffi::c_void);
1029                *#binding = ::core::ptr::null_mut();
1030            );
1031        }
1032        // A nested `data_struct` payload crosses BY VALUE, so the arm binds the
1033        // mirror itself and what has to be released is each of its OWNING
1034        // fields — reached through the binding and nulled in place, exactly as
1035        // a directly-owning payload is. This is the shape zenoh-flat#30 needs
1036        // (`ReplyResult`'s alternatives are structs whose fields are handles),
1037        // and without it those fields would leak silently.
1038        let owning = self.owning_data_struct_fields(fty, registry);
1039        if !owning.is_empty() {
1040            let frees = owning.iter().map(|(fname, fty)| {
1041                if r_is_string(fty) {
1042                    quote!(
1043                        free((*#binding).#fname as *mut ::core::ffi::c_void);
1044                        (*#binding).#fname = ::core::ptr::null_mut();
1045                    )
1046                } else if self.tagged_union_has_drop(fty, registry) {
1047                    // The field is ANOTHER union, crossing by value. Its own
1048                    // typed drop releases whichever arm is live and nulls the
1049                    // slot, so this stays idempotent like every other arm here
1050                    // — and the owning pointer is reached even though it is two
1051                    // levels down. Nothing else can reach it: a union arm is not
1052                    // a top-level struct field the C caller releases by hand.
1053                    let drop_ident = self.destructor_symbol(&fty.key());
1054                    quote!(#drop_ident(&mut (*#binding).#fname);)
1055                } else {
1056                    // `owning_data_struct_fields` yields exactly the two shapes
1057                    // above (`data_field_owns`), so this is unreachable — and a
1058                    // silent fall-through here would be a leak, which is the
1059                    // defect this whole path exists to prevent.
1060                    panic!(
1061                        "Cbindgen: data-struct field `{}` of type `{}` is owning but has no \
1062                         release form (expected a `String` or a declared `tagged_union`)",
1063                        fname, fty,
1064                    )
1065                }
1066            });
1067            return quote!(#(#frees)*);
1068        }
1069        let src_inner = self.src_ty_of(&r_boxed_inner(fty).unwrap_or(fty).key());
1070        quote!(
1071            if !(*#binding).is_null() {
1072                drop(::std::boxed::Box::from_raw(*#binding as *mut #src_inner));
1073                *#binding = ::core::ptr::null_mut();
1074            }
1075        )
1076    }
1077
1078    /// Tagged-union **input**: **validate the tag**, then `match` the C union
1079    /// back to the source enum, converting each arm's payload through the
1080    /// per-field policy. The generalization of [`Self::in_enum`] from "match
1081    /// idents" to "match idents and convert each arm's fields" — fallible for
1082    /// the same reason, and by the same rule (#158): a Rust `enum` must never
1083    /// be *materialised* from C-supplied bytes without checking first, because
1084    /// an undeclared discriminant is UB at the boundary, before any `match`.
1085    ///
1086    /// So the wire is [`::core::mem::MaybeUninit`] over the mirror. A
1087    /// `#[repr(C)]` enum with payload variants is laid out as a leading
1088    /// discriminant of a C `int` followed by the variant union, so the tag is
1089    /// read from the front as a plain `c_int` and range-checked against the
1090    /// variants (the mirror carries no explicit discriminants, so its tags are
1091    /// declaration order `0..N`). Only then is the value `assume_init`ed —
1092    /// which is sound because [`CbindgenBuilder::payload_field_wire`] makes every
1093    /// payload wire bit-pattern-agnostic, leaving the tag as the sole
1094    /// obligation.
1095    pub(crate) fn in_tagged_union(
1096        &self,
1097        ty: &TypeRef,
1098        r: &impl Conversions<()>,
1099        emit: &prebindgen_registry::Emit,
1100    ) -> Option<ConverterImpl<()>> {
1101        let key = ty.key();
1102        if !self.tagged_unions.contains_key(&key) {
1103            return None;
1104        }
1105        let e = payload_enum(r, &key)?;
1106        let name = Self::in_name_of(&ty.key());
1107        let cname = self.c_type_ident(&ty.key());
1108        let src = self.src_ty_of(&ty.key());
1109        // A payload that crosses through its own converter needs that converter
1110        // to exist before this one can call it. `subs` only drives the
1111        // post-resolution propagation pass, so it cannot order the build —
1112        // returning `None` here is the resolver's DEFERRAL protocol, and it
1113        // retries at the next fixed point. Without this the payload silently
1114        // degrades to a passthrough and the generated code does not compile.
1115        for a in &e.alternatives {
1116            for f in &a.fields {
1117                if self.payload_needs_converter(&f.ty) && r.input_entry(&f.ty).is_none() {
1118                    return None;
1119                }
1120            }
1121        }
1122        let mut subs: Vec<TypeKey> = Vec::new();
1123        let arms: Vec<TokenStream> = e
1124            .alternatives
1125            .iter()
1126            .map(|a| {
1127                let vident = &a.name;
1128                let binds: Vec<syn::Ident> = (0..a.fields.len())
1129                    .map(|i| format_ident!("__f{}", i))
1130                    .collect();
1131                let parts: Vec<TokenStream> = a
1132                    .fields
1133                    .iter()
1134                    .zip(&binds)
1135                    .map(|(f, b)| f.bind(b))
1136                    .collect();
1137                let from = emit.shape(a, quote!(#cname::#vident), &parts);
1138                let exprs: Vec<TokenStream> = a
1139                    .fields
1140                    .iter()
1141                    .zip(&binds)
1142                    .map(|(f, b)| {
1143                        // Every payload that crosses through a converter of its
1144                        // own — a declared `enum_type`, a nested `data_struct`,
1145                        // an opaque handle, a converted leaf — is a resolver
1146                        // dependency, so its converter exists before this one is
1147                        // emitted. Without it the payload silently falls back to
1148                        // a passthrough and the generated code does not compile.
1149                        if self.payload_needs_converter(&f.ty) {
1150                            subs.push(f.ty.key());
1151                        }
1152                        self.payload_in_expr(&f.ty, b, r)
1153                    })
1154                    .collect();
1155                let inits: Vec<TokenStream> = a
1156                    .fields
1157                    .iter()
1158                    .zip(&exprs)
1159                    .map(|(f, e)| f.bind(e))
1160                    .collect();
1161                let to = emit.shape(a, quote!(#src::#vident), &inits);
1162                quote!(#from => #to,)
1163            })
1164            .collect();
1165        let bad_msg = format!(
1166            "invalid tag {{}} for `{cname}` (expected 0..{})",
1167            e.alternatives.len()
1168        );
1169        let tag_guard = self.tag_guard(
1170            &cname,
1171            e.alternatives.len(),
1172            quote!(v),
1173            quote!(return ::core::result::Result::Err(::std::format!(#bad_msg, __tag));),
1174        );
1175        let function: syn::ItemFn = syn::parse_quote!(
1176            #[allow(non_snake_case, unused_variables, dead_code)]
1177            pub(crate) unsafe fn #name(
1178                v: ::core::mem::MaybeUninit<#cname>,
1179            ) -> ::core::result::Result<#src, ::std::string::String> {
1180                #tag_guard
1181                let v = v.assume_init();
1182                ::core::result::Result::Ok(match v { #(#arms)* })
1183            }
1184        );
1185        Some(ConverterImpl {
1186            subs,
1187            destination: syn::parse_quote!(::core::mem::MaybeUninit<#cname>),
1188            function,
1189            pre_stages: vec![],
1190            niches: Niches::empty(),
1191            metadata: (),
1192        })
1193    }
1194
1195    /// The statements that make a C-supplied `MaybeUninit<mirror>` safe to
1196    /// `assume_init`: read the leading discriminant as a plain `c_int` and
1197    /// reject anything outside `0..variants`.
1198    ///
1199    /// `slot` is an expression for the `MaybeUninit` in scope and `on_bad` is
1200    /// what to do with an out-of-range tag — the **only** thing the two C entry
1201    /// points into these bytes differ in (the input converter returns `Err`,
1202    /// the typed drop returns `()` and so just bails). Passing that difference
1203    /// in, rather than letting the drop repeat the check inline, is what keeps
1204    /// the two from drifting apart.
1205    fn tag_guard(
1206        &self,
1207        cname: &syn::Ident,
1208        variants: usize,
1209        slot: TokenStream,
1210        on_bad: TokenStream,
1211    ) -> TokenStream {
1212        let n = variants as i64;
1213        let bounds_msg = format!(
1214            "`{cname}`: a #[repr(C)] enum with payload variants must be at least as large as \
1215             its C `int` discriminant"
1216        );
1217        quote!(
1218            const _: () = {
1219                assert!(
1220                    ::core::mem::size_of::<#cname>()
1221                        >= ::core::mem::size_of::<::core::ffi::c_int>(),
1222                    #bounds_msg
1223                );
1224            };
1225            let __tag: ::core::ffi::c_int =
1226                ::core::ptr::read(#slot.as_ptr() as *const ::core::ffi::c_int);
1227            if !((__tag as i64) >= 0 && (__tag as i64) < #n) {
1228                #on_bad
1229            }
1230        )
1231    }
1232
1233    /// Tagged-union **output**: `match` the source enum to the C union,
1234    /// converting each arm's payload. The counterpart of
1235    /// [`Self::in_tagged_union`]; a `String` payload is allocated here and
1236    /// released by the union's typed drop.
1237    pub(crate) fn out_tagged_union(
1238        &self,
1239        ty: &TypeRef,
1240        r: &impl Conversions<()>,
1241        emit: &prebindgen_registry::Emit,
1242    ) -> Option<ConverterImpl<()>> {
1243        let key = ty.key();
1244        if !self.tagged_unions.contains_key(&key) {
1245            return None;
1246        }
1247        let e = payload_enum(r, &key)?;
1248        let name = Self::out_name_of(&ty.key());
1249        let cname = self.c_type_ident(&ty.key());
1250        let src = self.src_ty_of(&ty.key());
1251        // Deferral, as in `in_tagged_union` — the output counterpart.
1252        for a in &e.alternatives {
1253            for f in &a.fields {
1254                if self.payload_needs_converter(&f.ty) && r.output_entry(&f.ty).is_none() {
1255                    return None;
1256                }
1257            }
1258        }
1259        let mut subs: Vec<TypeKey> = Vec::new();
1260        let arms: Vec<TokenStream> = e
1261            .alternatives
1262            .iter()
1263            .map(|a| {
1264                let vident = &a.name;
1265                let binds: Vec<syn::Ident> = (0..a.fields.len())
1266                    .map(|i| format_ident!("__f{}", i))
1267                    .collect();
1268                let parts: Vec<TokenStream> = a
1269                    .fields
1270                    .iter()
1271                    .zip(&binds)
1272                    .map(|(f, b)| f.bind(b))
1273                    .collect();
1274                let from = emit.shape(a, quote!(#src::#vident), &parts);
1275                let exprs: Vec<TokenStream> = a
1276                    .fields
1277                    .iter()
1278                    .zip(&binds)
1279                    .map(|(f, b)| {
1280                        if self.payload_needs_converter(&f.ty) {
1281                            subs.push(f.ty.key());
1282                        }
1283                        self.payload_out_expr(&f.ty, b, r)
1284                    })
1285                    .collect();
1286                let inits: Vec<TokenStream> = a
1287                    .fields
1288                    .iter()
1289                    .zip(&exprs)
1290                    .map(|(f, e)| f.bind(e))
1291                    .collect();
1292                let to = emit.shape(a, quote!(#cname::#vident), &inits);
1293                quote!(#from => #to,)
1294            })
1295            .collect();
1296        // Same wire as the input direction — one mirror type serves both, and a
1297        // union carried through a `data_struct` field has only one field type
1298        // to be. Rust always writes a live arm, so nothing is validated here.
1299        let function: syn::ItemFn = syn::parse_quote!(
1300            #[allow(non_snake_case, unused_variables, dead_code)]
1301            pub(crate) fn #name(v: #src) -> ::core::mem::MaybeUninit<#cname> {
1302                ::core::mem::MaybeUninit::new(match v { #(#arms)* })
1303            }
1304        );
1305        Some(ConverterImpl {
1306            subs,
1307            destination: syn::parse_quote!(::core::mem::MaybeUninit<#cname>),
1308            function,
1309            pre_stages: vec![],
1310            niches: Niches::empty(),
1311            metadata: (),
1312        })
1313    }
1314
1315    /// One payload field, C wire → Rust value. Mirrors the `data_struct`
1316    /// input policy, plus the opaque-pointer and declared-enum cases the
1317    /// mirror wire allows.
1318    fn payload_in_expr(
1319        &self,
1320        fty: &TypeRef,
1321        b: &syn::Ident,
1322        registry: &impl Conversions<()>,
1323    ) -> TokenStream {
1324        if r_is_string(fty) {
1325            return quote!(if #b.is_null() {
1326                ::std::string::String::new()
1327            } else {
1328                ::std::ffi::CStr::from_ptr(#b).to_string_lossy().into_owned()
1329            });
1330        }
1331        if self.enums.contains_key(&fty.key()) {
1332            // The payload rides as `MaybeUninit<enum mirror>` and goes through
1333            // the same validating decode a top-level enum parameter does; an
1334            // out-of-range one propagates out of the union's own converter.
1335            let conv = Self::in_name_of(&fty.key());
1336            return quote!(#conv(#b)?);
1337        }
1338        // The same opaque-pointer arm the wire took, for a spelling with no
1339        // `Box` in it: the C caller still hands over a `*mut handle_t` it gave
1340        // up ownership of, so the pointer is reclaimed the same way — the value
1341        // is just moved out of the box instead of kept in one. Conversion
1342        // follows the SYNTAX; the C type followed `kind` + the declaration.
1343        if let Some(inner) = self.declared_opaque_payload_inner(fty) {
1344            let src_inner = self.src_ty_of(&inner);
1345            let owned = quote!(*::std::boxed::Box::from_raw(#b as *mut #src_inner));
1346            let null_msg = format!(
1347                "null payload for `{}` (a non-optional handle payload cannot be NULL — the \
1348                 union may already have been dropped)",
1349                type_short(&inner)
1350            );
1351            return if fty.optional_inner().is_some() {
1352                quote!(if #b.is_null() {
1353                    ::core::option::Option::None
1354                } else {
1355                    ::core::option::Option::Some(#owned)
1356                })
1357            } else {
1358                quote!({
1359                    if #b.is_null() {
1360                        return ::core::result::Result::Err(
1361                            ::std::string::String::from(#null_msg),
1362                        );
1363                    }
1364                    #owned
1365                })
1366            };
1367        }
1368        if let Some(inner) = r_boxed_inner(fty) {
1369            let src_inner = self.src_ty_of(&inner.key());
1370            let boxed = quote!(::std::boxed::Box::from_raw(#b as *mut #src_inner));
1371            return if fty.optional_inner().is_some() {
1372                quote!(if #b.is_null() {
1373                    ::core::option::Option::None
1374                } else {
1375                    ::core::option::Option::Some(#boxed)
1376                })
1377            } else {
1378                // A bare `Box<T>` has no null representation, so a NULL slot
1379                // cannot be decoded — and it is reachable, not hypothetical:
1380                // the typed drop nulls the arm it frees, so a union passed back
1381                // in after being dropped arrives here NULL. Same rule as the
1382                // tag: report it, never materialise it.
1383                let null_msg = format!(
1384                    "null payload for `{}` (a non-optional `Box` payload cannot be NULL — the \
1385                     union may already have been dropped)",
1386                    type_short(&inner.key())
1387                );
1388                quote!({
1389                    if #b.is_null() {
1390                        return ::core::result::Result::Err(
1391                            ::std::string::String::from(#null_msg),
1392                        );
1393                    }
1394                    #boxed
1395                })
1396            };
1397        }
1398        // A `bool` payload rides as `MaybeUninit<bool>` (see `bool_wire`), so
1399        // the byte C wrote is normalised rather than materialised.
1400        if r_is_bool(fty) {
1401            return bool_in_expr(quote!(#b));
1402        }
1403        // A scalar is its own wire and needs no call.
1404        if r_is_scalar(fty) {
1405            return quote!(#b);
1406        }
1407        // Everything else rides its own resolved input converter — the wire
1408        // came from that converter's destination, so the two cannot disagree.
1409        // A fallible one propagates with `?`, which the union's own `Result`
1410        // already provides.
1411        match registry.input_entry(fty) {
1412            Some(entry) => {
1413                let conv = &entry.function.sig.ident;
1414                if returns_result(&entry.function.sig.output) {
1415                    quote!(#conv(#b)?)
1416                } else {
1417                    quote!(#conv(#b))
1418                }
1419            }
1420            None => quote!(#b),
1421        }
1422    }
1423
1424    /// One payload field, Rust value → C wire. The `String` arm allocates the
1425    /// `char *` block the union's typed drop later frees.
1426    fn payload_out_expr(
1427        &self,
1428        fty: &TypeRef,
1429        b: &syn::Ident,
1430        registry: &impl Conversions<()>,
1431    ) -> TokenStream {
1432        if r_is_string(fty) {
1433            return quote!(__cbg_alloc_cstr(#b));
1434        }
1435        if self.enums.contains_key(&fty.key()) {
1436            let conv = Self::out_name_of(&fty.key());
1437            return quote!(::core::mem::MaybeUninit::new(#conv(#b)));
1438        }
1439        // The peer of the input arm above: an owned value the C side must later
1440        // release, so it is boxed HERE rather than having arrived boxed.
1441        if let Some(inner) = self.declared_opaque_payload_inner(fty) {
1442            let c = self.c_type_ident(&inner);
1443            return if fty.optional_inner().is_some() {
1444                quote!(match #b {
1445                    ::core::option::Option::Some(__v) => {
1446                        ::std::boxed::Box::into_raw(::std::boxed::Box::new(__v)) as *mut #c
1447                    }
1448                    ::core::option::Option::None => ::core::ptr::null_mut(),
1449                })
1450            } else {
1451                quote!(::std::boxed::Box::into_raw(::std::boxed::Box::new(#b)) as *mut #c)
1452            };
1453        }
1454        if let Some(inner) = r_boxed_inner(fty) {
1455            let c = self.c_type_ident(&inner.key());
1456            return if fty.optional_inner().is_some() {
1457                quote!(match #b {
1458                    ::core::option::Option::Some(__b) => {
1459                        ::std::boxed::Box::into_raw(__b) as *mut #c
1460                    }
1461                    ::core::option::Option::None => ::core::ptr::null_mut(),
1462                })
1463            } else {
1464                quote!(::std::boxed::Box::into_raw(#b) as *mut #c)
1465            };
1466        }
1467        // The counterpart of the normalising read above: Rust always writes a
1468        // valid `0`/`1`, so this only wraps.
1469        if r_is_bool(fty) {
1470            return bool_out_expr(quote!(#b));
1471        }
1472        if r_is_scalar(fty) {
1473            return quote!(#b);
1474        }
1475        // The output counterpart of the input dispatch above. Acceptance —
1476        // including the refusal of a FALLIBLE output converter, which a union
1477        // cannot report through — is decided once in `payload_field_wire`, so
1478        // this site only emits the call.
1479        match registry.output_entry(fty) {
1480            Some(entry) => {
1481                let conv = entry.function.sig.ident.clone();
1482                quote!(#conv(#b))
1483            }
1484            None => quote!(#b),
1485        }
1486    }
1487
1488    /// Callback closure structs: one `#[repr(C)]` `{ context, call, drop }`
1489    /// per declared signature actually used (its `impl Fn(...)` input
1490    /// resolved). `call` takes each arg's output wire (the owned handle the
1491    /// C callback must drop) plus the `void *context`; `drop` releases the
1492    /// context. Deterministic order by emitted name.
1493    fn prereq_callback_structs(&self, registry: &Registry<()>) -> Vec<syn::Item> {
1494        let mut items: Vec<syn::Item> = Vec::new();
1495        // The declaration's own argument types. `CallbackKey` is a list of
1496        // identities — what the map is keyed by — and the arguments it was
1497        // declared with are beside it, so neither is rebuilt from the other
1498        // (#291).
1499        let mut cb_keys: Vec<(&CallbackKey, &CbCfg)> = self.callbacks.iter().collect();
1500        cb_keys.sort_by_key(|(k, _)| self.callback_c_name(k));
1501        for (key, cfg) in cb_keys {
1502            let args: Vec<syn::Type> = cfg.args.clone();
1503            // Emit only if the callback is required (its input resolved); skip a
1504            // declared-but-unused signature.
1505            if registry
1506                .reading_of(&callback_fn_type(&args))
1507                .and_then(|tr| registry.input_entry(&tr))
1508                .is_none()
1509            {
1510                continue;
1511            }
1512            let takeable = &self.callbacks.get(key).expect("callback cfg").takeable;
1513            let mut arg_wires: Vec<syn::Type> = Vec::new();
1514            for (i, a) in args.iter().enumerate() {
1515                // `&[E]` slice arg → TWO C `call` params: `const E_wire *` + `size_t`
1516                // (the slice delivered by reference, zero-copy).
1517                if let Some((_src, elem_wire)) = self.callback_slice_elem_wire(a) {
1518                    arg_wires.push(syn::parse_quote!(*const #elem_wire));
1519                    arg_wires.push(syn::parse_quote!(usize));
1520                    continue;
1521                }
1522                let wire = registry
1523                    .reading_of(a)
1524                    .and_then(|tr| registry.output_entry(&tr))
1525                    .unwrap_or_else(|| {
1526                        panic!(
1527                            "Cbindgen: callback arg `{}` has no output converter (declare it \
1528                             as a opaque_ptr/data_struct/enum_type)",
1529                            a.to_token_stream()
1530                        )
1531                    })
1532                    .destination
1533                    .clone();
1534                // Takeable params are delivered as an owned pointer.
1535                if takeable.contains(&i) {
1536                    arg_wires.push(syn::parse_quote!(*mut #wire));
1537                } else {
1538                    arg_wires.push(wire);
1539                }
1540            }
1541            let c_struct = self.callback_c_ident(key);
1542            items.push(syn::parse_quote!(
1543                #[repr(C)]
1544                #[allow(non_camel_case_types)]
1545                pub struct #c_struct {
1546                    pub context: *mut ::core::ffi::c_void,
1547                    pub call: ::core::option::Option<
1548                        unsafe extern "C" fn(#(#arg_wires,)* *mut ::core::ffi::c_void),
1549                    >,
1550                    pub drop: ::core::option::Option<
1551                        unsafe extern "C" fn(*mut ::core::ffi::c_void),
1552                    >,
1553                }
1554            ));
1555        }
1556        items
1557    }
1558}
1559
1560impl CbindgenBuilder {
1561    /// State this binding into `registry` — see `JniGenBuilder::declare_into`.
1562    ///
1563    /// Push, not pull: the build script calls this, and the registry never
1564    /// calls back. cbindgen declares no consts (it has no const mechanism, so
1565    /// every captured const re-emits verbatim) and no decompositions.
1566    /// Binding-local fns declared by `convert!(..).local(..)`.
1567    fn collect_local_functions(&self) -> Vec<(syn::ItemFn, String)> {
1568        let mut result = Vec::new();
1569        let mut seen = HashMap::<syn::Ident, String>::new();
1570        for (ident, path, sig) in self.convert_decls.iter().flat_map(|decl| decl.locals()) {
1571            let origin = prebindgen_registry::decl::local_path_prefix(path);
1572            let mut sig = sig.clone();
1573            sig.ident = ident.clone();
1574            let signature = quote!(#origin #sig).to_string();
1575            match seen.get(ident) {
1576                Some(previous) if previous == &signature => continue,
1577                Some(_) => panic!(
1578                    "binding-local conversion fn `{ident}` is declared with two different signatures"
1579                ),
1580                None => {
1581                    seen.insert(ident.clone(), signature);
1582                }
1583            }
1584            let item: syn::ItemFn = syn::parse_quote!(#sig { unimplemented!() });
1585            result.push((item, origin));
1586        }
1587        result
1588    }
1589
1590    /// State this binding into `registry`, then resolve it — see
1591    /// `JniGenBuilder::build`.
1592    /// Read the source, resolve every crossing, and hand back the binding —
1593    /// see `JniGenBuilder::build`.
1594    pub fn build(self) -> Result<Cbindgen, prebindgen_registry::WriteRustError> {
1595        let flat = self
1596            .sources
1597            .clone()
1598            .build()
1599            .map_err(prebindgen_registry::ScanError::from)?;
1600        let registry = prebindgen_registry::Registry::builder(flat)?;
1601        self.build_with(registry)
1602    }
1603
1604    /// [`Self::build`] over a registry described elsewhere — the test seam.
1605    pub(crate) fn build_with(
1606        self,
1607        registry: prebindgen_registry::RegistryBuilder<()>,
1608    ) -> Result<Cbindgen, prebindgen_registry::WriteRustError> {
1609        let registry = self
1610            .declare_into(registry)?
1611            .validate_with(&self)?
1612            .convert_with(|crossing, built, emit| self.convert_crossing(crossing, built, emit))?
1613            .build()?;
1614        self.validate_resolved(&registry)
1615            .map_err(|message| prebindgen_registry::ScanError::AdapterInvariant { message })?;
1616        Ok(Cbindgen {
1617            gen: self,
1618            registry,
1619        })
1620    }
1621
1622    /// Build the conversion for one crossing — see `JniGenBuilder::convert_crossing`.
1623    fn convert_crossing(
1624        &self,
1625        crossing: &Crossing,
1626        built: &Building<'_, ()>,
1627        emit: &prebindgen_registry::Emit,
1628    ) -> Option<ConverterImpl<()>> {
1629        let (dir, key) = crossing;
1630        // The reading the scan already took for this crossing, fetched by the
1631        // key the crossing IS — the same migration the JNI adapter's twin made in #284,
1632        // in place of `key -> to_type() -> spelling` (#291). Every crossing
1633        // `convert_with` hands out comes from a type table, so it has a cell.
1634        // The selectors take the reading now, so nothing here spells it.
1635        let ty = built.reading(key)?;
1636        match dir {
1637            Direction::Input => self.select_input_type(&ty, built, emit).or_else(|| {
1638                // The callback's arguments off the model's own `Callback` kind,
1639                // where `extract_fn_trait_args` re-read the parameter's bounds.
1640                let args = ty.callback_args()?;
1641                self.dispatch_fn_input(args, built)
1642            }),
1643            Direction::Output => self.select_output_type(&ty, built, emit),
1644        }
1645    }
1646
1647    pub fn declare_into(
1648        &self,
1649        mut registry: RegistryBuilder<()>,
1650    ) -> Result<RegistryBuilder<()>, prebindgen_registry::ScanError> {
1651        for (item_fn, origin) in self.collect_local_functions() {
1652            registry = registry.local_function(item_fn, origin)?;
1653        }
1654        for ident in self.declared_functions() {
1655            registry = registry.export(&ident);
1656        }
1657        for ident in self.helper_functions() {
1658            registry = registry.reference(&ident);
1659        }
1660        for ty in self.declared_types().into_values() {
1661            registry = registry.export_type(ty);
1662        }
1663        Ok(registry)
1664    }
1665}
1666
1667impl CbindgenBuilder {
1668    fn dispatch_fn_input(
1669        &self,
1670        args: &[TypeRef],
1671        registry: &impl Conversions<()>,
1672    ) -> Option<ConverterImpl<()>> {
1673        let key: CallbackKey = args.iter().map(|a| a.key()).collect();
1674        if !self.callbacks.contains_key(&key) {
1675            // Undeclared callback signature: leave unresolved so the registry
1676            // reports it (the consumer must `.callback(...)`-declare it).
1677            return None;
1678        }
1679        let c_struct = self.callback_c_ident(&key);
1680
1681        // Per-arg: closure parameter (`__aN: <src>`) + encode statement
1682        // (`let __wN = <output_conv>(__aN);`, panicking if the converter is
1683        // fallible — a firing callback has no error channel). A non-takeable arg
1684        // is passed to the C `call` by value (the C side owns + drops it); a
1685        // **takeable** arg is passed as `&mut __wN` (`*mut z_x_t`) and dropped here
1686        // after the call (no-op if the C side took it, leaving a gravestone).
1687        let takeable = &self.callbacks.get(&key).expect("callback cfg").takeable;
1688        let mut closure_params: Vec<TokenStream> = Vec::new();
1689        let mut encode_stmts: Vec<TokenStream> = Vec::new();
1690        let mut call_args: Vec<TokenStream> = Vec::new();
1691        let mut post_drops: Vec<TokenStream> = Vec::new();
1692        for (i, arg) in args.iter().enumerate() {
1693            // `&[E]` slice arg: deliver the slice to the C `call` **by reference** —
1694            // `(*const E_wire, size_t)`, zero-copy (the closure borrows the slice for
1695            // the call). The element wire is layout-identical to `E`, so the pointer
1696            // cast is sound; no per-element encode and no post-call drop.
1697            if let Some((src_elem, elem_wire)) = self.callback_slice_elem_wire_of(arg) {
1698                let ai = format_ident!("__a{}", i);
1699                closure_params.push(quote!(#ai: &[#src_elem]));
1700                call_args.push(quote!(#ai.as_ptr() as *const #elem_wire));
1701                call_args.push(quote!(#ai.len()));
1702                continue;
1703            }
1704            let entry = registry.output_entry(arg)?;
1705            let conv = entry.function.sig.ident.clone();
1706            let opaque = entry.destination.clone();
1707            let fallible = matches!(
1708                &entry.function.sig.output,
1709                syn::ReturnType::Type(_, ty) if is_result(ty)
1710            );
1711            let src = self.src_ty_deep_of(arg);
1712            let ai = format_ident!("__a{}", i);
1713            let wi = format_ident!("__w{}", i);
1714            closure_params.push(quote!(#ai: #src));
1715            let is_takeable = takeable.contains(&i);
1716            let mut_kw = if is_takeable { quote!(mut) } else { quote!() };
1717            if fallible {
1718                encode_stmts.push(quote!(
1719                    let #mut_kw #wi = match #conv(#ai) {
1720                        ::core::result::Result::Ok(__v) => __v,
1721                        ::core::result::Result::Err(__e) => {
1722                            ::core::panic!("cbindgen: callback argument conversion failed: {}", __e)
1723                        }
1724                    };
1725                ));
1726            } else {
1727                encode_stmts.push(quote!(let #mut_kw #wi = #conv(#ai);));
1728            }
1729            if is_takeable {
1730                call_args.push(quote!(&mut #wi as *mut #opaque));
1731                // Always drop after the call (leak-safe): live value if untaken,
1732                // gravestone (no-op) if the C side took it via `z_x_take`.
1733                post_drops.push(
1734                    quote!(let _ = <#opaque as ::prebindgen_c_runtime::Transmute>::into_rust(#wi);),
1735                );
1736            } else {
1737                call_args.push(quote!(#wi));
1738            }
1739        }
1740
1741        let fn_ty = callback_fn_type(
1742            &args
1743                .iter()
1744                .map(|a| self.src_ty_deep_of(a))
1745                .collect::<Vec<_>>(),
1746        );
1747        let name = format_ident!("__cbg_in_{}", self.callback_c_name(&key));
1748        let function: syn::ItemFn = syn::parse_quote!(
1749            #[allow(non_snake_case, unused_variables, dead_code)]
1750            pub(crate) unsafe fn #name(c: #c_struct) -> #fn_ty {
1751                struct __Ctx {
1752                    context: *mut ::core::ffi::c_void,
1753                    drop: ::core::option::Option<unsafe extern "C" fn(*mut ::core::ffi::c_void)>,
1754                }
1755                unsafe impl ::core::marker::Send for __Ctx {}
1756                unsafe impl ::core::marker::Sync for __Ctx {}
1757                impl ::core::ops::Drop for __Ctx {
1758                    fn drop(&mut self) {
1759                        if let ::core::option::Option::Some(__d) = self.drop {
1760                            unsafe { __d(self.context) }
1761                        }
1762                    }
1763                }
1764                let __call = c.call;
1765                let __ctx = ::std::sync::Arc::new(__Ctx { context: c.context, drop: c.drop });
1766                move |#(#closure_params),*| {
1767                    #(#encode_stmts)*
1768                    if let ::core::option::Option::Some(__f) = __call {
1769                        unsafe { __f(#(#call_args,)* __ctx.context) }
1770                    }
1771                    #(#post_drops)*
1772                }
1773            }
1774        );
1775        Some(ConverterImpl {
1776            subs: vec![],
1777            destination: syn::parse_quote!(#c_struct),
1778            function,
1779            pre_stages: vec![],
1780            niches: Niches::empty(),
1781            metadata: (),
1782        })
1783    }
1784}
1785
1786impl Prebindgen for CbindgenBuilder {
1787    /// Report what this binding left unclaimed. Here because it is the
1788    /// earliest generator-owned hook that sees the model, and it runs exactly
1789    /// where the registry used to print these itself. Moves into
1790    /// `CbindgenBuilder::generate` once that exists (prebindgen#251 phase E).
1791    ///
1792    /// `consts: None` — cbindgen has no const declaration mechanism, so every
1793    /// captured const is re-emitted verbatim and none is ever a skip.
1794    fn validate(&self, binding: &Building<'_, Self::Metadata>) -> Result<(), String> {
1795        let mut functions = self.declared_functions();
1796        functions.extend(self.helper_functions());
1797        prebindgen_registry::warn_unclaimed(
1798            binding.flat(),
1799            &prebindgen_registry::Claimed {
1800                functions,
1801                // The report asks what was *claimed*, which is a set of
1802                // identities — the declarations' spellings are the scan's
1803                // business, not this one's.
1804                types: self.declared_types().into_keys().collect(),
1805                consts: None,
1806                ignored_functions: self.ignored_functions(),
1807                ignored_types: self.ignored_types(),
1808                ..Default::default()
1809            },
1810        );
1811        Ok(())
1812    }
1813
1814    type Metadata = ();
1815
1816    // Consts have no declaration mechanism here (`declared_consts` stays
1817    // `None`), so every indexed const re-emits through the default
1818    // `on_const` — a path-alias against this source module, keeping consts
1819    // with non-portable initializers valid in the generated file. (cbindgen
1820    // cannot evaluate a path initializer, so aliased consts don't surface
1821    // as `#define`s in the C header.)
1822    fn source_module(&self) -> Option<&syn::Path> {
1823        self.source_module.as_ref()
1824    }
1825
1826    // ── Structural type resolution ──────────────────────────────────────
1827    // The adapter peels `ty` itself: a rank-0 terminal category, else a
1828    // wrapper shape (`Option<_>`, `&`/`&mut`/`&[_]`/`&str`). See `in_wrappers`
1829    // / `out_wrappers`.
1830
1831    fn prerequisites(
1832        &self,
1833        registry: &Registry<()>,
1834        emit: &prebindgen_registry::Emit,
1835    ) -> Vec<syn::Item> {
1836        // C-string data memory (string returns + `String` fields of data structs)
1837        // is malloc'd raw and freed by the single universal `free_memory_function`.
1838        // Array returns (`Vec<T>`) also hand out a malloc'd block freed via the
1839        // same function (per element through the `z_free_array` macro), so the
1840        // allocator/freer prelude is needed for them too. Each section's emitter
1841        // lives in the `impl CbindgenBuilder` block above; order is significant.
1842        let produces_array = self.produces_array(registry);
1843        let mut items: Vec<syn::Item> = Vec::new();
1844        items.extend(self.prereq_alloc_free(registry, produces_array));
1845        items.extend(self.prereq_array_builder(produces_array));
1846        items.extend(self.prereq_opaque_handles(registry));
1847        items.extend(self.prereq_data_structs(registry));
1848        items.extend(self.prereq_value_opaque(registry));
1849        items.extend(self.prereq_enums(registry, emit));
1850        items.extend(self.prereq_tagged_unions(registry, emit));
1851        items.extend(self.prereq_callback_structs(registry));
1852        items.extend(self.prereq_domain_constants(registry));
1853        items
1854    }
1855
1856    // ── Item emission ──────────────────────────────────────────────────
1857
1858    fn on_function(
1859        &self,
1860        f: &prebindgen_registry::flat::Function,
1861        registry: &Registry<()>,
1862        emit: &prebindgen_registry::Emit,
1863    ) -> TokenStream {
1864        self.emit_function_wrapper(f, registry, emit)
1865    }
1866
1867    fn on_struct(
1868        &self,
1869        _s: &prebindgen_registry::flat::Struct,
1870        _registry: &Registry<()>,
1871        _emit: &prebindgen_registry::Emit,
1872    ) -> TokenStream {
1873        // The `#[repr(C)]` mirror + converters come from prerequisites /
1874        // on_output_type; the original (non-FFI-safe) struct is dropped.
1875        TokenStream::new()
1876    }
1877
1878    fn on_variant(
1879        &self,
1880        _v: &prebindgen_registry::flat::Variant,
1881        _registry: &Registry<()>,
1882        _emit: &prebindgen_registry::Emit,
1883    ) -> TokenStream {
1884        TokenStream::new()
1885    }
1886
1887    fn on_enum(
1888        &self,
1889        _e: &prebindgen_registry::flat::Enum,
1890        _registry: &Registry<()>,
1891        _emit: &prebindgen_registry::Emit,
1892    ) -> TokenStream {
1893        TokenStream::new()
1894    }
1895}
1896
1897/// Output-direction terminal categories — the rank-0 chain, now an inherent
1898/// helper called by [`CbindgenBuilder::select_output_type`].
1899impl CbindgenBuilder {
1900    pub(crate) fn out_terminal(
1901        &self,
1902        ty: &TypeRef,
1903        _r: &impl Conversions<()>,
1904        emit: &prebindgen_registry::Emit,
1905    ) -> Option<ConverterImpl<()>> {
1906        // Unit return: trivial converter so `()` (and `Result<(), _>`) resolves.
1907        // Never actually called — void-returning wrappers ignore it, and
1908        // `emit_fallible_wrapper` special-cases `Result<(), E>` to drop the
1909        // out-param entirely (it exists only to satisfy the resolver).
1910        if matches!(ty.kind(), TypeKind::Unit) {
1911            let function: syn::ItemFn = syn::parse_quote!(
1912                #[allow(non_snake_case, dead_code, unused_variables)]
1913                pub(crate) fn __cbg_out_unit(v: ()) {}
1914            );
1915            return Some(ConverterImpl {
1916                subs: vec![],
1917                destination: syn::parse_quote!(()),
1918                function,
1919                pre_stages: vec![],
1920                niches: Niches::empty(),
1921                metadata: (),
1922            });
1923        }
1924
1925        // `String` output: a `malloc`'d `char*` raw block freed via the
1926        // `free_memory_function`. A `String` explicitly declared `opaque_ptr`
1927        // (held by C as `string_t *`) opts out — the opaque-handle branch below
1928        // owns it then (mirroring the input side, where `in_opaque_handle` wins).
1929        if r_is_string(ty) && !self.opaque.contains_key(&ty.key()) {
1930            let name = Self::out_name_of(&ty.key());
1931            let function: syn::ItemFn = syn::parse_quote!(
1932                #[allow(non_snake_case, unused_variables, dead_code)]
1933                pub(crate) fn #name(v: ::std::string::String) -> *mut ::core::ffi::c_char {
1934                    __cbg_alloc_cstr(v)
1935                }
1936            );
1937            return Some(ConverterImpl {
1938                subs: vec![],
1939                destination: syn::parse_quote!(*mut ::core::ffi::c_char),
1940                function,
1941                pre_stages: vec![],
1942                niches: Niches::empty(),
1943                metadata: (),
1944            });
1945        }
1946
1947        // FFI-safe scalar (`bool`, integers, floats): identity pass-through.
1948        if r_is_scalar(ty) {
1949            let name = Self::out_name_of(&ty.key());
1950            let spelled = scalar_ty(ty)?;
1951            let function: syn::ItemFn = syn::parse_quote!(
1952                #[allow(non_snake_case, unused_variables, dead_code)]
1953                pub(crate) fn #name(v: #spelled) -> #spelled {
1954                    v
1955                }
1956            );
1957            return Some(ConverterImpl {
1958                subs: vec![],
1959                destination: spelled.clone(),
1960                function,
1961                pre_stages: vec![],
1962                niches: Niches::empty(),
1963                metadata: (),
1964            });
1965        }
1966
1967        let key = ty.key();
1968
1969        // Opaque handle output: `Box::into_raw` → the bare `*mut #c_struct` handle.
1970        if self.opaque.contains_key(&key) {
1971            let name = Self::out_name_of(&ty.key());
1972            let c_struct = self.c_type_ident(&ty.key());
1973            let src = self.src_ty_of(&ty.key());
1974            let function: syn::ItemFn = syn::parse_quote!(
1975                #[allow(non_snake_case, unused_variables, dead_code)]
1976                pub(crate) fn #name(v: #src) -> *mut #c_struct {
1977                    ::std::boxed::Box::into_raw(::std::boxed::Box::new(v)) as *mut #c_struct
1978                }
1979            );
1980            return Some(ConverterImpl {
1981                subs: vec![],
1982                destination: syn::parse_quote!(*mut #c_struct),
1983                function,
1984                pre_stages: vec![],
1985                niches: Niches::empty(),
1986                metadata: (),
1987            });
1988        }
1989
1990        // Opaque error output (e.g. `ZError`): not a by-value struct — marshal it
1991        // to a malloc'd `char*` message via the recorded accessor `fn(&E) ->
1992        // String`. The error out-param of a `Result<_, E>` wrapper is thus
1993        // `char **e`. Freed by the universal `free_memory_function`.
1994        if let Some(msg_fn) = self.opaque_errors.get(&key) {
1995            let name = Self::out_name_of(&ty.key());
1996            let src = self.src_ty_of(&ty.key());
1997            let msg_path = self.src_fn(msg_fn);
1998            let function: syn::ItemFn = syn::parse_quote!(
1999                #[allow(non_snake_case, unused_variables, dead_code)]
2000                pub(crate) fn #name(v: #src) -> *mut ::core::ffi::c_char {
2001                    __cbg_alloc_cstr(#msg_path(&v))
2002                }
2003            );
2004            return Some(ConverterImpl {
2005                subs: vec![],
2006                destination: syn::parse_quote!(*mut ::core::ffi::c_char),
2007                function,
2008                pre_stages: vec![],
2009                niches: Niches::empty(),
2010                metadata: (),
2011            });
2012        }
2013
2014        // Data struct output: encode each field into its C wire (`String` →
2015        // malloc'd `char*` raw block, freed by the `free_memory_function`).
2016        if self.data.contains_key(&key) {
2017            let fields = self.struct_fields(_r, &ty.key())?;
2018            let name = Self::out_name_of(&ty.key());
2019            let c_struct = self.c_type_ident(&ty.key());
2020            let src = self.src_ty_of(&ty.key());
2021            let mut inits: Vec<TokenStream> = Vec::new();
2022            let mut subs: Vec<TypeKey> = Vec::new();
2023            for (fname, fty) in &fields {
2024                if r_is_string(fty) {
2025                    inits.push(quote!(#fname: __cbg_alloc_cstr(v.#fname)));
2026                } else if self.tagged_unions.contains_key(&fty.key()) {
2027                    let conv = Self::out_name_of(&fty.key());
2028                    subs.push(fty.key());
2029                    inits.push(quote!(#fname: #conv(v.#fname)));
2030                } else if r_is_bool(fty) {
2031                    let wrap = bool_out_expr(quote!(v.#fname));
2032                    inits.push(quote!(#fname: #wrap));
2033                } else {
2034                    inits.push(quote!(#fname: v.#fname));
2035                }
2036            }
2037            let function: syn::ItemFn = syn::parse_quote!(
2038                #[allow(non_snake_case, unused_variables, dead_code)]
2039                pub(crate) fn #name(v: #src) -> #c_struct {
2040                    #c_struct { #(#inits),* }
2041                }
2042            );
2043            return Some(ConverterImpl {
2044                subs,
2045                destination: syn::parse_quote!(#c_struct),
2046                function,
2047                pre_stages: vec![],
2048                niches: Niches::empty(),
2049                metadata: (),
2050            });
2051        }
2052
2053        // Value-opaque output: move the Rust value's bytes into the opaque
2054        // counterpart, by value (no Box). Size/align equality is asserted at the
2055        // type's emission site (fail-closed).
2056        if let Some(opaque) = self.value_opaque_ty_of(&ty.key()) {
2057            let opaque = opaque.clone();
2058            let name = Self::out_name_of(&ty.key());
2059            let src = self.src_ty_of(&ty.key());
2060            let function: syn::ItemFn = syn::parse_quote!(
2061                #[allow(non_snake_case, unused_variables, dead_code)]
2062                pub(crate) fn #name(v: #src) -> #opaque {
2063                    <#opaque as ::prebindgen_c_runtime::Transmute>::from_rust(v)
2064                }
2065            );
2066            return Some(ConverterImpl {
2067                subs: vec![],
2068                destination: opaque,
2069                function,
2070                pre_stages: vec![],
2071                niches: Niches::empty(),
2072                metadata: (),
2073            });
2074        }
2075
2076        // Enum output: `match` the source enum to the C enum.
2077        if self.enums.contains_key(&key) {
2078            let e = unit_enum(_r, &ty.key())?;
2079            let name = Self::out_name_of(&ty.key());
2080            let cname = self.c_type_ident(&ty.key());
2081            let src = self.src_ty_of(&ty.key());
2082            let arms = e.values.iter().map(|v| {
2083                let id = &v.name;
2084                quote!(#src::#id => #cname::#id,)
2085            });
2086            let function: syn::ItemFn = syn::parse_quote!(
2087                #[allow(non_snake_case, unused_variables, dead_code)]
2088                pub(crate) fn #name(v: #src) -> #cname {
2089                    match v { #(#arms)* }
2090                }
2091            );
2092            return Some(ConverterImpl {
2093                subs: vec![],
2094                destination: syn::parse_quote!(#cname),
2095                function,
2096                pre_stages: vec![],
2097                niches: Niches::empty(),
2098                metadata: (),
2099            });
2100        }
2101
2102        // Tagged-union output: `match` the source enum to the C union,
2103        // converting each arm's payload.
2104        if let Some(c) = self.out_tagged_union(ty, _r, emit) {
2105            return Some(c);
2106        }
2107
2108        None
2109    }
2110}
2111
2112/// Structural wrapper-shape resolvers (the post-rank-machinery surface). Each
2113/// peels `ty`'s outermost layer and composes the inner's converter; `subs`
2114/// lists the immediate inner(s) it looked up.
2115impl CbindgenBuilder {
2116    /// `Option<X>` and reference (`&`/`&mut`/`&[E]`/`&str`) **input** shapes.
2117    pub(crate) fn in_wrappers(
2118        &self,
2119        ty: &TypeRef,
2120        r: &impl Conversions<()>,
2121    ) -> Option<ConverterImpl<()>> {
2122        // `Option<X>` input: a single nullable C param, NULL = `None`. The inner
2123        // `X` is reused wholesale (its own converter — e.g. an `&T` borrow — does
2124        // the non-null decode), so `Option<&ZConfig>` binds the *reference*
2125        // converter, never the owned one.
2126        if let Some(inner) = ty.optional_inner() {
2127            let entry = r.input_entry(inner)?;
2128            let inner_wire = entry.destination.clone();
2129            let inner_conv = entry.function.sig.ident.clone();
2130            let (inner_ok, fallible): (syn::Type, bool) = match &entry.function.sig.output {
2131                syn::ReturnType::Type(_, t) if is_result(t) => {
2132                    let (ok, _e) = result_parts(t).expect("is_result ⇒ result_parts");
2133                    (ok, true)
2134                }
2135                syn::ReturnType::Type(_, t) => ((**t).clone(), false),
2136                syn::ReturnType::Default => (syn::parse_quote!(()), false),
2137            };
2138            if let Some((slot, rest)) = entry.niches.clone().carve() {
2139                let pred = &slot.matches;
2140                let name = format_ident!("__cbg_in_option_{}", sanitize(&inner.key()));
2141                let function: syn::ItemFn = if fallible {
2142                    syn::parse_quote!(
2143                        #[allow(non_snake_case, unused_variables, dead_code)]
2144                        pub(crate) unsafe fn #name(
2145                            v: #inner_wire,
2146                        ) -> ::core::result::Result<
2147                            ::core::option::Option<#inner_ok>,
2148                            ::std::string::String
2149                        > {
2150                            if #pred {
2151                                ::core::result::Result::Ok(::core::option::Option::None)
2152                            } else {
2153                                #inner_conv(v).map(::core::option::Option::Some)
2154                            }
2155                        }
2156                    )
2157                } else {
2158                    syn::parse_quote!(
2159                        #[allow(non_snake_case, unused_variables, dead_code)]
2160                        pub(crate) unsafe fn #name(
2161                            v: #inner_wire,
2162                        ) -> ::core::option::Option<#inner_ok> {
2163                            if #pred {
2164                                ::core::option::Option::None
2165                            } else {
2166                                ::core::option::Option::Some(#inner_conv(v))
2167                            }
2168                        }
2169                    )
2170                };
2171                return Some(ConverterImpl {
2172                    subs: vec![inner.key()],
2173                    destination: inner_wire,
2174                    function,
2175                    pre_stages: vec![],
2176                    niches: rest,
2177                    metadata: (),
2178                });
2179            }
2180            let is_ptr = matches!(inner_wire, syn::Type::Ptr(_));
2181            let wire: syn::Type = if is_ptr {
2182                inner_wire.clone()
2183            } else {
2184                syn::parse_quote!(*const #inner_wire)
2185            };
2186            let read = if is_ptr { quote!(v) } else { quote!(*v) };
2187            let name = format_ident!("__cbg_in_option_{}", sanitize(&inner.key()));
2188            let lt: TokenStream = if inner.borrow_target().is_some() {
2189                quote!(<'a>)
2190            } else {
2191                quote!()
2192            };
2193            let function: syn::ItemFn = if fallible {
2194                syn::parse_quote!(
2195                    #[allow(non_snake_case, unused_variables, dead_code)]
2196                    pub(crate) unsafe fn #name #lt(
2197                        v: #wire,
2198                    ) -> ::core::result::Result<::core::option::Option<#inner_ok>, ::std::string::String> {
2199                        if v.is_null() {
2200                            return ::core::result::Result::Ok(::core::option::Option::None);
2201                        }
2202                        match #inner_conv(#read) {
2203                            ::core::result::Result::Ok(__x) => {
2204                                ::core::result::Result::Ok(::core::option::Option::Some(__x))
2205                            }
2206                            ::core::result::Result::Err(__e) => ::core::result::Result::Err(__e),
2207                        }
2208                    }
2209                )
2210            } else {
2211                syn::parse_quote!(
2212                    #[allow(non_snake_case, unused_variables, dead_code)]
2213                    pub(crate) unsafe fn #name #lt(
2214                        v: #wire,
2215                    ) -> ::core::option::Option<#inner_ok> {
2216                        if v.is_null() {
2217                            ::core::option::Option::None
2218                        } else {
2219                            ::core::option::Option::Some(#inner_conv(#read))
2220                        }
2221                    }
2222                )
2223            };
2224            return Some(ConverterImpl {
2225                subs: vec![inner.key()],
2226                destination: wire,
2227                function,
2228                pre_stages: vec![],
2229                niches: Niches::empty(),
2230                metadata: (),
2231            });
2232        }
2233
2234        // `mutable` off the `Ref` itself, NOT `is_exclusive_borrow`: that
2235        // reading deliberately answers `false` for `&mut MaybeUninit<_>` — an
2236        // out-param slot is not an exclusive borrow OF A VALUE — and these arms
2237        // ask the syntactic question, "did the source write `&mut`".
2238        let TypeKind::Ref {
2239            mutable: rf_mut,
2240            inner: rf_inner,
2241            ..
2242        } = ty.kind()
2243        else {
2244            return None;
2245        };
2246        // The borrow's target, as a reading — every use below is its identity
2247        // or its source path, both of which the model answers.
2248        let elem = rf_inner;
2249
2250        // `&[E]` slice: marker only — the two-param (`*const E_wire`, `usize`)
2251        // lowering is done structurally in `emit_inputs`. A scalar `E` crosses as
2252        // itself (`*const E`); a declared inline-opaque by-value `E` (e.g. a
2253        // `repr_c_struct`) crosses as `*const E_counterpart` reinterpreted to
2254        // `&[E]` zero-copy. `subs` marks `E`'s input required so its mirror /
2255        // prerequisites are emitted.
2256        if !*rf_mut {
2257            if let Some(e) = r_shared_slice_elem(ty) {
2258                // #170, the slice instance. The two-param lowering builds the
2259                // `&[E]` zero-copy from C's own block, so there is nowhere to
2260                // normalise the bytes: `&[bool]` would materialise every
2261                // element's restricted domain at once. `MaybeUninit<bool>` is
2262                // not a fix here — the callee wants `&[bool]`, and rebuilding
2263                // the block would silently drop the zero-copy contract this
2264                // path exists for. Rejected until a raw-wire lowering exists.
2265                if r_is_bool(e) {
2266                    panic!(
2267                        "Cbindgen: `&[bool]` cannot cross IN from C. A `bool` slice is \
2268                         reinterpreted zero-copy from the caller's block, so a byte outside \
2269                         `{{0, 1}}` would become a Rust `bool` with no chance to normalise it \
2270                         (#170). Take the flags as an integer slice, or wrap them in a declared \
2271                         `opaque_ptr` handle."
2272                    );
2273                }
2274                if let Some(e_ty) = scalar_ty(e) {
2275                    let name = format_ident!("__cbg_inmark_slice_{}", sanitize(&e.key()));
2276                    let function: syn::ItemFn = syn::parse_quote!(
2277                        #[allow(non_snake_case, dead_code, unused)]
2278                        pub(crate) fn #name() {}
2279                    );
2280                    return Some(ConverterImpl {
2281                        subs: vec![e.key()],
2282                        destination: syn::parse_quote!(*const #e_ty),
2283                        function,
2284                        pre_stages: vec![],
2285                        niches: Niches::empty(),
2286                        metadata: (),
2287                    });
2288                }
2289                if let Some(counterpart) = self.value_opaque_ty_of(&e.key()) {
2290                    let counterpart = counterpart.clone();
2291                    let name = format_ident!("__cbg_inmark_slice_{}", sanitize(&e.key()));
2292                    let function: syn::ItemFn = syn::parse_quote!(
2293                        #[allow(non_snake_case, dead_code, unused)]
2294                        pub(crate) fn #name() {}
2295                    );
2296                    return Some(ConverterImpl {
2297                        subs: vec![e.key()],
2298                        destination: syn::parse_quote!(*const #counterpart),
2299                        function,
2300                        pre_stages: vec![],
2301                        niches: Niches::empty(),
2302                        metadata: (),
2303                    });
2304                }
2305            }
2306        }
2307        // `&str`: borrow a UTF-8 C string directly from the caller.
2308        if !*rf_mut && r_is_str(rf_inner) {
2309            let name = Self::in_name_of(&ty.key());
2310            let function: syn::ItemFn = syn::parse_quote!(
2311                #[allow(non_snake_case, unused_variables, dead_code)]
2312                pub(crate) unsafe fn #name<'a>(
2313                    v: *const ::core::ffi::c_char,
2314                ) -> ::core::result::Result<&'a str, ::std::string::String> {
2315                    if v.is_null() {
2316                        return ::core::result::Result::Err(
2317                            ::std::string::String::from("null pointer passed for str argument"),
2318                        );
2319                    }
2320                    match ::std::ffi::CStr::from_ptr(v).to_str() {
2321                        ::core::result::Result::Ok(s) => ::core::result::Result::Ok(s),
2322                        ::core::result::Result::Err(_) => ::core::result::Result::Err(
2323                            ::std::string::String::from("invalid UTF-8 in str argument"),
2324                        ),
2325                    }
2326                }
2327            );
2328            return Some(ConverterImpl {
2329                subs: vec![elem.key()],
2330                destination: syn::parse_quote!(*const ::core::ffi::c_char),
2331                function,
2332                pre_stages: vec![],
2333                niches: Niches::empty(),
2334                metadata: (),
2335            });
2336        }
2337        // `&mut T` (mutable borrow). Three sub-cases, all wiring to a `*mut` of the
2338        // wire (the C memory IS the Rust value for a value-opaque mirror — asserted
2339        // layout-identical — so the cast is sound; `&mut` is a borrow, no gravestone).
2340        if *rf_mut {
2341            // `&mut MaybeUninit<X>` (X value-opaque): out-param into uninitialized
2342            // memory. Rust writes via the `MaybeUninit` (no drop of the garbage slot).
2343            // `TypeKind::Uninit` is the form `maybe_uninit_inner` matched by
2344            // reading a path's tail ident.
2345            if let prebindgen_registry::flat::TypeKind::Uninit(inner) = elem.kind() {
2346                let op = self.value_opaque_ty_of(&inner.key())?.clone();
2347                let name = Self::in_name_of(&ty.key());
2348                let src = self.src_ty_of(&inner.key());
2349                let short = type_short(&inner.key());
2350                let null_ptr_msg = format!("null {short} pointer");
2351                let function: syn::ItemFn = syn::parse_quote!(
2352                    #[allow(non_snake_case, unused_variables, dead_code)]
2353                    pub(crate) unsafe fn #name<'a>(
2354                        v: *mut #op,
2355                    ) -> ::core::result::Result<&'a mut ::core::mem::MaybeUninit<#src>, ::std::string::String> {
2356                        if v.is_null() {
2357                            return ::core::result::Result::Err(
2358                                ::std::string::String::from(#null_ptr_msg),
2359                            );
2360                        }
2361                        ::core::result::Result::Ok(&mut *(v as *mut ::core::mem::MaybeUninit<#src>))
2362                    }
2363                );
2364                return Some(ConverterImpl {
2365                    subs: vec![inner.key()],
2366                    destination: syn::parse_quote!(*mut #op),
2367                    function,
2368                    pre_stages: vec![],
2369                    niches: Niches::empty(),
2370                    metadata: (),
2371                });
2372            }
2373            // `&mut` opaque handle, or `&mut` value-opaque: both reinterpret the C
2374            // pointer as a mutable Rust reference. The wire is the handle's C struct
2375            // or the value-opaque mirror.
2376            let wire_ty: syn::Type = if self.opaque.contains_key(&elem.key()) {
2377                let c_struct = self.c_type_ident(&elem.key());
2378                syn::parse_quote!(#c_struct)
2379            } else {
2380                self.value_opaque_ty_of(&elem.key())?.clone()
2381            };
2382            let name = Self::in_name_of(&ty.key());
2383            let src = self.src_ty_of(&elem.key());
2384            let short = type_short(&elem.key());
2385            let null_ptr_msg = format!("null {short} pointer");
2386            let function: syn::ItemFn = syn::parse_quote!(
2387                #[allow(non_snake_case, unused_variables, dead_code)]
2388                pub(crate) unsafe fn #name<'a>(
2389                    v: *mut #wire_ty,
2390                ) -> ::core::result::Result<&'a mut #src, ::std::string::String> {
2391                    if v.is_null() {
2392                        return ::core::result::Result::Err(
2393                            ::std::string::String::from(#null_ptr_msg),
2394                        );
2395                    }
2396                    ::core::result::Result::Ok(&mut *(v as *mut #src))
2397                }
2398            );
2399            return Some(ConverterImpl {
2400                subs: vec![elem.key()],
2401                destination: syn::parse_quote!(*mut #wire_ty),
2402                function,
2403                pre_stages: vec![],
2404                niches: Niches::empty(),
2405                metadata: (),
2406            });
2407        }
2408        // `&T` (shared borrow) of an opaque handle or value-opaque type.
2409        let key1 = elem.key();
2410        let wire_ty: syn::Type = if self.opaque.contains_key(&key1) {
2411            let c_struct = self.c_type_ident(&elem.key());
2412            syn::parse_quote!(#c_struct)
2413        } else {
2414            self.value_opaque_ty_of(&elem.key())?.clone()
2415        };
2416        let name = Self::in_name_of(&ty.key());
2417        let src = self.src_ty_of(&elem.key());
2418        let short = type_short(&elem.key());
2419        let null_ptr_msg = format!("null {short} pointer");
2420        let function: syn::ItemFn = syn::parse_quote!(
2421            #[allow(non_snake_case, unused_variables, dead_code)]
2422            pub(crate) unsafe fn #name<'a>(
2423                v: *const #wire_ty,
2424            ) -> ::core::result::Result<&'a #src, ::std::string::String> {
2425                if v.is_null() {
2426                    return ::core::result::Result::Err(::std::string::String::from(#null_ptr_msg));
2427                }
2428                ::core::result::Result::Ok(&*(v as *const #src))
2429            }
2430        );
2431        Some(ConverterImpl {
2432            subs: vec![elem.key()],
2433            destination: syn::parse_quote!(*const #wire_ty),
2434            function,
2435            pre_stages: vec![],
2436            niches: Niches::empty(),
2437            metadata: (),
2438        })
2439    }
2440
2441    /// `Option<X>`/`Vec<X>`/`&T`/`Result<T,E>` **output** shapes. The composite
2442    /// markers (`Option`/`Vec`/`Result`) carry a `()` destination — the real
2443    /// lowering is structural in `emit_function_wrapper` — and exist only to
2444    /// resolve the entry and make the inner(s) required.
2445    pub(crate) fn out_wrappers(
2446        &self,
2447        ty: &TypeRef,
2448        r: &impl Conversions<()>,
2449    ) -> Option<ConverterImpl<()>> {
2450        // `Option<T>` / `Vec<T>` marker.
2451        if let Some(inner) = ty.optional_inner().or_else(|| ty.sequence_elem()) {
2452            r.output_entry(inner)?;
2453            let kind = if ty.optional_inner().is_some() {
2454                "option"
2455            } else {
2456                "vec"
2457            };
2458            let name = format_ident!("__cbg_outmark_{}_{}", kind, sanitize(&inner.key()));
2459            let function: syn::ItemFn = syn::parse_quote!(
2460                #[allow(non_snake_case, dead_code, unused)]
2461                pub(crate) fn #name() {}
2462            );
2463            return Some(ConverterImpl {
2464                subs: vec![inner.key()],
2465                destination: syn::parse_quote!(()),
2466                function,
2467                pre_stages: vec![],
2468                niches: Niches::empty(),
2469                metadata: (),
2470            });
2471        }
2472        // `Cow<'_, [T]>` marker. The actual C ABI shape is structural in
2473        // `lower_shape`/`encode_value`, like `Vec<T>`.
2474        if let Some(inner) = r_cow_slice_elem(ty) {
2475            r.output_entry(inner)?;
2476            let name = format_ident!("__cbg_outmark_cow_slice_{}", sanitize(&inner.key()));
2477            let function: syn::ItemFn = syn::parse_quote!(
2478                #[allow(non_snake_case, dead_code, unused)]
2479                pub(crate) fn #name() {}
2480            );
2481            return Some(ConverterImpl {
2482                subs: vec![inner.key()],
2483                destination: syn::parse_quote!(()),
2484                function,
2485                pre_stages: vec![],
2486                niches: Niches::empty(),
2487                metadata: (),
2488            });
2489        }
2490        // `&[E]` shared slice borrow (a callback argument): marker only — the real
2491        // two-component `(*const E_wire, size_t)` lowering of the closure `call`
2492        // param is structural in `prereq_callback_structs` / `dispatch_fn_input`.
2493        // `subs: [E]` forces E's output (its `payload_t` mirror / scalar) so the
2494        // closure wire element type exists; `destination` is unused for the slice
2495        // (the callback emitter reads the element wire directly).
2496        if let Some(elem) = self
2497            .r_value_opaque_slice_elem(ty)
2498            .or_else(|| r_scalar_slice_elem(ty))
2499        {
2500            r.output_entry(elem)?;
2501            let name = format_ident!("__cbg_outmark_slice_{}", sanitize(&elem.key()));
2502            let function: syn::ItemFn = syn::parse_quote!(
2503                #[allow(non_snake_case, dead_code, unused)]
2504                pub(crate) fn #name() {}
2505            );
2506            return Some(ConverterImpl {
2507                subs: vec![elem.key()],
2508                destination: syn::parse_quote!(()),
2509                function,
2510                pre_stages: vec![],
2511                niches: Niches::empty(),
2512                metadata: (),
2513            });
2514        }
2515        // `&T` shared borrow of an opaque/value-opaque type → non-owning `*const`.
2516        if let TypeKind::Ref { mutable, inner, .. } = ty.kind() {
2517            if !*mutable {
2518                let key = inner.key();
2519                let wire_ty: syn::Type = if self.opaque.contains_key(&key) {
2520                    let c_struct = self.c_type_ident(&key);
2521                    syn::parse_quote!(#c_struct)
2522                } else {
2523                    self.value_opaque_ty_of(&key)?.clone()
2524                };
2525                let src = self.src_ty_of(&key);
2526                let name = format_ident!("__cbg_out_ref_{}", sanitize(&key));
2527                let function: syn::ItemFn = syn::parse_quote!(
2528                    #[allow(non_snake_case, dead_code, unused)]
2529                    pub(crate) unsafe fn #name(v: &#src) -> *const #wire_ty {
2530                        v as *const #src as *const #wire_ty
2531                    }
2532                );
2533                return Some(ConverterImpl {
2534                    subs: vec![key],
2535                    destination: syn::parse_quote!(*const #wire_ty),
2536                    function,
2537                    pre_stages: vec![],
2538                    niches: Niches::empty(),
2539                    metadata: (),
2540                });
2541            }
2542            return None;
2543        }
2544        // `Result<T, E>` marker — real lowering (bool + out-param + error-param)
2545        // is in `on_function`.
2546        if ty.fallible_parts().is_some() {
2547            let (ok, err) = ty.fallible_parts()?;
2548            let name = format_ident!("__cbg_result_{}", sanitize(&ty.key()));
2549            let function: syn::ItemFn = syn::parse_quote!(
2550                #[allow(non_snake_case, dead_code, unused)]
2551                pub(crate) fn #name() {}
2552            );
2553            return Some(ConverterImpl {
2554                subs: vec![ok.key(), err.key()],
2555                destination: syn::parse_quote!(()),
2556                function,
2557                pre_stages: vec![],
2558                niches: Niches::empty(),
2559                metadata: (),
2560            });
2561        }
2562        None
2563    }
2564}
2565
2566/// The declaration surface, stated once.
2567///
2568/// These were trait methods the registry called back into the adapter from
2569/// inside `resolve`. They are the adapter's own business now, gathered into the
2570/// one value the registry is constructed from.
2571impl CbindgenBuilder {
2572    pub(crate) fn declared_functions(&self) -> HashSet<syn::Ident> {
2573        self.functions.keys().cloned().collect()
2574    }
2575    pub(crate) fn ignored_functions(&self) -> HashSet<syn::Ident> {
2576        self.ignored_functions.clone()
2577    }
2578    pub(crate) fn helper_functions(&self) -> HashSet<syn::Ident> {
2579        self.convert_decls
2580            .iter()
2581            .flat_map(|decl| decl.input_spec().iter().chain(decl.output_spec().iter()))
2582            .filter_map(|spec| match spec {
2583                ConvertSpec::PrebindgenFn(ident) => Some(ident.clone()),
2584                ConvertSpec::Trait { .. } => None,
2585            })
2586            .filter(|ident| !self.functions.contains_key(ident))
2587            .collect()
2588    }
2589    /// Each with the spelling its declarator was written with — the scan needs
2590    /// real tokens to intern a type that is in no table yet (#291).
2591    pub(crate) fn declared_types(&self) -> HashMap<TypeKey, Origin<syn::Type>> {
2592        self.opaque
2593            .iter()
2594            .chain(self.data.iter())
2595            .map(|(k, c)| (k, &c.rust_type))
2596            .chain(self.value_opaque.iter().map(|(k, c)| (k, &c.cfg.rust_type)))
2597            .chain(
2598                self.enums
2599                    .iter()
2600                    .chain(self.tagged_unions.iter())
2601                    .map(|(k, c)| (k, &c.rust_type)),
2602            )
2603            .map(|(k, t)| (k.clone(), t.clone()))
2604            .collect()
2605    }
2606    pub(crate) fn ignored_types(&self) -> HashSet<TypeKey> {
2607        self.ignored_types.clone()
2608    }
2609}