prebindgen_c/lib.rs
1//! `CbindgenBuilder` — the C / cbindgen language adapter.
2//!
3//! # Experimental API
4//!
5//! This module is a proof of concept. Its Rust builder API may change in a
6//! minor release; do not rely on it as part of the stable 0.5 API.
7//!
8//! A [`Prebindgen`] back-end that turns a "flat" `#[prebindgen]` library into a
9//! Rust file suitable for [`cbindgen`](https://github.com/mozilla/cbindgen) to
10//! parse into a C header plus a static / dynamic library.
11//!
12//! Items are **opt-in**: nothing is converted unless it is explicitly declared
13//! with [`CbindgenBuilder::function`] / [`CbindgenBuilder::opaque_ptr`] /
14//! [`CbindgenBuilder::data_struct`] / [`CbindgenBuilder::enum_type`] /
15//! [`CbindgenBuilder::tagged_union`]. The C name of a declared
16//! type's generated destructor can be pinned by chaining [`CbindgenBuilder::base_name`].
17//!
18//! ## C ABI conventions
19//!
20//! * **Pointer struct** (declared with [`CbindgenBuilder::opaque_ptr`]): a `Box`-owned
21//! Rust value whose lifecycle is owned by the C side. The C type `T` is
22//! **opaque/incomplete** and the handle is a bare `T *` = `Box::into_raw`. A
23//! typed `<name>_drop(T *)` destructor (running the Rust `Drop`) is generated
24//! per handle.
25//! * **Data struct** (declared with [`CbindgenBuilder::data_struct`]): a by-value
26//! `#[repr(C)]` struct whose fields are mapped to C-ABI wire types
27//! (`String` → `*mut c_char`). No per-struct destructor — each `char*` field
28//! is released individually via the [`CbindgenBuilder::free_memory_function`].
29//! * **Enum type** (declared with [`CbindgenBuilder::enum_type`]): a fieldless enum,
30//! mirrored as a `#[repr(C)]` enum that cbindgen renders as the C enum.
31//! Rust → C hands over the mirror directly (Rust only ever builds declared
32//! variants). C → Rust must **not** do the reverse: a C `enum` is an `int` at
33//! the ABI, so materialising a caller-supplied discriminant as a Rust enum is
34//! undefined behaviour when it matches no variant — before any `match` could
35//! check it. An enum parameter is therefore taken as
36//! `MaybeUninit<mirror>` — the same ABI and the same C spelling (cbindgen
37//! renders `MaybeUninit<T>` as `T`), but legal to hold any bit pattern — and
38//! its raw `c_int` is validated against the mirror's variants before the Rust
39//! value is built. An unmatched value is a fallible-input error (see below),
40//! so a function taking an enum by value needs either a `Result` return or
41//! [`CbindgenBuilder::panic`]. This relies on cbindgen's C rendering; the `C++`
42//! language mode is not supported.
43//! * **Tagged union** (declared with [`CbindgenBuilder::tagged_union`]): a
44//! data-carrying enum crossing by value as a `#[repr(C)]` enum with payload
45//! variants, which cbindgen renders as a tag enum plus a `union` of the
46//! variant bodies. When any variant's payload wire owns memory, a typed
47//! `<name>_drop` frees the **active arm**. Inbound it obeys the same rule as
48//! a plain enum, one level up: the mirror arrives as `MaybeUninit<mirror>`,
49//! its leading `c_int` tag is range-checked against the variants, and only
50//! then is the value `assume_init`ed and matched. Every payload wire is
51//! bit-pattern-agnostic (a declared `enum_type` payload rides as
52//! `MaybeUninit` too, and is validated by its own converter), so the tag is
53//! the sole obligation. The typed drop checks it as well and treats an
54//! out-of-range one as nothing to release.
55//! * **Direct `String` output**: a bare `char *` — a `malloc`'d, null-terminated
56//! raw block (no wrapper struct), freed via the `free_memory_function`.
57//! * **[`CbindgenBuilder::free_memory_function`]**: the single, type-agnostic raw memory
58//! freer (C `free`) for every `char*` the layer hands out (string returns and
59//! data-struct `String` fields). It runs no destructor and needs no length.
60//! Required whenever such string memory is produced.
61//! * **`Result<T, E>` return** lowers by the success wire kind:
62//! - **pointer wire** (opaque handle, `char*`) → `T f(<inputs>, E *e)`, where a
63//! **NULL return signals error** (details written to `*e`);
64//! - **unit** → `bool f(<inputs>, E *e)`;
65//! - **value wire** (data struct, scalar, enum) → `bool f(T *out, <inputs>, E *e)`
66//! filling a caller-allocated `*out`.
67//!
68//! `e` may be `NULL`, in which case the error value is dropped. Infallible
69//! producers return the value/pointer directly (no out-param).
70//!
71//! ## Error handling (multiple error types)
72//!
73//! Any type used as the `E` of a `Result<T, E>` return **must be declared** as an
74//! error type via [`CbindgenBuilder::data_struct`] + [`CbindgenBuilder::error`] — otherwise the
75//! build fails. Error types are ordinary data structs (marshalled by value) and
76//! must additionally implement `From<String>`.
77//!
78//! Built-in input converters that can fail (a `String` arg, an opaque handle
79//! passed by value, a declared enum whose discriminant the caller chose) are
80//! **error-type-agnostic**: they return `Result<_, String>`
81//! where the `Err` is just a message. The generated wrapper for a `Result<T, E>`
82//! function converts such a message into *that function's* `E` via
83//! `<E as From<String>>::from(msg)`; the function's own `Err(E)` is marshalled
84//! directly through `E`'s output converter.
85//!
86//! If a function can produce such an internal message but does **not** return
87//! `Result`, that is a build error — suppress it by chaining [`CbindgenBuilder::panic`]
88//! after the function declaration, which makes the wrapper `panic!` on the
89//! internal error instead.
90//!
91//! References to the original Rust types in generated bodies are written
92//! fully-qualified against [`CbindgenBuilder::source_module`] so the generated file can
93//! define its own identically-named `#[repr(C)]` wrapper structs without
94//! colliding with the source crate's types.
95
96use std::collections::{HashMap, HashSet};
97
98// Shared `syn::Type` shape predicates live in `core::types_util`; re-exported
99// here under this back-end's historical names so the submodules (`use super::*`)
100// keep their call sites. `pub(crate) use` so the glob re-export reaches them.
101//
102// Down from seven: the shape questions this back-end asks are asked of a
103// `TypeRef` now, so what is left here serves the two node populations that
104// remain — a build-script declaration, and a converter's own generated
105// signature.
106pub(crate) use prebindgen_registry::types_util::{
107 is_result_type as is_result, path_tail_ident as type_path_tail, result_parts,
108};
109use prebindgen_registry::{
110 decl::{ConvertDecl, ConvertSpec},
111 flat::{extract_fn_trait_args, Field, Origin, ScalarKind, TypeKind, TypeRef},
112 Conversions, ConverterImpl, Direction, NicheSlot, Niches, Prebindgen, Registry, TypeKey,
113};
114use proc_macro2::TokenStream;
115use quote::{format_ident, quote, ToTokens};
116
117/// The origin of a type a **build script** wrote: real tokens, and deliberately
118/// no source position — `SourceLocation::default()` is the sanctioned placeless
119/// location for a type that was never in a captured file.
120fn declared_origin(ty: syn::Type) -> Origin<syn::Type> {
121 Origin::new(ty, std::rc::Rc::new(prebindgen::SourceLocation::default()))
122}
123
124/// Identity of a declared callback signature: its argument-type list (the
125/// dedup key, since two `impl Fn` params with the same args share one closure
126/// struct). The return is always unit for the supported callbacks.
127type CallbackKey = Vec<TypeKey>;
128
129/// Per-opaque-handle / per-data-struct / per-enum configuration.
130#[derive(Clone)]
131struct TypeCfg {
132 /// The type this declaration was **written with** — the `ty` handed to
133 /// `opaque_ptr` / `data_struct` / `enum_type` / `tagged_union`.
134 ///
135 /// A declarator receives a real `syn::Type` and used to keep only the key
136 /// derived from it, so later sites had to ask the key for the tokens back.
137 /// The declaration is where the type came from, and this is where it stays
138 /// (#291).
139 rust_type: Origin<syn::Type>,
140 /// Per-declaration **base** token override, fed to the name manglers
141 /// (`mangle_type_name` / `mangle_destructor` / `mangle_take`) in place of the
142 /// `mangle_rust_type`-derived base. Set by [`CbindgenBuilder::base_name`]. `None` ⇒
143 /// the base comes from `mangle_rust_type(short)` (or the short name).
144 base: Option<String>,
145}
146
147impl TypeCfg {
148 /// A freshly declared type, no naming override yet.
149 fn new(rust_type: syn::Type) -> Self {
150 Self {
151 rust_type: declared_origin(rust_type),
152 base: None,
153 }
154 }
155}
156
157/// What an inline-opaque by-value type holds, which decides whether its consume
158/// path needs a gravestone write-back (and thus a `prebindgen_c_runtime::Gravestone`
159/// impl). See [`CbindgenBuilder::opaque_data_struct`] / [`CbindgenBuilder::opaque_owned_struct`].
160#[derive(Clone, Copy, PartialEq, Eq)]
161enum OpaqueKind {
162 /// **Plain data** — holds no external resource (typically `Copy`, e.g. a
163 /// timestamp). Drop is a no-op, so consuming (moving out) leaves the source's
164 /// bitwise duplicate harmlessly droppable: **no gravestone write-back, no
165 /// `Gravestone` impl required** (only the autogenerated `Transmute`).
166 Data,
167 /// **Owns external data** — refcounts / heap (e.g. a byte buffer, a sample).
168 /// Consuming must write a `prebindgen_c_runtime::Gravestone` back over the moved-from
169 /// source so a later drop is a no-op (double-free safe). Requires the consumer
170 /// to implement `Gravestone` for the opaque counterpart (its *logic* only).
171 Owned,
172}
173
174/// Per-inline-opaque configuration: the opaque `#[repr(C, align(_))]` counterpart
175/// type the Rust value is transmuted to/from, whether it owns external data, plus
176/// the usual name config.
177#[derive(Clone)]
178struct ValueOpaqueCfg {
179 /// The opaque counterpart type (defined elsewhere — e.g. by a size/align
180 /// probe generator). Used verbatim as the by-value wire type. Must have
181 /// identical size+align to the Rust type (a `const _` assert is emitted to
182 /// enforce that, fail-closed) and — for [`OpaqueKind::Owned`] — implement
183 /// `prebindgen_c_runtime::Gravestone`.
184 opaque: syn::Type,
185 /// Plain-data vs owns-external-data (gravestone write-back on consume).
186 kind: OpaqueKind,
187 /// When `true`, the `opaque` counterpart is **not** supplied externally but is
188 /// an auto-generated **visible-field** `#[repr(C)]` mirror of the source struct,
189 /// emitted by [`CbindgenBuilder::prereq_value_opaque`]. Set by
190 /// [`CbindgenBuilder::repr_c_struct`]; `false` for `opaque_data_struct` /
191 /// `opaque_owned_struct` (counterpart defined elsewhere).
192 generate_mirror: bool,
193 /// Opt-out of the restricted-validity field audit (#170 instance 3, #158
194 /// instance 3). Set by [`CbindgenBuilder::assume_c_field_validity`]. See
195 /// [`CbindgenBuilder::restricted_validity_field`] for what the audit rejects and
196 /// why the escape hatch exists.
197 assume_c_field_validity: bool,
198 /// Name config (`.base_name()` override; default naming via the manglers).
199 cfg: TypeCfg,
200}
201
202/// Per-declared-callback configuration.
203#[derive(Clone)]
204struct CbCfg {
205 /// The argument types this callback was declared with, in order.
206 ///
207 /// `CallbackKey` is a `Vec<TypeKey>` — a list of identities, which is what
208 /// the map is keyed by. Emission needs the argument *types*, and these are
209 /// the ones `extract_fn_trait_args` produced at declaration time (#291).
210 args: Vec<syn::Type>,
211 /// Per-declaration **base** token override fed to `mangle_callback` (as the
212 /// sole base, replacing the args' derived bases). Set by
213 /// [`CbindgenBuilder::base_name`]. `None` ⇒ bases come from the arguments.
214 base: Option<String>,
215 /// Argument indices delivered to the C `call` as a **takeable owned pointer**
216 /// (`*mut z_x_t`) instead of by value: the callee may take the value (move it
217 /// out via `z_x_take`, leaving a gravestone) or just read it, and the
218 /// trampoline drops it after the call (no-op if taken). Set by
219 /// [`CbindgenBuilder::takeable_param`]; each such arg type must be an inline-opaque
220 /// type ([`CbindgenBuilder::opaque_owned_struct`] / [`CbindgenBuilder::opaque_data_struct`]).
221 takeable: std::collections::BTreeSet<usize>,
222}
223
224impl CbCfg {
225 /// A freshly declared callback signature, no naming or takeable overrides yet.
226 fn new(args: Vec<syn::Type>) -> Self {
227 Self {
228 args,
229 base: None,
230 takeable: std::collections::BTreeSet::new(),
231 }
232 }
233}
234
235/// Per-declared-function configuration.
236#[derive(Clone, Default)]
237struct FnCfg {
238 /// Per-declaration **base** token override fed to `mangle_function` in place of
239 /// the Rust fn ident. Set by [`CbindgenBuilder::base_name`]. `None` ⇒ the fn ident.
240 base: Option<String>,
241 /// Allow the generated wrapper to `panic!` on an internal error message
242 /// (set by [`CbindgenBuilder::panic`]). Only meaningful for non-`Result` functions
243 /// that have a fallible input.
244 panic: bool,
245}
246
247/// The declaration a chained modifier ([`CbindgenBuilder::name`] / [`CbindgenBuilder::error`]
248/// / [`CbindgenBuilder::panic`]) applies to. Set by each declaration method, reset to
249/// `None` by root-level modifiers (e.g. [`CbindgenBuilder::source_module`]).
250#[derive(Clone)]
251enum CurrentDecl {
252 Ptr(TypeKey),
253 Data(TypeKey),
254 ValueOpaque(TypeKey),
255 Enum(TypeKey),
256 TaggedUnion(TypeKey),
257 Callback(CallbackKey),
258 Function(syn::Ident),
259 Convert(TypeKey),
260}
261
262/// Where a fallible input-decode failure is routed in a generated wrapper.
263#[allow(clippy::large_enum_variant)]
264enum ErrRoute<'a> {
265 /// `Result<T, E>` function: convert the message to `E`, write `*e`, and
266 /// return `fail_return` (`false` for a `bool`/out-param wrapper,
267 /// `::core::ptr::null_mut()` for a pointer-returning wrapper).
268 Result {
269 e_conv: &'a syn::Ident,
270 e_ty_src: syn::Type,
271 fail_return: TokenStream,
272 },
273 /// Non-`Result` function declared `.panic()`: abort via `panic!`.
274 Panic,
275}
276
277/// Emit the statements that report `__msg` — a `String` already in scope — per
278/// `route`, and leave the wrapper.
279///
280/// Shared by the per-input decode failure and by the alias preflight, so the
281/// two cannot drift on how a binding error reaches the caller.
282fn route_message(route: &ErrRoute<'_>) -> TokenStream {
283 match route {
284 ErrRoute::Result {
285 e_conv,
286 e_ty_src,
287 fail_return,
288 } => quote!(
289 if !e.is_null() {
290 *e = #e_conv(
291 <#e_ty_src as ::core::convert::From<::std::string::String>>::from(__msg),
292 );
293 }
294 return #fail_return;
295 ),
296 ErrRoute::Panic => quote!(panic!("{}", __msg);),
297 }
298}
299
300/// How a parameter uses the resource it names — the axis
301/// [`CbindgenBuilder::alias_preflight`] states its rule on.
302#[derive(Clone, Copy, PartialEq, Eq)]
303enum AliasAccess {
304 /// Taken by value: the callee owns it afterwards, and the C-side handle is
305 /// dead.
306 Consume,
307 /// `&mut T`: exclusive for the duration of the call.
308 Exclusive,
309 /// `&T`: shared, and the only access that may legally coexist with another
310 /// of its own kind.
311 Shared,
312}
313
314impl AliasAccess {
315 /// The word used for this access in a preflight rejection message.
316 fn describe(self) -> &'static str {
317 match self {
318 AliasAccess::Consume => "consumed",
319 AliasAccess::Exclusive => "exclusively borrowed",
320 AliasAccess::Shared => "borrowed",
321 }
322 }
323}
324
325/// C / cbindgen language adapter. Build it with [`CbindgenBuilder::new`], declare the
326/// items to convert with the fluent methods, then drive it through
327/// [`CbindgenBuilder::build`] → [`Cbindgen::write_rust`].
328///
329/// A resolved C binding: every crossing has a conversion, and the header-facing
330/// Rust file can be written.
331///
332/// Built by [`CbindgenBuilder::build`]. Read-only, so `write_rust` is a pure
333/// emission over a complete registry.
334pub struct Cbindgen {
335 pub(crate) gen: CbindgenBuilder,
336 pub(crate) registry: prebindgen_registry::Registry<()>,
337}
338
339// Opaque — exists so `Result<Cbindgen, _>::expect_err` works in tests.
340impl std::fmt::Debug for Cbindgen {
341 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 f.write_str("Cbindgen(..)")
343 }
344}
345
346impl Cbindgen {
347 /// Describe a C binding.
348 pub fn builder() -> CbindgenBuilder {
349 CbindgenBuilder::new()
350 }
351
352 /// Write the generated Rust file — the `extern "C"` wrappers and their
353 /// converters, which `cbindgen` then reads to emit the header.
354 pub fn write_rust(
355 &self,
356 out_path: impl AsRef<std::path::Path>,
357 ) -> Result<std::path::PathBuf, prebindgen_registry::WriteRustError> {
358 Ok(prebindgen_registry::write::write_rust(
359 &self.registry,
360 &self.gen,
361 out_path,
362 )?)
363 }
364
365 /// The resolved registry — conversions, decompositions, and the model.
366 pub fn registry(&self) -> &prebindgen_registry::Registry<()> {
367 &self.registry
368 }
369
370 /// What the binding declared.
371 pub fn declarations(&self) -> &CbindgenBuilder {
372 &self.gen
373 }
374}
375
376#[derive(Default)]
377pub struct CbindgenBuilder {
378 /// Module path the original `#[prebindgen]` items live under. Used to
379 /// fully-qualify bare references to source types in generated bodies.
380 source_module: Option<syn::Path>,
381 /// `#[prebindgen]` functions explicitly declared for conversion.
382 functions: HashMap<syn::Ident, FnCfg>,
383 /// Canonical scalar conversions shared with JniGenBuilder.
384 convert_decls: Vec<ConvertDecl>,
385 /// Per-conversion C naming base used for generated niche constants.
386 convert_bases: HashMap<TypeKey, String>,
387 /// `#[prebindgen]` functions intentionally not exported by this adapter.
388 ignored_functions: HashSet<syn::Ident>,
389 /// Opaque-handle types (`Box` + `void*` lifecycle, auto `_drop`).
390 opaque: HashMap<TypeKey, TypeCfg>,
391 /// By-value `#[repr(C)]` data structs.
392 data: HashMap<TypeKey, TypeCfg>,
393 /// Inline-opaque by-value types: the Rust value is transmuted to/from an
394 /// opaque `#[repr(C, align(_))]` counterpart of identical size+align (no
395 /// `Box`). Keyed by the Rust type; the value carries the opaque counterpart.
396 value_opaque: HashMap<TypeKey, ValueOpaqueCfg>,
397 /// Enum types (unit-variant only — a C `enum` is a bare discriminant).
398 enums: HashMap<TypeKey, TypeCfg>,
399 /// Data-carrying enum types crossing by value as a `#[repr(C)]` enum with
400 /// payload variants, which cbindgen renders as the idiomatic C tag +
401 /// `union`. Declared with [`CbindgenBuilder::tagged_union`].
402 tagged_unions: HashMap<TypeKey, TypeCfg>,
403 /// Declared callback signatures (`impl Fn(...) + Send + Sync + 'static`),
404 /// keyed by their argument-type list. Each emits one `#[repr(C)]` closure
405 /// struct.
406 callbacks: HashMap<CallbackKey, CbCfg>,
407 /// Types intentionally not exported by this adapter.
408 ignored_types: HashSet<TypeKey>,
409 /// Data structs additionally marked as error types (allowlist for the
410 /// "Result error type must be declared" rule).
411 error: HashSet<TypeKey>,
412 /// Opaque error types (e.g. `ZError = Box<dyn Error>`) that are NOT by-value
413 /// data structs: they appear as the `E` of a `Result<_, E>` but are
414 /// marshalled to C as a `char*` message obtained by calling the recorded
415 /// accessor `fn(&E) -> String`. Keyed by the error type; the value is the
416 /// message-accessor function ident. Also inserted into [`Self::error`].
417 opaque_errors: HashMap<TypeKey, syn::Ident>,
418 /// Name of the universal raw-memory freer (C `free`) for `char*` data the
419 /// generated code hands out. Set by [`Self::free_memory_function`]. Required
420 /// (build error otherwise) whenever string memory is produced.
421 free_fn: Option<String>,
422 /// The declaration that chained modifiers apply to. Set by declaration
423 /// methods; reset to `None` by root-level modifiers.
424 current: Option<CurrentDecl>,
425 /// Optional name-mangling rules (all `None` ⇒ the built-in defaults below,
426 /// which carry no target-language convention). A per-declaration
427 /// [`.base_name()`](Self::base_name) replaces the *base* token fed to these
428 /// manglers. See [[the builder methods]](Self::mangle_rust_type).
429 ///
430 /// Base: Rust short name → canonical token, feeding the three type manglers.
431 mangle_rust_type: Option<Mangle1>,
432 /// Base → C type name (struct / enum / data).
433 mangle_type_name: Option<Mangle1>,
434 /// Base → opaque-handle destructor symbol.
435 mangle_destructor: Option<Mangle1>,
436 /// Base → value_opaque "take" (move) symbol, for takeable callback params.
437 mangle_take: Option<Mangle1>,
438 /// Callback arg bases → closure-struct name.
439 mangle_callback: Option<MangleN>,
440 /// Rust function ident → exported `#[no_mangle]` symbol.
441 mangle_function: Option<Mangle1>,
442 /// Where the `#[prebindgen]` items come from — see
443 /// `JniGenBuilder::source`.
444 pub(crate) sources: prebindgen_registry::flat::FlatBuilder,
445}
446
447/// A mangler over a single name component (Rust short name, base, or fn ident).
448type Mangle1 = Box<dyn Fn(&str) -> String>;
449/// A mangler over a callback's argument bases.
450type MangleN = Box<dyn Fn(&[String]) -> String>;
451
452mod builder;
453mod convert;
454mod emit;
455mod selector;
456#[cfg(test)]
457mod test_util;
458#[cfg(test)]
459mod tests;
460mod trait_impl;
461
462// ── Free helpers ───────────────────────────────────────────────────────
463
464/// Iterate a `TypeKey`-keyed map in deterministic (key-string) order.
465fn sorted_by_key(map: &HashMap<TypeKey, TypeCfg>) -> Vec<(&TypeKey, &TypeCfg)> {
466 let mut entries: Vec<(&TypeKey, &TypeCfg)> = map.iter().collect();
467 entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
468 entries
469}
470
471/// Turn a `TypeKey` into a valid ident fragment (non-alphanumerics → `_`).
472fn sanitize(key: &TypeKey) -> String {
473 key.as_str()
474 .chars()
475 .map(|c| if c.is_alphanumeric() { c } else { '_' })
476 .collect()
477}
478
479/// Last path-segment ident of a type as a `String` (e.g. `ZKeyExpr`).
480/// The short name a C symbol is built from — the key's last path segment, or a
481/// sanitized rendering of the whole key when it is not a path.
482///
483/// Off the **identity**: `type_path_tail` took the last segment of a node, and
484/// `TypeKey::short_name` is the same question asked of the canonical string, so
485/// the name no longer depends on holding the node it was derived from.
486fn type_short(key: &TypeKey) -> String {
487 key.short_name().unwrap_or_else(|| sanitize(key))
488}
489
490/// The declared **payload-carrying** enum under `key`, or a panic naming the
491/// right declarator when it is fieldless.
492///
493/// The mirror image of [`unit_enum`], and the same act: the model split the two
494/// enum shapes into two elements at parse time, so the lookup answers the shape
495/// question. This was `enum_item` + `assert_payload_enum`, the second running
496/// `enum_shape` over a `syn::ItemEnum` to re-derive what the first had thrown
497/// away.
498fn payload_enum<'r>(
499 registry: &'r impl Conversions<()>,
500 key: &TypeKey,
501) -> Option<&'r prebindgen_registry::flat::Variant> {
502 match registry.flat().declared_type(&key.ident()?)? {
503 prebindgen_registry::flat::Type::Variant(v) => Some(v),
504 prebindgen_registry::flat::Type::Enum(e) => panic!(
505 "Cbindgen: `{}` has no payload variants: declare it with `.enum_type()`, \
506 not `.tagged_union()` — a fieldless enum crosses as a plain C `enum`",
507 e.name
508 ),
509 _ => None,
510 }
511}
512
513/// Hard error when an `.enum_type()`-declared enum is not the shape that
514/// declarator describes. A plain C `enum` is exactly a discriminant, which
515/// is [`EnumShape::Unit`]; a data-carrying enum crosses as a tag plus a
516/// `union` and is reached through a different declarator, so this names
517/// that declarator rather than asserting on `syn::Fields`.
518/// The declared **fieldless** enum under `ty`'s name, or a panic naming the
519/// right declarator when it is a sum.
520///
521/// The lookup and the check are the same act: the model decided which of the
522/// two shapes an item is at parse time, and expresses it as two elements. This
523/// was `enum_item` + `assert_unit_enum`, the second running `enum_shape` over a
524/// `syn::ItemEnum` to re-derive what the first had already thrown away.
525fn unit_enum<'r>(
526 registry: &'r impl Conversions<()>,
527 key: &TypeKey,
528) -> Option<&'r prebindgen_registry::flat::Enum> {
529 match registry.flat().declared_type(&key.ident()?)? {
530 prebindgen_registry::flat::Type::Enum(e) => Some(e),
531 prebindgen_registry::flat::Type::Variant(v) => {
532 let offender = v
533 .alternatives
534 .iter()
535 .find(|a| !a.is_empty())
536 .map(|a| a.name.to_string())
537 .unwrap_or_default();
538 panic!(
539 "Cbindgen: `{}` is a data-carrying enum (variant `{offender}` has fields): \
540 declare it with `.tagged_union()`, not `.enum_type()` — a C `enum` is a bare \
541 discriminant and has no room for a payload",
542 v.name
543 )
544 }
545 _ => None,
546 }
547}
548
549/// PascalCase → snake_case (`ZKeyExpr` → `z_key_expr`).
550/// Convert a `PascalCase` / `camelCase` identifier to `snake_case` (a
551/// convention-free helper, re-exported for consumers composing their own
552/// [`CbindgenBuilder::mangle_rust_type`] rules).
553/// Thin alias for the core spelling, which sum-variant leaf naming shares.
554pub fn snake_case(s: &str) -> String {
555 prebindgen_registry::types_util::pascal_to_snake(s)
556}
557
558/// A reading spelled back as a `syn::Type`.
559///
560/// The source's **own tokens**, re-parsed — not [`TypeKind::to_syn`], which
561/// exists to check the lowering rather than to generate with. Every wire this
562/// back-end builds from a field's own type goes through here, so what C sees
563/// is what the source wrote.
564fn spelled(t: &TypeRef, emit: &prebindgen_registry::Emit) -> syn::Type {
565 let toks = emit.spell(t);
566 syn::parse_quote!(#toks)
567}
568
569/// `String`, off the classification.
570fn r_is_string(t: &TypeRef) -> bool {
571 matches!(t.kind(), TypeKind::String)
572}
573
574/// `str`, off the classification.
575fn r_is_str(t: &TypeRef) -> bool {
576 matches!(t.kind(), TypeKind::Str)
577}
578
579/// `bool`, off the classification — the one scalar with a restricted domain.
580fn r_is_bool(t: &TypeRef) -> bool {
581 matches!(t.kind(), TypeKind::Scalar(ScalarKind::Bool))
582}
583
584/// A scalar's Rust type, built from its **kind**.
585///
586/// A scalar's spelling is its name — `ScalarKind::as_str` is the closed set the
587/// source can have written — so this needs no captured syntax and no `Emit`.
588/// Three wire policies asked `spelled()` for exactly this behind an
589/// `r_is_scalar` guard, which was a source spelling standing in for an
590/// identity that could answer.
591fn scalar_ty(t: &TypeRef) -> Option<syn::Type> {
592 let TypeKind::Scalar(k) = t.kind() else {
593 return None;
594 };
595 let id = syn::Ident::new(k.as_str(), proc_macro2::Span::call_site());
596 Some(syn::parse_quote!(#id))
597}
598
599/// An FFI-safe scalar primitive, off the classification. `ScalarKind` IS the
600/// closed set the name table below was spelling out by hand.
601fn r_is_scalar(t: &TypeRef) -> bool {
602 matches!(t.kind(), TypeKind::Scalar(_))
603}
604
605/// `Vec<T>`, off the classification.
606fn r_is_vec(t: &TypeRef) -> bool {
607 matches!(t.kind(), TypeKind::Vec(_))
608}
609
610/// The opaque-pointer payload shape — `Box<T>` or `Option<Box<T>>` — off the
611/// classification, returning the reading of `T`.
612///
613/// The model peer of [`opaque_ptr_payload_inner`]: same shape question, asked
614/// of `TypeKind` instead of of a path's tail ident.
615fn r_boxed_inner(t: &TypeRef) -> Option<&TypeRef> {
616 let core = t.optional_inner().unwrap_or(t);
617 match core.kind() {
618 TypeKind::Boxed(inner) => Some(inner),
619 _ => None,
620 }
621}
622
623fn is_string(ty: &syn::Type) -> bool {
624 type_path_tail(ty).map(|i| i == "String").unwrap_or(false)
625}
626
627/// The C wire for a `bool` in any position C can write: `MaybeUninit<bool>`.
628///
629/// `bool` is the one FFI-safe scalar with a restricted domain — only `0` and
630/// `1` are valid — so a byte a C caller supplies may not be **held** in a Rust
631/// `bool` at all, let alone read from one. `MaybeUninit<bool>` holds any byte
632/// legally, has `bool`'s size and alignment (so a layout-preserving mirror
633/// still transmutes), and is invisible in the header: cbindgen simplifies
634/// `MaybeUninit<T>` to `T`, so the C prototype keeps saying `bool`.
635///
636/// The counterpart read is [`bool_in_expr`]. Together they are the single
637/// policy for #170; every position that lets C hand over a `bool` — a
638/// parameter, a `data_struct` field, a tagged-union payload — uses this pair
639/// and nothing else.
640fn bool_wire() -> syn::Type {
641 syn::parse_quote!(::core::mem::MaybeUninit<bool>)
642}
643
644/// Read a [`bool_wire`] slot the way C converts to `_Bool`: nonzero is true.
645///
646/// `access` must evaluate to a `MaybeUninit<bool>` place. The byte is read out
647/// as a `u8` — legal for any bit pattern — so no invalid `bool` ever exists.
648/// Unlike an enum discriminant there is nothing to reject: every byte has an
649/// unambiguous C meaning.
650///
651/// The read is `unsafe`; every caller emits it inside an `unsafe fn` body.
652fn bool_in_expr(access: TokenStream) -> TokenStream {
653 quote!(::core::ptr::read(#access.as_ptr() as *const u8) != 0)
654}
655
656/// Wrap a Rust `bool` for a [`bool_wire`] slot. Rust only ever writes `0`/`1`,
657/// so the outbound direction is a pure wrap.
658fn bool_out_expr(value: TokenStream) -> TokenStream {
659 quote!(::core::mem::MaybeUninit::new(#value))
660}
661
662/// Whether `ty` is an FFI-safe scalar primitive that passes through unchanged
663/// (`bool`, the fixed-width / pointer-width integers, and floats).
664fn is_scalar(ty: &syn::Type) -> bool {
665 type_path_tail(ty)
666 .map(|i| {
667 matches!(
668 i.to_string().as_str(),
669 "bool"
670 | "i8"
671 | "i16"
672 | "i32"
673 | "i64"
674 | "isize"
675 | "u8"
676 | "u16"
677 | "u32"
678 | "u64"
679 | "usize"
680 | "f32"
681 | "f64"
682 )
683 })
684 .unwrap_or(false)
685}
686
687/// The element of a shared slice borrow (`&[E]`), off the classification.
688fn r_shared_slice_elem(t: &TypeRef) -> Option<&TypeRef> {
689 let TypeKind::Ref {
690 mutable: false,
691 inner,
692 ..
693 } = t.kind()
694 else {
695 return None;
696 };
697 match inner.kind() {
698 TypeKind::Slice(e) => Some(e),
699 _ => None,
700 }
701}
702
703/// [`cow_slice_elem`] off the classification: `Cow<'_, [E]>` with scalar `E`.
704fn r_cow_slice_elem(t: &TypeRef) -> Option<&TypeRef> {
705 let TypeKind::Cow { inner, .. } = t.kind() else {
706 return None;
707 };
708 match inner.kind() {
709 TypeKind::Slice(e) if r_is_scalar(e) => Some(e),
710 _ => None,
711 }
712}
713
714/// [`scalar_slice_elem`] off the classification.
715fn r_scalar_slice_elem(t: &TypeRef) -> Option<&TypeRef> {
716 r_shared_slice_elem(t).filter(|e| r_is_scalar(e))
717}
718
719/// If `ty` is `&[E]` (a shared slice borrow) with scalar `E`, return `E`.
720fn scalar_slice_elem(ty: &syn::Type) -> Option<syn::Type> {
721 let syn::Type::Reference(r) = ty else {
722 return None;
723 };
724 if r.mutability.is_some() {
725 return None;
726 }
727 let syn::Type::Slice(s) = &*r.elem else {
728 return None;
729 };
730 let elem = (*s.elem).clone();
731 is_scalar(&elem).then_some(elem)
732}
733
734/// C name for an out-parameter field. When the value's primary field (suffix
735/// `""`) is itself an out-param the whole group is `out`-prefixed (`out`,
736/// `out_len`, `out_present`); otherwise the accompanying fields use bare names
737/// (`len`, `present`).
738fn out_param_name(suffix: &str, prefixed: bool) -> syn::Ident {
739 if prefixed {
740 format_ident!("out{}", suffix)
741 } else {
742 format_ident!("{}", suffix.trim_start_matches('_'))
743 }
744}
745
746/// NULL literal matching a raw-pointer wire: `null_mut()` for `*mut`, else `null()`.
747fn null_for(wire: &syn::Type) -> TokenStream {
748 match wire {
749 syn::Type::Ptr(p) if p.mutability.is_some() => quote!(::core::ptr::null_mut()),
750 _ => quote!(::core::ptr::null()),
751 }
752}
753
754/// One C-ABI wire component of a lowered return value. `suffix` names it
755/// relative to a base (`""` → `out`, `"_len"` → `len`, `"_present"` → `present`).
756struct WireField {
757 suffix: &'static str,
758 wire: syn::Type,
759}
760
761/// How a *present / ok* value of a return type is carried over the C ABI: an
762/// ordered list of wire components plus the representation niches still free
763/// for enclosing `Option`/`Result` layers.
764struct ValueShape {
765 fields: Vec<WireField>,
766 niches: Niches,
767}
768
769/// Whether a converter function's return type is `Result<_, _>` (⇒ fallible).
770fn returns_result(output: &syn::ReturnType) -> bool {
771 match output {
772 syn::ReturnType::Type(_, ty) => is_result(ty),
773 syn::ReturnType::Default => false,
774 }
775}
776
777fn route_result(call: TokenStream, route: &ErrRoute<'_>) -> TokenStream {
778 match route {
779 ErrRoute::Result {
780 e_conv,
781 e_ty_src,
782 fail_return,
783 } => quote! {
784 match #call {
785 ::core::result::Result::Ok(value) => value,
786 ::core::result::Result::Err(message) => {
787 if !e.is_null() {
788 *e = #e_conv(
789 <#e_ty_src as ::core::convert::From<
790 ::std::string::String
791 >>::from(message)
792 );
793 }
794 return #fail_return;
795 }
796 }
797 },
798 ErrRoute::Panic => quote! {
799 match #call {
800 ::core::result::Result::Ok(value) => value,
801 ::core::result::Result::Err(message) => panic!("{}", message),
802 }
803 },
804 }
805}