zerodds_lang_rules/adapter/c.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The C CST as the other end of a rule. Spec §3.2.
4//!
5//! The counterpart of [`crate::adapter::cst`], which does the same for
6//! Rust. Reading the two side by side is the point: where they differ,
7//! the difference is C's, and it is written down here rather than
8//! spread through a renderer.
9//!
10//! Values are resolved from the model, never from the overlay — the
11//! same rule the Rust materialiser follows and for the same reason
12//! (spec §2.3).
13//!
14//! ## The three things C forces that Rust does not
15//!
16//! 1. **A name for every type.** Rust writes `Vec<i32>` where a member
17//! needs it; C has no generics, so `sequence<long>` has to become a
18//! declared type with a name of its own. Those names are structural —
19//! `zd_seq_int32_t` — so two identical anonymous collections collapse
20//! onto one declaration instead of two that differ only in a counter.
21//! 2. **Declaration before use.** The Rust materialiser walks the model
22//! in declaration order and stops. Here the walk is a post-order over
23//! the type graph, because a C translation unit that names a struct
24//! before declaring it does not compile.
25//! 3. **No enumeration wide enough.** A C enumeration constant is an
26//! `int` (C11 §6.7.2.2), and `@bit_bound(32)` admits enumerator
27//! values up to `2^32 - 1`, which an `int` cannot hold. So an IDL
28//! enumeration becomes a fixed-width integer typedef plus one
29//! constant per enumerator, and the C `enum` is used only where every
30//! value fits. Emitting the `enum` unconditionally would make the
31//! holder width implementation-defined for exactly the cases the
32//! interop corpus tests.
33//!
34//! ## The memory shapes
35//!
36//! | IDL | C |
37//! |---|---|
38//! | `string` | `const char *` |
39//! | `wstring` | `const char *`, holding UTF-8 |
40//! | `fixed<P,S>` | `const char *`, holding the decimal |
41//! | `sequence<T>` | `struct { uint32_t _maximum; uint32_t _length; T *_buffer; bool _release; }` |
42//! | array | a typedef of the C array |
43//! | `map<K,V>` | `struct { uint32_t _maximum; uint32_t _length; Entry *_buffer; bool _release; }` of `struct { K key; V value; }`, named `[key][element]Map`, with the operations of Table 47 (see [`c_map`](super::c_map)) |
44//! | `union` | `struct { D _d; union { ... } _u; }` |
45//! | `@optional T m` | `T *m;`, NULL for an absent member (a `string` stays `const char *`) |
46//! | `@external T m` | `T *m;` (a `string` stays `const char *`), with the functions of [`c_lifecycle`](crate::adapter::c_lifecycle) |
47//!
48//! A `wstring` holds UTF-8 rather than `uint16_t` units on purpose: it
49//! puts the §7.4.1.1.2 conversion — the surrogate rule the byte count
50//! depends on — inside the C runtime, where it is a second
51//! implementation of the clause and not a copy of the Rust one's answer.
52//!
53//! ## Where the IDL spelling of a member is kept
54//!
55//! C has no keyword the generated identifier could keep, so
56//! [`c::escape_keyword`] moves a colliding name — `long static;` becomes
57//! `int32_t static_;`. That rename is not only a readability question:
58//! XTypes 1.3 §7.3.1.2.1.1 computes a member's ID under `@autoid(HASH)`
59//! and under `@hashid` from the first four octets of the MD5 of its
60//! **IDL** name, so a header that only knows `static_` cannot compute
61//! the ID for `static` and the member it writes lands under an ID the
62//! peer does not have.
63//!
64//! Every declaration that has named parts therefore carries them in one
65//! macro of its own, beside the declaration:
66//!
67//! ```c
68//! #define ZD_IDL_MEMBERS_package_Sample { { "impl", "impl" }, \
69//! { "static_", "static" } }
70//! ```
71//!
72//! and [`MEMBER_NAME`] is the record the entries fit, so a caller writes
73//!
74//! ```c
75//! static const zd_member_name names[] = ZD_IDL_MEMBERS_package_Sample;
76//! ```
77//!
78//! and has the mapping, in IDL declaration order — which is the order
79//! the member IDs are assigned in — with the C spelling on the left and
80//! the IDL spelling on the right.
81//!
82//! Three forms were measured against the flag set the probe compiles
83//! with (`-std=c99 -Wall -Wextra -Wpedantic -Wconversion
84//! -Wsign-conversion -Werror`); this one is what is left:
85//!
86//! - **A table as an object** — `static const zd_member_name
87//! zd_names_T[] = { … };` — is what C# and Python effectively have,
88//! and is the form this tree cannot write at all: [`c::Variable`]
89//! carries no array declarator, so adding one would be a change to
90//! `zerodds-lang-cst` made for this. The reason not to make it is the
91//! one the constants above are `#define`s for: the object is a
92//! separate object in every translation unit that includes the header.
93//! Whether an unused one is also diagnosed depends on the compiler —
94//! GCC documents `-Wunused-const-variable` as enabled by
95//! `-Wunused-variable` for C, and Apple clang 21 was silent about a
96//! header-provided table under the flag set above — so the diagnostic
97//! is a hazard here and not the measurement.
98//! - **An X-macro block** — `#define ZD_MEMBERS_T(X) X(0, static_,
99//! "static") …` — enumerates and costs nothing, and its replacement
100//! list is a run of juxtaposed invocations, which is not a C
101//! expression at all. It could only reach the output as raw text
102//! smuggled through an [`Ident`], in a tree whose whole point is that
103//! it is not text.
104//! - **One `#define` per member** — `#define ZD_IDL_NAME_T_static_
105//! "static"` — is expressible and costs nothing, and its name is
106//! ambiguous to anything reading the header: `ZD_IDL_NAME_a_b_c` does
107//! not say where the type name ends and the field name begins, and IDL
108//! scopes flatten with the same underscore.
109//!
110//! The braced initialiser is a C expression this tree already carries
111//! ([`c::Expr::Initializer`]), it puts both spellings of a member in one
112//! entry rather than in two lists that can disagree, it is enumerable —
113//! by the preprocessor for a C caller, by a line reader for a tool — and
114//! a macro that nothing expands occupies no storage and is not
115//! diagnosable. A second declaration reaching the same macro name is a
116//! redefinition and fails loudly, which is the property a silent
117//! collision would not have.
118
119use crate::adapter::constant;
120use crate::adapter::cst::{MaterialiseError, no_spelling_collision};
121use crate::apply::Applied;
122use crate::verbatim;
123use seesaw_tgg::ident::GhostId;
124use zerodds_ast::{Ast, CharWidth, PrimitiveKind, TypeKind, TypeNode};
125use zerodds_ast_core::{Id, NodeId};
126use zerodds_lang_cst::c;
127use zerodds_lang_cst::common::{Doc, Ident, Literal};
128
129/// The support typedefs every generated translation unit needs.
130///
131/// Emitted once, ahead of everything the model contributes: a `string`
132/// has the same C shape wherever it appears, and repeating the typedef
133/// per use would not compile.
134#[must_use]
135pub fn prelude() -> Vec<c::Decl> {
136 let mut out = vec![
137 c::Decl::Typedef(c::Typedef {
138 doc: Doc::lines(&[
139 "A `string`. The bytes are the caller's and are not owned here:",
140 "nothing in the write path allocates.",
141 ]),
142 type_: c::Type::Pointer(Box::new(c::Type::Const(Box::new(c::Type::Primitive(
143 c::Primitive::Char,
144 ))))),
145 name: Ident::new(STRING),
146 dims: Vec::new(),
147 }),
148 c::Decl::Typedef(c::Typedef {
149 doc: Doc::lines(&[
150 "A `wstring`, held as UTF-8. The runtime converts to UTF-16 on",
151 "the way out, so the surrogate rule of XTypes 1.3 7.4.1.1.2 is",
152 "implemented in C and not taken from the generator.",
153 ]),
154 type_: c::Type::Pointer(Box::new(c::Type::Const(Box::new(c::Type::Primitive(
155 c::Primitive::Char,
156 ))))),
157 name: Ident::new(WSTRING),
158 dims: Vec::new(),
159 }),
160 ];
161 // The record the `ZD_IDL_MEMBERS_…` macros are written to fit. A
162 // type and not an object: a `typedef` at file scope that nothing
163 // uses is not diagnosable, so every generated translation unit can
164 // carry the shape of the mapping whether or not it reads it.
165 out.extend(struct_typedef(
166 MEMBER_NAME,
167 vec![
168 c::Member {
169 doc: Doc::line("The identifier as this header spells it."),
170 type_: c::Type::Named(Ident::new(STRING)),
171 name: Ident::new("field"),
172 dims: Vec::new(),
173 bits: None,
174 },
175 c::Member {
176 doc: Doc::line("The identifier as the IDL source spelled it."),
177 type_: c::Type::Named(Ident::new(STRING)),
178 name: Ident::new("idl"),
179 dims: Vec::new(),
180 bits: None,
181 },
182 ],
183 Doc::lines(&[
184 "One member's two spellings.",
185 "",
186 "Load-bearing, not documentation. A member's ID under @autoid(HASH)",
187 "and under @hashid is the first four octets of the MD5 of its IDL",
188 "name (XTypes 1.3 7.3.1.2.1.1), so a caller holding only the C",
189 "spelling — which a keyword collision moved — cannot compute the ID",
190 "of the member it is writing.",
191 "",
192 "The entries come out of the `ZD_IDL_MEMBERS_<type>` macro beside",
193 "each declaration, in IDL declaration order:",
194 "",
195 " static const zd_member_name names[] = ZD_IDL_MEMBERS_a_Sample;",
196 ]),
197 ));
198 out
199}
200
201/// The C name of the `string` support type.
202pub const STRING: &str = "zd_string";
203/// The C name of the `wstring` support type.
204pub const WSTRING: &str = "zd_wstring";
205/// The C name of the record one entry of an IDL-name table fits.
206pub const MEMBER_NAME: &str = "zd_member_name";
207/// The prefix of the macro carrying a declaration's own IDL name.
208///
209/// The same escape that moves a member moves a type — `struct class`
210/// becomes `class_` — and a type's name reaches the wire twice over: as
211/// `TypeObject`'s `type_name` and as the topic type name a peer matches
212/// on. The C spelling cannot be inverted back to it, because
213/// [`c::escape_keyword`] leaves a name that was already spelled `class_`
214/// alone and both arrive at the same identifier.
215pub const IDL_NAME_PREFIX: &str = "ZD_IDL_NAME_";
216/// The prefix of the macro carrying a declaration's IDL member names.
217///
218/// The rest of the name is the declaration's own C name, which is
219/// already the flattened scoped name — so the macro is unique wherever
220/// the type is, and two that were not would be a redefinition the
221/// preprocessor reports rather than a mapping one of them lost.
222pub const IDL_MEMBERS_PREFIX: &str = "ZD_IDL_MEMBERS_";
223/// The C name of the `long double` support type.
224///
225/// The runtime's `zdw_f128` and never the compiler's `long double`: that
226/// type is x87 80-bit extended on x86-64, binary128 on aarch64 and a
227/// plain `double` on macOS arm64, so a generated structure declaring it
228/// would have a different wire format on each. `runtime/c/include/
229/// zerodds_wire.h` carries the measurements.
230pub const FLOAT128: &str = "zdw_f128";
231
232/// Build the C declarations a rule application produced.
233///
234/// # Errors
235/// [`MaterialiseError`] when a value the output has to state does not
236/// evaluate, or a type has no C shape — never a silently dropped
237/// declaration.
238pub fn c_items(ast: &Ast, applied: &Applied) -> Result<Vec<c::Decl>, MaterialiseError> {
239 let mut out = prelude();
240 // `@verbatim(placement = BEGIN_FILE)`, wherever it was written,
241 // opens the file — OMG IDL 4.2 §8.3.5.1. After the prelude, which
242 // is the earliest position this declaration list controls; the
243 // caller's own includes stand above the list either way.
244 let (begin_file, end_file) = verbatim::file_slots(ast, verbatim::aliases::C);
245 out.extend(begin_file.into_iter().map(c::Decl::Verbatim));
246 let order = ordered_types(ast);
247 no_bits_name_collision(ast, &order)?;
248 // Every named aggregate as an incomplete type first. The post-order
249 // walk puts a dependency before its user, except where the graph
250 // has a cycle — `struct A { sequence<B> bs; }; struct B {
251 // sequence<A> as; }` came out with `m_A *_buffer` above the
252 // `typedef` that names `m_A`, and the header did not compile. A
253 // pointer to an incomplete type is what C offers for exactly this,
254 // and a legal cycle always crosses one (a by-value cycle has no
255 // layout and is refused before it gets here). The duplicate
256 // typedefs the walk then produces are removed below, because
257 // `-std=c99` calls a typedef redefinition an error.
258 for id in &order {
259 let Ok(node) = ast.type_(*id) else { continue };
260 if matches!(
261 node.kind,
262 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } | TypeKind::Union { .. }
263 ) {
264 let name = c_type_name(ast, *id);
265 out.push(c::Decl::Typedef(c::Typedef {
266 doc: Doc::none(),
267 type_: c::Type::StructTag(Ident::new(&name)),
268 name: Ident::new(&name),
269 dims: Vec::new(),
270 }));
271 }
272 }
273 let mut emitted: Vec<String> = Vec::new();
274 // A map's entry struct embeds its key and value by value, and a
275 // value on a cycle (`struct Node { map<long, Node> children; }`)
276 // is incomplete at the map's position in the walk. Nothing holds
277 // an entry except the map's `_buffer` pointer, so every entry
278 // definition can wait until all named types are complete.
279 let mut deferred: Vec<c::Decl> = Vec::new();
280 let lifecycle_order = order.clone();
281 for id in order {
282 let start = out.len();
283 declare(ast, applied, id, &mut out, &mut deferred, &mut emitted)?;
284 weave_verbatim(ast, id, start, &mut out);
285 }
286 out.append(&mut deferred);
287 // The walk's own `typedef struct N N;` lines repeat the forwards;
288 // keep the first of each and drop the rest.
289 let mut seen_typedefs: Vec<String> = Vec::new();
290 out.retain(|decl| {
291 if let c::Decl::Typedef(typedef) = decl {
292 if let c::Type::StructTag(tag) = &typedef.type_ {
293 if tag.as_str() == typedef.name.as_str() {
294 let name = typedef.name.as_str().to_string();
295 if seen_typedefs.contains(&name) {
296 return false;
297 }
298 seen_typedefs.push(name);
299 }
300 }
301 }
302 true
303 });
304
305 // Constants, exceptions and natives are not types, so they are
306 // walked separately — the model keeps them in arenas of their own,
307 // and the Rust materialiser does the same.
308 for (id, node) in ast.constants() {
309 if ghost_created_for(applied, node.node_id, "CConst").is_none() {
310 continue;
311 }
312 let _ = id;
313 // `eval_const` and not `eval_integral`. Every constant IDL 4.2
314 // admits was asked the integral question here, so
315 // `const string MODULE_ID = "…";` — a constant that evaluates
316 // perfectly well and is not a number — came back as
317 // `Unevaluated` and was not emitted at all. The integral
318 // question is simply the wrong one for four of the eight forms.
319 let value = ast
320 .eval_const(node.expr)
321 .map_err(|_| MaterialiseError::Unevaluated {
322 construct: "constant's value".to_string(),
323 at: node.name.clone(),
324 })?;
325 // A `#define` and not a `static const`: a `static const` in a
326 // header is a separate object in every translation unit that
327 // includes it, and every one that does not use it warns.
328 let type_ = node
329 .type_
330 .resolved()
331 .map_or(c::Type::Primitive(c::Primitive::Int32), |target| {
332 c_value_type(ast, target)
333 });
334 let literal = c_constant_literal(&value, &type_, &node.name)?;
335 out.push(c::Decl::Define(c::Define {
336 doc: Doc::none(),
337 name: Ident::new(&c::escape_keyword(&node.name)),
338 value: Some(c::Expr::Paren(Box::new(c::Expr::Cast {
339 type_: Box::new(type_),
340 value: Box::new(c::Expr::Literal(literal)),
341 }))),
342 }));
343 }
344
345 for (id, node) in ast.exceptions() {
346 if ghost_created_for(applied, node.node_id, "CError").is_none() {
347 continue;
348 }
349 let _ = id;
350 let mut members = Vec::new();
351 let mut names = Vec::new();
352 for member in &node.members {
353 let Ok(member_node) = ast.member(*member) else {
354 continue;
355 };
356 let Some(target) = member_node.type_.resolved() else {
357 return Err(MaterialiseError::Unevaluated {
358 construct: "exception member type".to_string(),
359 at: member_node.name.clone(),
360 });
361 };
362 let field = c::escape_keyword(&member_node.name);
363 names.push((field.clone(), member_node.name.clone()));
364 members.push(c::Member {
365 doc: Doc::none(),
366 type_: c_value_type(ast, target),
367 name: Ident::new(&field),
368 dims: Vec::new(),
369 bits: None,
370 });
371 }
372 let exception_name = c::escape_keyword(&node.name);
373 out.extend(struct_typedef(
374 &exception_name,
375 members,
376 Doc::line("An exception, as the structure its members make."),
377 ));
378 out.extend(idl_members(&exception_name, &names)?);
379 out.push(idl_name(&exception_name, &node.name));
380 }
381
382 // The initialiser, destructor and copy functions of the aggregates
383 // that hold an external member (XTypes 1.3 §7.5.1.2.3.1), after every
384 // type they name and before the text that closes the file.
385 out.extend(super::c_lifecycle::lifecycle(ast, &lifecycle_order)?);
386
387 // The operations of Table 47 on every map, and the maps of every
388 // type (XTypes 1.3 §7.5.1.3.2), after every type they name.
389 out.extend(super::c_map::maps(ast, &lifecycle_order, &emitted)?);
390
391 // `@verbatim(placement = END_FILE)` closes the file, after even the
392 // constants and exceptions. OMG IDL 4.2 §8.3.5.1.
393 out.extend(end_file.into_iter().map(c::Decl::Verbatim));
394
395 Ok(out)
396}
397
398/// Weave one type's `@verbatim` text around what `declare` just pushed.
399/// OMG IDL 4.2 §8.3.5.1; the slot semantics are [`crate::verbatim`]'s.
400///
401/// `start` is where the type's declarations begin in `out`.
402/// `BEGIN_DECLARATION` and `END_DECLARATION` open and close the member
403/// list of the type's `struct`; an enumeration and a typedef have no body
404/// to hold raw text, so for them the text lands adjacent to the
405/// declaration — the nearest position the tree can express, never a
406/// dropped directive.
407fn weave_verbatim(ast: &Ast, id: Id<TypeNode>, start: usize, out: &mut Vec<c::Decl>) {
408 verbatim::weave(
409 verbatim::declaration_slots(ast, id, verbatim::aliases::C),
410 &c_type_name(ast, id),
411 start,
412 out,
413 );
414}
415
416impl verbatim::Carrier for c::Decl {
417 fn verbatim(text: String) -> Self {
418 Self::Verbatim(text)
419 }
420
421 fn take_into_body(
422 &mut self,
423 name: &str,
424 begin: &mut Vec<String>,
425 end: &mut Vec<String>,
426 ) -> bool {
427 let Self::Struct(node) = self else {
428 return false;
429 };
430 if node.name.as_ref().map(Ident::as_str) != Some(name) {
431 return false;
432 }
433 node.body_text.begin.append(begin);
434 node.body_text.end.append(end);
435 true
436 }
437}
438
439/// Every type in the model, dependencies first.
440///
441/// Post-order over the type graph. A type already visited is not
442/// revisited, which also means a cycle stops rather than recurses — a
443/// recursive IDL type has no C declaration order at all, and
444/// [`c_codec`](crate::encode_c::c_codec) is where that is reported.
445#[must_use]
446pub fn ordered_types(ast: &Ast) -> Vec<Id<TypeNode>> {
447 let all: Vec<Id<TypeNode>> = ast.types().map(|(id, _)| id).collect();
448 let order = ordered_types_from(ast, &all);
449 // The node every `any` points at stays in the model when the types
450 // that held one have been left out (`Ast::drop_outside_profile`).
451 // Nothing reaches it then, and a codec for it is a refusal of a
452 // construct no declaration uses. One that something still reaches is
453 // kept, so that the refusal by name stays where it is needed.
454 order
455 .iter()
456 .copied()
457 .filter(|id| {
458 let no_data_form = ast
459 .type_(*id)
460 .is_ok_and(|node| matches!(node.kind, TypeKind::Any | TypeKind::ValueBase));
461 !no_data_form
462 || order
463 .iter()
464 .any(|other| other != id && dependencies(ast, *other).contains(id))
465 })
466 .collect()
467}
468
469/// The same walk, from named roots instead of from the whole model.
470///
471/// What a translation unit for one topic type needs, and no more. The
472/// distinction matters in C and not in Rust: a `static` function nothing
473/// calls is a warning, so emitting a codec for a type the root cannot
474/// reach would make every generated file noisy — and a generator whose
475/// output warns trains its users to stop reading warnings.
476#[must_use]
477pub fn ordered_types_from(ast: &Ast, roots: &[Id<TypeNode>]) -> Vec<Id<TypeNode>> {
478 ordered_types_from_pruning(ast, roots, &[])
479}
480
481/// [`ordered_types_from`] that neither lists a type in `pruned` nor follows
482/// its dependencies: what a type reaches only through a pruned type is not
483/// needed, as the pruned type's code is written elsewhere.
484#[must_use]
485pub fn ordered_types_from_pruning(
486 ast: &Ast,
487 roots: &[Id<TypeNode>],
488 pruned: &[Id<TypeNode>],
489) -> Vec<Id<TypeNode>> {
490 let mut order = Vec::new();
491 let mut seen: Vec<Id<TypeNode>> = pruned.to_vec();
492 for root in roots {
493 visit(ast, *root, &mut order, &mut seen, 0);
494 }
495 order
496}
497
498/// zerodds-lint: recursion-depth 64 (dependency edges of the type
499/// graph; bounded by `MAX_DEPTH` and by `seen`, which admits each
500/// type once, so a cycle stops on the second visit)
501fn visit(
502 ast: &Ast,
503 id: Id<TypeNode>,
504 order: &mut Vec<Id<TypeNode>>,
505 seen: &mut Vec<Id<TypeNode>>,
506 depth: usize,
507) {
508 if depth > MAX_DEPTH || seen.contains(&id) {
509 return;
510 }
511 seen.push(id);
512 for dependency in dependencies(ast, id) {
513 visit(ast, dependency, order, seen, depth + 1);
514 }
515 order.push(id);
516}
517
518/// How deep the walk follows the type graph, matching the layout
519/// planner's own bound.
520pub(crate) const MAX_DEPTH: usize = 64;
521
522/// The types a type's C declaration has to be preceded by.
523///
524/// Also the types whose codec a type's codec calls, which is what
525/// [`crate::emit::encode::called_only_in_bulk`] reads it for.
526pub(crate) fn dependencies(ast: &Ast, id: Id<TypeNode>) -> Vec<Id<TypeNode>> {
527 let mut out = Vec::new();
528 let Ok(node) = ast.type_(id) else {
529 return out;
530 };
531 match &node.kind {
532 TypeKind::Sequence { element, .. } | TypeKind::Array { element, .. } => {
533 out.extend(element.resolved());
534 }
535 TypeKind::Map { key, value, .. } => {
536 out.extend(key.resolved());
537 out.extend(value.resolved());
538 }
539 TypeKind::Alias { aliased } => out.extend(aliased.resolved()),
540 TypeKind::ValueBox { boxed } => out.extend(boxed.resolved()),
541 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
542 // A base is a type the derived one is declared in terms of
543 // (XTypes 1.3 §7.5.1.4.1: a base class, or the member
544 // `parent`), so it comes first.
545 if let TypeKind::Structure {
546 base: Some(base), ..
547 } = &node.kind
548 {
549 out.extend(base.resolved());
550 }
551 for member in ast.all_members_of(id) {
552 if let Ok(member_node) = ast.member(member) {
553 out.extend(member_node.type_.resolved());
554 }
555 }
556 }
557 TypeKind::Union { discriminator, .. } => {
558 out.extend(discriminator.resolved());
559 for case in ast.cases_of(id) {
560 if let Ok(case_node) = ast.case(*case) {
561 if let Ok(member) = ast.member(case_node.member) {
562 out.extend(member.type_.resolved());
563 }
564 }
565 }
566 }
567 TypeKind::Primitive(_)
568 | TypeKind::String { .. }
569 | TypeKind::Fixed { .. }
570 | TypeKind::Enumeration { .. }
571 | TypeKind::Bitmask { .. }
572 | TypeKind::Bitset { .. }
573 | TypeKind::Native
574 | TypeKind::Any
575 | TypeKind::ValueBase
576 | TypeKind::ObjectRef { .. }
577 | TypeKind::AnnotationType { .. } => {}
578 }
579 out
580}
581
582/// The C spelling of a type in a position that has to hold a value,
583/// refusing the constructs C cannot hold.
584///
585/// `any` is the first of those. It is in the model because Building
586/// Block Any is one of IDL 4.2's fifteen, and what it holds is decided
587/// at run time — there is no C type for it and no wire form to generate
588/// a codec against. Emitting `any a;` into a header and reporting
589/// success produced a file no compiler reads, from a generator that said
590/// nothing was wrong.
591fn c_member_type(ast: &Ast, target: Id<TypeNode>, at: &str) -> Result<c::Type, MaterialiseError> {
592 let construct = match ast.type_(target).map(|node| &node.kind) {
593 Ok(TypeKind::Any) => Some("any"),
594 Ok(TypeKind::ValueBase) => Some("ValueBase"),
595 // An object reference is a handle into the RPC arc and
596 // `coverage.rs` says so; there is no C data type for it, and
597 // writing the IDL spelling into the header produced a file no
598 // compiler reads.
599 Ok(TypeKind::ObjectRef { .. }) => Some("Object"),
600 _ => None,
601 };
602 if let Some(construct) = construct {
603 return Err(MaterialiseError::NoLanguageForm {
604 construct: construct.to_string(),
605 language: "C",
606 at: at.to_string(),
607 });
608 }
609 Ok(c_value_type(ast, target))
610}
611
612/// The C storage of one member of `enclosing`, which is
613/// [`c_member_type`] except where the value has no room there.
614///
615/// A C structure holds its members as storage, so a member whose type
616/// reaches the structure that declares it back — over members, union
617/// branches, array elements and aliases, none of which is a pointer in
618/// C — is a structure that contains itself, and the compiler answers
619/// `field 'next' has incomplete type`. The pointer is what gives such a
620/// type a size, and it is what DDS-XTypes 1.3 Table 21 already calls the
621/// case: a member "stored externally to the containing type".
622///
623/// [`zerodds_ast_layout::member_held_inline_closes_a_cycle`] is the
624/// question, asked of the *pair* — a member's type alone does not answer
625/// it, because the same type is inline in one aggregate and an
626/// indirection in another. The cycle is one reason: `@optional Node next`
627/// without `@external` closes it. `@external` (§7.5.1.2.3.1) and
628/// `@optional` (§7.5.1.2.4.1) are the other, and each makes the member a
629/// pointer whether or not it closes a cycle: `@optional long a` is
630/// `int32_t *a`.
631///
632/// The codec answers the same question at the same seam —
633/// [`crate::emit::CodecTarget::behind_indirection`] — so what the header
634/// declares and what the encoder reaches through cannot part.
635fn c_held_member_type(
636 ast: &Ast,
637 enclosing: Id<TypeNode>,
638 member: &zerodds_ast::MemberNode,
639 target: Id<TypeNode>,
640 at: &str,
641) -> Result<c::Type, MaterialiseError> {
642 let held = c_member_type(ast, target, at)?;
643 if member_held_behind_pointer(ast, enclosing, member, target) {
644 return Ok(c::Type::Pointer(Box::new(held)));
645 }
646 Ok(held)
647}
648
649/// Whether the C value of a type is a pointer already: a `string`, a
650/// `wstring` and a `fixed`, which are `const char *`, and an alias of
651/// one. XTypes 1.3 §7.5.1.2.3.1: "String and wide string members are
652/// already represented using pointers, so the mappings for these members
653/// do not change. The same applies to aliases to string and wide string
654/// types."
655#[must_use]
656pub fn value_is_pointer(ast: &Ast, id: Id<TypeNode>) -> bool {
657 let mut current = id;
658 for _ in 0..MAX_DEPTH {
659 match ast.type_(current).map(|node| &node.kind) {
660 Ok(TypeKind::String { .. } | TypeKind::Fixed { .. }) => return true,
661 Ok(TypeKind::Alias { aliased }) => match aliased.resolved() {
662 Some(target) => current = target,
663 None => return false,
664 },
665 _ => return false,
666 }
667 }
668 false
669}
670
671/// Whether C holds a member behind a pointer, given whether the member
672/// is annotated `@external` or `@optional` (`annotated`, see
673/// [`pointer_annotated`]).
674///
675/// Two reasons, and either is enough. A member that closes a cycle has no
676/// other way to have a size (`c_held_member_type`). An `@external`
677/// member "shall be represented using pointers […] a member of type X
678/// shall instead be mapped as type pointer-to-X" (XTypes 1.3
679/// §7.5.1.2.3.1), unless the value is a pointer already
680/// ([`value_is_pointer`]); an `@optional` member is held the same way
681/// (§7.5.1.2.4.1: "Optional members shall be represented using
682/// pointers", "A NULL pointer shall indicate an omitted value"). One
683/// pointer for all of them: `@external Node next` inside `Node` is
684/// `Node *next`.
685#[must_use]
686pub fn pointer_member(
687 ast: &Ast,
688 enclosing: Id<TypeNode>,
689 target: Id<TypeNode>,
690 annotated: bool,
691) -> bool {
692 zerodds_ast_layout::member_held_inline_closes_a_cycle(ast, enclosing, target)
693 || (annotated && !value_is_pointer(ast, target))
694}
695
696/// Whether the annotations of `member` alone make C hold it behind a
697/// pointer: `@external` (XTypes 1.3 §7.5.1.2.3.1) or `@optional`
698/// (§7.5.1.2.4.1). The `external` argument of [`pointer_member`].
699#[must_use]
700pub fn pointer_annotated(member: &zerodds_ast::MemberNode) -> bool {
701 crate::adapter::annotated_external(member) || member.facets.is_optional()
702}
703
704/// [`pointer_member`] for the member of `enclosing` that is called
705/// `member_name`: a structure's member or a union's branch. For a caller
706/// that holds the name and the type and not the node, as the wire probe's
707/// generator does.
708///
709/// # Errors
710/// A message naming the aggregate and the name, when `enclosing` declares
711/// no such member: the answer would otherwise be the cycle rule's, for a
712/// member that may well be `@external`.
713pub fn named_member_held_behind_pointer(
714 ast: &Ast,
715 enclosing: Id<TypeNode>,
716 member_name: &str,
717 target: Id<TypeNode>,
718) -> Result<bool, String> {
719 let structure_member = ast
720 .all_members_of(enclosing)
721 .into_iter()
722 .filter_map(|member| ast.member(member).ok())
723 .find(|member| member.name == member_name);
724 let branch_member = ast
725 .cases_of(enclosing)
726 .iter()
727 .filter_map(|case| ast.case(*case).ok())
728 .filter_map(|case| ast.member(case.member).ok())
729 .find(|member| member.name == member_name);
730 let member = structure_member.or(branch_member).ok_or_else(|| {
731 format!(
732 "`{}` declares no member or branch `{member_name}`",
733 c_type_name(ast, enclosing)
734 )
735 })?;
736 Ok(pointer_member(
737 ast,
738 enclosing,
739 target,
740 pointer_annotated(member),
741 ))
742}
743
744/// [`pointer_member`] for a member as declared.
745#[must_use]
746pub fn member_held_behind_pointer(
747 ast: &Ast,
748 enclosing: Id<TypeNode>,
749 member: &zerodds_ast::MemberNode,
750 target: Id<TypeNode>,
751) -> bool {
752 pointer_member(ast, enclosing, target, pointer_annotated(member))
753}
754
755/// The member that holds the base of a derived structure.
756/// XTypes 1.3 §7.5.1.4.1.3.
757pub const PARENT: &str = "parent";
758
759/// The structure `id` derives from, if it does.
760#[must_use]
761pub fn struct_base(ast: &Ast, id: Id<TypeNode>) -> Option<Id<TypeNode>> {
762 match ast.type_(id).map(|node| &node.kind) {
763 Ok(TypeKind::Structure {
764 base: Some(base), ..
765 }) => base.resolved(),
766 _ => None,
767 }
768}
769
770/// One member of a structure as C reaches it.
771#[derive(Debug, Clone)]
772pub struct CMember {
773 /// The member.
774 pub handle: Id<zerodds_ast::MemberNode>,
775 /// The path to it from the structure: empty for its own, `parent.` for the base's,
776 /// `parent.parent.` for the base's base.
777 pub path: String,
778 /// The structure that declares it.
779 pub owner: Id<TypeNode>,
780}
781
782/// Every member `id` has, the base's first. Same order as [`Ast::all_members_of`].
783#[must_use]
784pub fn c_members(ast: &Ast, id: Id<TypeNode>) -> Vec<CMember> {
785 let mut chain = vec![id];
786 while chain.len() < 64 {
787 match struct_base(ast, *chain.last().unwrap_or(&id)) {
788 Some(base) if !chain.contains(&base) => chain.push(base),
789 _ => break,
790 }
791 }
792 let mut out = Vec::new();
793 for (depth, owner) in chain.iter().enumerate().rev() {
794 let path = format!("{PARENT}.").repeat(depth);
795 for member in ast.members_of(*owner) {
796 out.push(CMember {
797 handle: *member,
798 path: path.clone(),
799 owner: *owner,
800 });
801 }
802 }
803 out
804}
805
806/// The bound a `string`, a `sequence` or a `map` carries, as the suffix
807/// that keeps two of them apart.
808///
809/// One codec body is emitted per routine name, and the name is built
810/// from the type's C name. With the bound left out, `sequence<long>`
811/// and `sequence<long, 4>` were one name and whichever was declared
812/// first decided: measured on
813/// `@final struct S { sequence<long> b; sequence<long, 4> a; };`, the
814/// generated C held no `zd_over` check at all, so `a` accepted a stream
815/// of any length; with the members the other way round both got the
816/// bounded body, so `b` refused — and truncated — a length the IDL puts
817/// no limit on. `string<4>` beside `string` and `wstring<4>` beside
818/// `wstring` collapsed the same way, onto `zdw_utf8_octets` and
819/// `zdw_utf16_units`. Either way the reader answered with a value the
820/// IDL does not describe and said nothing.
821///
822/// An `array` needs nothing here: its `dims` are already in the name.
823pub(crate) fn bound_tag(ast: &Ast, bound: &zerodds_ast::Bound) -> String {
824 match bound.expr() {
825 None => String::new(),
826 // `_b` and not a bare `_n`: `zd_seq_long_4` is what the `Array`
827 // arm answers for an array of four `long`, and the two are
828 // different wire forms.
829 Some(expr) => match ast.eval_integral(expr) {
830 Ok(limit) => format!("_b{limit}"),
831 // As the array's `_x`: a bound that does not evaluate is
832 // still not the unbounded form, and `c_items` reports the
833 // expression that would not evaluate.
834 Err(_) => "_bx".to_string(),
835 },
836 }
837}
838
839/// The C spelling of a type where a declarator needs one.
840///
841/// A primitive is its own spelling; everything else is a declared name,
842/// because C has no way to write an anonymous aggregate twice and have
843/// the two be the same type.
844///
845/// A `sequence` and a `map` carry their bound here and not only in
846/// [`c_codec_tag`], because in this column the name is a *declaration*
847/// as well as a routine name: `zd_seq_long` is a struct `declare`
848/// emits, and two codecs over one struct would be two functions whose
849/// parameter type says nothing about which of the two IDL types they
850/// read. `zd_seq_long` and `zd_seq_b4_long` are two structs of the same
851/// shape, one per IDL type, and the bounded member is declared at the
852/// bounded one — which is what makes the split reach the header and not
853/// just the codec.
854///
855/// A `string` is the exception and takes its bound in [`type_tag`]
856/// alone: both shapes are the prelude's [`STRING`] and [`WSTRING`],
857/// there is one C type per width whatever the bound, and `declare`
858/// emits nothing for either. A declarator with a bound in it would name
859/// a type no header defines.
860#[must_use]
861pub fn c_type_name(ast: &Ast, id: Id<TypeNode>) -> String {
862 type_name_at(ast, id, 0)
863}
864
865/// [`c_type_name`], carrying how many connector steps the walk has
866/// already taken.
867///
868/// zerodds-lint: recursion-depth 64 (unnamed-typedef chains and the
869/// element, key and value tags; bounded by `MAX_DEPTH`, which names the
870/// type by its arena slot rather than descending). The slot and not a
871/// fixed word: two types that both nest past the bound would otherwise
872/// take one C name, and the second declaration would silently stand for
873/// both.
874pub(crate) fn type_name_at(ast: &Ast, id: Id<TypeNode>, depth: usize) -> String {
875 if depth > MAX_DEPTH {
876 return format!("zd_deep_{}", id.slot());
877 }
878 let Ok(node) = ast.type_(id) else {
879 return "void".to_string();
880 };
881 match &node.kind {
882 // `long double` is the one primitive with no C built-in that
883 // carries it: the spelling is `zdw_f128`, which is binary128
884 // whatever the compiler's `long double` happens to be on this
885 // target. Matched ahead of the arm below, which would reach
886 // `c_primitive`'s `double`. See `runtime/c/include/zerodds_wire.h`
887 // and `docs/float128-column.md` §2 for the three formats that
888 // name covers.
889 TypeKind::Primitive(PrimitiveKind::Float128) => FLOAT128.to_string(),
890 TypeKind::Primitive(kind) => c_primitive(*kind).as_str().to_string(),
891 TypeKind::String {
892 width: CharWidth::Char,
893 ..
894 } => STRING.to_string(),
895 TypeKind::String {
896 width: CharWidth::WChar,
897 ..
898 } => WSTRING.to_string(),
899 TypeKind::Fixed { digits, scale } => format!("zd_fixed_{digits}_{scale}"),
900 TypeKind::Sequence { element, bound } => match element.resolved() {
901 Some(element) => format!(
902 "zd_seq{}_{}",
903 bound_tag(ast, bound),
904 type_tag_at(ast, element, depth + 1)
905 ),
906 None => "void".to_string(),
907 },
908 TypeKind::Array { element, dims } => match element.resolved() {
909 Some(element) => {
910 let mut name = format!("zd_arr_{}", type_tag_at(ast, element, depth + 1));
911 for dim in dims {
912 match ast.eval_integral(*dim) {
913 Ok(length) => name.push_str(&format!("_{length}")),
914 // A dimension that does not evaluate gets no
915 // number rather than a zero: the name has to
916 // differ from a `[0]` array's, and
917 // `c_items` reports the failure by name.
918 Err(_) => name.push_str("_x"),
919 }
920 }
921 name
922 }
923 None => "void".to_string(),
924 },
925 // XTypes 1.3 §7.5.1.3.2.1: `[key_type][fully_qualified_element_type]Map`
926 // where the key is a primitive or a string; any other key keeps the
927 // structural name, because the clause gives none.
928 TypeKind::Map { key, value, bound } => match (key.resolved(), value.resolved()) {
929 (Some(key), Some(value)) => super::c_map::map_type_name(ast, key, value, bound, depth)
930 .unwrap_or_else(|| {
931 format!(
932 "zd_map{}_{}_{}",
933 bound_tag(ast, bound),
934 type_tag_at(ast, key, depth + 1),
935 type_tag_at(ast, value, depth + 1)
936 )
937 }),
938 _ => "void".to_string(),
939 },
940 // A typedef with no name is one the frontend made for an inline
941 // type; it is transparent, so the C name is the target's.
942 TypeKind::Alias { aliased } if node.name.is_empty() => match aliased.resolved() {
943 Some(target) => type_name_at(ast, target, depth + 1),
944 None => "void".to_string(),
945 },
946 _ => flattened(ast, id),
947 }
948}
949
950/// The name component a collection takes from its element.
951///
952/// The IDL kind and not the C spelling, and this is not cosmetic:
953/// `double` and `long double` are both a C `double` here — Rust has no
954/// 128-bit float either — and they are eight and sixteen bytes on the
955/// wire. A structural name built from the C type would give
956/// `sequence<double>` and `sequence<long double>` one declaration and
957/// one encoder, and the second would go out at the first's width with
958/// nothing to say it had. The same collapse would hit `octet` against
959/// `uint8` and `unsigned short` against `wchar`, where it happens to be
960/// harmless.
961///
962/// A `string`'s bound rides here and not in [`c_type_name`], for the
963/// reason [`c_codec_tag`] states: both bounds are one declarator type
964/// and two wire forms, so the name has to keep them apart where the
965/// declarator cannot.
966///
967#[must_use]
968pub fn type_tag(ast: &Ast, id: Id<TypeNode>) -> String {
969 type_tag_at(ast, id, 0)
970}
971
972/// [`type_tag`], carrying how many connector steps the walk has already
973/// taken.
974///
975/// The depth is threaded through [`type_name_at`] and back rather than
976/// started over at either door: the two reach each other, so a counter
977/// reset at one of them would leave the pair with no bound at all. The
978/// marker this function used to carry — `recursion-depth 64 (a chain of
979/// unnamed typedefs)` — named a bound nothing enforced.
980///
981/// zerodds-lint: recursion-depth 64 (unnamed-typedef chains, via
982/// `type_name_at`; bounded by `MAX_DEPTH`, which names the type by its
983/// arena slot rather than descending)
984pub(crate) fn type_tag_at(ast: &Ast, id: Id<TypeNode>, depth: usize) -> String {
985 if depth > MAX_DEPTH {
986 return format!("zd_deep_{}", id.slot());
987 }
988 let Ok(node) = ast.type_(id) else {
989 return type_name_at(ast, id, depth + 1);
990 };
991 match &node.kind {
992 TypeKind::Primitive(kind) => kind.as_str().replace(' ', "_"),
993 TypeKind::String {
994 width: CharWidth::Char,
995 bound,
996 } => format!("{STRING}{}", bound_tag(ast, bound)),
997 TypeKind::String {
998 width: CharWidth::WChar,
999 bound,
1000 } => format!("{WSTRING}{}", bound_tag(ast, bound)),
1001 // A typedef with no name is one the frontend made for an inline
1002 // type; it is transparent, so the tag is the target's — which
1003 // is what carries a bounded string's bound through one.
1004 TypeKind::Alias { aliased } if node.name.is_empty() => match aliased.resolved() {
1005 Some(target) => type_tag_at(ast, target, depth + 1),
1006 None => type_name_at(ast, id, depth + 1),
1007 },
1008 _ => type_name_at(ast, id, depth + 1),
1009 }
1010}
1011
1012/// The name a generated codec function is called after.
1013///
1014/// [`type_tag`], which is the *name component* function and not the
1015/// declarator one: the two part on a string, and only on a string. A
1016/// `string<4>` and a `string` are both the prelude's [`STRING`] — one C
1017/// type per width whatever the bound, and `declare` emits nothing for
1018/// either — so `c_type_name` has to keep answering `zd_string` for both
1019/// or a member would be declared at a type no header defines. The tag
1020/// carries the bound instead, and `zd_dec_zd_string` beside
1021/// `zd_dec_zd_string_b4` is the same arrangement the nineteen other
1022/// columns have for a string: two routines over one type.
1023///
1024/// Everything else is its declaration's own name, which already carries
1025/// the bound — see [`c_type_name`] — so the routine and the struct it
1026/// reads split together.
1027#[must_use]
1028pub fn c_codec_tag(ast: &Ast, id: Id<TypeNode>) -> String {
1029 type_tag(ast, id)
1030}
1031
1032/// The map entry struct a `map<K,V>` needs a name for.
1033#[must_use]
1034pub fn c_map_entry_name(ast: &Ast, id: Id<TypeNode>) -> String {
1035 super::c_map::entry_name(&c_type_name(ast, id))
1036}
1037
1038/// A named type's C identifier: the scoped name with the module
1039/// separators flattened onto it, which is what both C generators in the
1040/// field do and what a `#include` of two modules requires.
1041#[must_use]
1042pub fn flattened(ast: &Ast, id: Id<TypeNode>) -> String {
1043 let Ok(any) = ast.any_of_type(id) else {
1044 return "zd_unnamed".to_string();
1045 };
1046 match ast.scoped_name_of(any) {
1047 Some(scoped) => c::escape_keyword(&scoped.segments().join("_")),
1048 None => "zd_unnamed".to_string(),
1049 }
1050}
1051
1052/// The flattened module path a file-scope constant carries.
1053#[must_use]
1054pub fn module_prefix(ast: &Ast, id: Id<TypeNode>) -> String {
1055 let Ok(any) = ast.any_of_type(id) else {
1056 return String::new();
1057 };
1058 let Some(scoped) = ast.scoped_name_of(any) else {
1059 return String::new();
1060 };
1061 let segments = scoped.segments();
1062 if segments.len() <= 1 {
1063 return String::new();
1064 }
1065 let mut prefix = segments[..segments.len() - 1].join("_");
1066 prefix.push('_');
1067 prefix
1068}
1069
1070/// The C primitive an IDL primitive becomes.
1071///
1072/// Exhaustive, so a primitive added to the model without a C spelling is
1073/// a compile error and not a member that silently takes another's.
1074///
1075/// `char` is one byte and `wchar` two, which is what the wire form is;
1076/// XTypes 1.3 §7.5.1.1.1 leaves the C spelling of `Char16` to the
1077/// binding, and a fixed-width integer is the only choice that does not
1078/// depend on the platform's `wchar_t`.
1079///
1080/// `long double` is **not** routed here: it takes [`FLOAT128`], which is
1081/// a real binary128. The `Float128` arm below exists so the match is
1082/// total and answers for a caller that wants the nearest built-in; both
1083/// callers in this module — [`c_type_name`] and [`c_value_type`] — match
1084/// `Float128` before they reach it. A caller that does not is the defect
1085/// the three-column gate found: `double` is eight bytes where the member
1086/// is sixteen.
1087#[must_use]
1088pub const fn c_primitive(kind: PrimitiveKind) -> c::Primitive {
1089 match kind {
1090 PrimitiveKind::Boolean => c::Primitive::Bool,
1091 PrimitiveKind::Octet | PrimitiveKind::UInt8 => c::Primitive::UInt8,
1092 PrimitiveKind::Int8 => c::Primitive::Int8,
1093 PrimitiveKind::Char => c::Primitive::Char,
1094 PrimitiveKind::WChar => c::Primitive::UInt16,
1095 PrimitiveKind::Int16 => c::Primitive::Int16,
1096 PrimitiveKind::UInt16 => c::Primitive::UInt16,
1097 PrimitiveKind::Int32 => c::Primitive::Int32,
1098 PrimitiveKind::UInt32 => c::Primitive::UInt32,
1099 PrimitiveKind::Int64 => c::Primitive::Int64,
1100 PrimitiveKind::UInt64 => c::Primitive::UInt64,
1101 PrimitiveKind::Float32 => c::Primitive::Float,
1102 PrimitiveKind::Float64 | PrimitiveKind::Float128 => c::Primitive::Double,
1103 }
1104}
1105
1106/// The unsigned integer a bit width calls for.
1107///
1108/// Public because the decoder emitter has to cast into exactly this
1109/// type: a bitfield is unpacked out of a 64-bit anchor and assigned to
1110/// a member whose width is decided here, and a second copy of the rule
1111/// would narrow into a type this one had moved on from.
1112#[must_use]
1113pub const fn c_holder(bits: u16) -> c::Primitive {
1114 match bits {
1115 0..=8 => c::Primitive::UInt8,
1116 9..=16 => c::Primitive::UInt16,
1117 17..=32 => c::Primitive::UInt32,
1118 _ => c::Primitive::UInt64,
1119 }
1120}
1121
1122fn type_by_name(name: &str) -> c::Type {
1123 c::Type::Named(Ident::new(name))
1124}
1125
1126/// The C declarator type a value of `id` is held in.
1127///
1128/// The one answer, for every emitter. A member of a generated structure,
1129/// a codec's `v` parameter, a defaulter's destination, the element type a
1130/// sequence's buffer is allocated at — all four have to name the same C
1131/// type or the translation unit does not compile, and where it happens to
1132/// compile anyway the two names have to be the same *width* or the
1133/// allocation is short.
1134///
1135/// It was three private copies in `encode_c`, `decode_c` and `default_c`
1136/// beside this one, and that is what let `long double` diverge: this
1137/// function answers [`FLOAT128`] for it and the copies asked
1138/// [`c_primitive`], which answers `double` — sixteen bytes against eight,
1139/// with the codec's `const double *` pointing at a `zdw_f128` member.
1140#[must_use]
1141pub fn c_value_type(ast: &Ast, id: Id<TypeNode>) -> c::Type {
1142 let Ok(node) = ast.type_(id) else {
1143 return c::Type::Void;
1144 };
1145 match &node.kind {
1146 TypeKind::Primitive(PrimitiveKind::Float128) => type_by_name(FLOAT128),
1147 TypeKind::Primitive(kind) => c::Type::Primitive(c_primitive(*kind)),
1148 _ => type_by_name(&c_type_name(ast, id)),
1149 }
1150}
1151
1152/// A type the model declares under the name `<TypeName>Bits` of a
1153/// bitmask's flag type.
1154///
1155/// DDS-XTypes 1.3 §7.5.1.2.2 names that type as part of the binding, so
1156/// a user type of the same C name cannot be given another name here
1157/// without the header no longer being the one the clause describes. The
1158/// two would be one identifier in the header, the second declaration
1159/// would be skipped as a repeat (`declare`) or rejected by the C
1160/// compiler, and either way the source would not mean what it says; the
1161/// refusal names both declarations instead.
1162///
1163/// # Errors
1164/// [`MaterialiseError::SpellingCollision`] when two declarations reach
1165/// one C name, the second being a bitmask's `<TypeName>Bits`.
1166fn no_bits_name_collision(ast: &Ast, order: &[Id<TypeNode>]) -> Result<(), MaterialiseError> {
1167 let mut spelled: Vec<(String, String)> = Vec::new();
1168 let mut primitives: Vec<String> = Vec::new();
1169 for id in order {
1170 let Ok(node) = ast.type_(*id) else { continue };
1171 // Anonymous collections share a C name by shape, on purpose.
1172 if node.name.is_empty() {
1173 continue;
1174 }
1175 let name = c_type_name(ast, *id);
1176 // Two IDL primitives may have one C type (`octet` and `uint8` are
1177 // both `uint8_t`); that is not a collision of declarations. A
1178 // primitive stands in the list once per spelling, so that a
1179 // declared name equal to one is still found.
1180 if matches!(node.kind, TypeKind::Primitive(_)) {
1181 if !primitives.contains(&name) {
1182 primitives.push(name.clone());
1183 spelled.push((name, node.name.clone()));
1184 }
1185 continue;
1186 }
1187 if matches!(node.kind, TypeKind::Bitmask { .. }) {
1188 spelled.push((name.clone(), node.name.clone()));
1189 spelled.push((
1190 format!("{name}Bits"),
1191 format!("{}Bits (the flag type of bitmask {})", node.name, node.name),
1192 ));
1193 } else {
1194 spelled.push((name, node.name.clone()));
1195 }
1196 }
1197 no_spelling_collision(
1198 spelled
1199 .iter()
1200 .map(|(spelled, declared)| (spelled.as_str(), declared.as_str())),
1201 "C",
1202 "the types of one header",
1203 )
1204}
1205
1206#[allow(clippy::too_many_lines)]
1207fn declare(
1208 ast: &Ast,
1209 applied: &Applied,
1210 id: Id<TypeNode>,
1211 out: &mut Vec<c::Decl>,
1212 deferred: &mut Vec<c::Decl>,
1213 emitted: &mut Vec<String>,
1214) -> Result<(), MaterialiseError> {
1215 let Ok(node) = ast.type_(id) else {
1216 return Ok(());
1217 };
1218 let name = c_type_name(ast, id);
1219 // Two anonymous collections of the same shape are one C type. The
1220 // check is by name because the name is structural.
1221 if emitted.contains(&name) {
1222 return Ok(());
1223 }
1224
1225 match &node.kind {
1226 // A primitive needs no declaration; its C spelling is built in.
1227 TypeKind::Primitive(_) => return Ok(()),
1228 // The two string shapes are in the prelude.
1229 TypeKind::String { .. } => return Ok(()),
1230
1231 TypeKind::Fixed { digits, scale } => {
1232 emitted.push(name.clone());
1233 out.push(c::Decl::Typedef(c::Typedef {
1234 doc: Doc::line(&format!(
1235 "`fixed<{digits},{scale}>`, held as the decimal it was written as. \
1236 A double cannot carry one, which is the point of the type."
1237 )),
1238 type_: c::Type::Pointer(Box::new(c::Type::Const(Box::new(c::Type::Primitive(
1239 c::Primitive::Char,
1240 ))))),
1241 name: Ident::new(&name),
1242 dims: Vec::new(),
1243 }));
1244 }
1245
1246 TypeKind::Sequence { element, .. } => {
1247 let Some(element) = element.resolved() else {
1248 return Err(MaterialiseError::Unevaluated {
1249 construct: "sequence element".to_string(),
1250 at: name,
1251 });
1252 };
1253 emitted.push(name.clone());
1254 out.extend(sequence_decl(&name, c_member_type(ast, element, &name)?));
1255 }
1256
1257 TypeKind::Array { element, dims } => {
1258 let Some(element) = element.resolved() else {
1259 return Err(MaterialiseError::Unevaluated {
1260 construct: "array element".to_string(),
1261 at: name,
1262 });
1263 };
1264 let mut lengths = Vec::new();
1265 for dim in dims {
1266 let length =
1267 ast.eval_integral(*dim)
1268 .map_err(|_| MaterialiseError::Unevaluated {
1269 construct: "array length".to_string(),
1270 at: name.clone(),
1271 })?;
1272 lengths.push(c::Expr::Literal(Literal::int(length)));
1273 }
1274 emitted.push(name.clone());
1275 out.push(c::Decl::Typedef(c::Typedef {
1276 doc: Doc::line("An array. The element count is in the type and not on the wire."),
1277 type_: c_member_type(ast, element, &name)?,
1278 name: Ident::new(&name),
1279 dims: lengths,
1280 }));
1281 }
1282
1283 TypeKind::Map { key, value, .. } => {
1284 let (Some(key), Some(value)) = (key.resolved(), value.resolved()) else {
1285 return Err(MaterialiseError::Unevaluated {
1286 construct: "map key or value".to_string(),
1287 at: name,
1288 });
1289 };
1290 let entry = c_map_entry_name(ast, id);
1291 emitted.push(name.clone());
1292 // The typedef here, the definition at the end of the file:
1293 // the map's `_buffer` is a pointer, so an incomplete entry
1294 // suffices at this position — and has to, when the value
1295 // type is an enclosing one and not yet complete.
1296 out.push(c::Decl::Typedef(c::Typedef {
1297 doc: Doc::none(),
1298 type_: c::Type::StructTag(Ident::new(&entry)),
1299 name: Ident::new(&entry),
1300 dims: Vec::new(),
1301 }));
1302 deferred.push(c::Decl::Struct(c::Struct {
1303 body_text: zerodds_lang_cst::common::BodyText::none(),
1304 doc: Doc::line(
1305 "One entry of a map. XTypes 1.3 7.4.1.1.4 defines a map as a \
1306 sequence of these, and the rules write the two halves with no \
1307 framing between them.",
1308 ),
1309 name: Some(Ident::new(&entry)),
1310 members: vec![
1311 c::Member {
1312 doc: Doc::none(),
1313 type_: c_member_type(ast, key, &name)?,
1314 name: Ident::new("key"),
1315 dims: Vec::new(),
1316 bits: None,
1317 },
1318 c::Member {
1319 doc: Doc::none(),
1320 type_: c_member_type(ast, value, &name)?,
1321 name: Ident::new("value"),
1322 dims: Vec::new(),
1323 bits: None,
1324 },
1325 ],
1326 }));
1327 out.extend(super::c_map::map_decl(&name, &entry));
1328 }
1329
1330 TypeKind::Alias { aliased } => {
1331 if node.name.is_empty() {
1332 return Ok(());
1333 }
1334 if ghost_created_for(applied, node.node_id, "CAlias").is_none() {
1335 return Ok(());
1336 }
1337 let Some(target) = aliased.resolved() else {
1338 return Err(MaterialiseError::Unevaluated {
1339 construct: "typedef target".to_string(),
1340 at: name,
1341 });
1342 };
1343 emitted.push(name.clone());
1344 // `typedef Row Grid;` where `Row` is itself an array typedef
1345 // is legal C and keeps the dimensions where they were
1346 // declared, so the alias never restates them.
1347 out.push(c::Decl::Typedef(c::Typedef {
1348 doc: Doc::none(),
1349 type_: c_value_type(ast, target),
1350 name: Ident::new(&name),
1351 dims: Vec::new(),
1352 }));
1353 }
1354
1355 TypeKind::Enumeration { enumerators } => {
1356 if ghost_created_for(applied, node.node_id, "CEnum").is_none() {
1357 return Ok(());
1358 }
1359 emitted.push(name.clone());
1360 let prefix = module_prefix(ast, id);
1361 let mut values = Vec::new();
1362 let mut names = Vec::new();
1363 for enumerator in enumerators {
1364 let Ok(enum_node) = ast.enumerator(*enumerator) else {
1365 continue;
1366 };
1367 if ghost_created_for(applied, enum_node.node_id, "CEnumerator").is_none() {
1368 continue;
1369 }
1370 let value = ast.effective_enumerator_value(*enumerator).ok_or_else(|| {
1371 MaterialiseError::Unevaluated {
1372 construct: "enumerator value".to_string(),
1373 at: enum_node.name.clone(),
1374 }
1375 })?;
1376 // The constant's whole spelling on the left, prefix and
1377 // all: that is the name a caller writes, and half of it
1378 // would be a mapping onto something the header does not
1379 // declare.
1380 let constant = format!("{prefix}{}", c::escape_keyword(&enum_node.name));
1381 names.push((constant.clone(), enum_node.name.clone()));
1382 values.push((constant, value));
1383 }
1384 // The storage is a fixed-width integer even when a C `enum`
1385 // would do: an enumeration constant is an `int`, and the
1386 // holder the wire wants is the `@bit_bound`'s, which is not
1387 // the same question.
1388 out.push(c::Decl::Typedef(c::Typedef {
1389 doc: Doc::line(
1390 "An enumeration. The holder is signed because XTypes 1.3 \
1391 7.4.3.5.3 makes it so; the width on the wire is the \
1392 @bit_bound's and is applied by the runtime.",
1393 ),
1394 type_: c::Type::Primitive(c::Primitive::Int32),
1395 name: Ident::new(&name),
1396 dims: Vec::new(),
1397 }));
1398 for (constant, value) in values {
1399 // Parenthesised: a macro replacement list that is not
1400 // self-contained changes meaning at its use site.
1401 out.push(c::Decl::Define(c::Define {
1402 doc: Doc::none(),
1403 name: Ident::new(&constant),
1404 value: Some(c::Expr::Paren(Box::new(c::Expr::Cast {
1405 type_: Box::new(type_by_name(&name)),
1406 value: Box::new(c::Expr::Literal(Literal::int(value))),
1407 }))),
1408 }));
1409 }
1410 out.extend(idl_members(&name, &names)?);
1411 }
1412
1413 TypeKind::Bitmask { bits } => {
1414 if ghost_created_for(applied, node.node_id, "CBitmask").is_none() {
1415 return Ok(());
1416 }
1417 emitted.push(name.clone());
1418 let bound = ast.effective_bit_bound(id);
1419 let prefix = module_prefix(ast, id);
1420 let mut names = Vec::new();
1421 // XTypes 1.3 §7.5.1.2.2: "the language binding shall be as if
1422 // there was an enumeration definition like the following:
1423 // @bit_bound(n) enum <TypeName>Bits { @value(1 << p) FLAG, … }",
1424 // and a typedef of the unsigned integer Table 44 gives for the
1425 // bound for "collections of flags from the previously defined
1426 // enumeration". The enumeration is written as C writes one
1427 // here (see the enumeration arm): an integer typedef with a
1428 // constant per flag. Its holder is the unsigned integer of the
1429 // bound, not a signed 32-bit one: a flag of a 64-bit bitmask
1430 // has a value an `int` cannot hold.
1431 let bits_name = format!("{name}Bits");
1432 out.push(c::Decl::Typedef(c::Typedef {
1433 doc: Doc::line(
1434 "The enumeration of the flags of a bitmask (XTypes 1.3 \
1435 7.5.1.2.2): one constant per flag, valued 1 << position.",
1436 ),
1437 type_: c::Type::Primitive(c_holder(bound)),
1438 name: Ident::new(&bits_name),
1439 dims: Vec::new(),
1440 }));
1441 out.push(c::Decl::Typedef(c::Typedef {
1442 doc: Doc::line(
1443 "A bitmask: a collection of flags, as the unsigned integer \
1444 Table 44 of XTypes 1.3 7.5.1.2.2 gives for its bound.",
1445 ),
1446 type_: c::Type::Primitive(c_holder(bound)),
1447 name: Ident::new(&name),
1448 dims: Vec::new(),
1449 }));
1450 for bit in bits {
1451 let Ok(bit_node) = ast.bit_value(*bit) else {
1452 continue;
1453 };
1454 if ghost_created_for(applied, bit_node.node_id, "CBitConst").is_none() {
1455 continue;
1456 }
1457 let position = ast.effective_bit_position(*bit).ok_or_else(|| {
1458 MaterialiseError::Unevaluated {
1459 construct: "bit position".to_string(),
1460 at: bit_node.name.clone(),
1461 }
1462 })?;
1463 // `#define MOVING (FlagsBits)1 << 4` expands inside
1464 // `x & MOVING` as `(x & (FlagsBits)1) << 4`. The brackets
1465 // are the difference between a bitmask constant and a
1466 // number nobody wrote.
1467 let constant = format!("{prefix}{}", c::escape_keyword(&bit_node.name));
1468 names.push((constant.clone(), bit_node.name.clone()));
1469 out.push(c::Decl::Define(c::Define {
1470 doc: Doc::none(),
1471 name: Ident::new(&constant),
1472 value: Some(c::Expr::Paren(Box::new(c::Expr::Binary {
1473 op: "<<",
1474 lhs: Box::new(c::Expr::Cast {
1475 type_: Box::new(type_by_name(&bits_name)),
1476 value: Box::new(c::Expr::Literal(Literal::int(1))),
1477 }),
1478 rhs: Box::new(c::Expr::Literal(Literal::int(i128::from(position)))),
1479 }))),
1480 }));
1481 }
1482 out.extend(idl_members(&name, &names)?);
1483 }
1484
1485 TypeKind::Bitset { .. } => {
1486 if ghost_created_for(applied, node.node_id, "CBitset").is_none() {
1487 return Ok(());
1488 }
1489 emitted.push(name.clone());
1490 let mut members = Vec::new();
1491 let mut names = Vec::new();
1492 // The base's bitfields are members of the derived struct
1493 // too: an inherited bitset is one flat run.
1494 for field in &ast.all_bitfields_of(id) {
1495 let Ok(field_node) = ast.bitfield(*field) else {
1496 continue;
1497 };
1498 // An anonymous field is padding: it moves the next field
1499 // along and carries no value of its own.
1500 let Some(field_name) = field_node.name.as_ref() else {
1501 continue;
1502 };
1503 let escaped = c::escape_keyword(field_name);
1504 names.push((escaped.clone(), field_name.clone()));
1505 members.push(c::Member {
1506 doc: Doc::none(),
1507 type_: c::Type::Primitive(c_holder(field_node.bits)),
1508 name: Ident::new(&escaped),
1509 dims: Vec::new(),
1510 // Not a C bitfield: C leaves the allocation order of
1511 // bitfields implementation-defined, and the order on
1512 // the wire is the declaration's. Packing is the
1513 // codec's, where it can be seen.
1514 bits: None,
1515 });
1516 }
1517 out.extend(struct_typedef(
1518 &name,
1519 members,
1520 Doc::line(
1521 "A bitset. The fields are separate integers here; the packing into \
1522 the anchor is the codec's, because C leaves bitfield allocation \
1523 order to the implementation.",
1524 ),
1525 ));
1526 out.extend(idl_members(&name, &names)?);
1527 }
1528
1529 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
1530 let created = if matches!(node.kind, TypeKind::Valuetype { .. }) {
1531 "CValue"
1532 } else {
1533 "CStruct"
1534 };
1535 if ghost_created_for(applied, node.node_id, created).is_none() {
1536 return Ok(());
1537 }
1538 emitted.push(name.clone());
1539 // XTypes 1.3 §7.5.1.4.1.3: "The language binding shall be
1540 // generated as if an instance of the base type were the first
1541 // member of the subtype with the name 'parent'". The struct
1542 // declares that member, then its own; the plan still puts the
1543 // base's members on the wire first (§7.4.1.1.5.1), and the codec
1544 // reaches them as `parent.m`. The table of IDL names lists every
1545 // member the struct has, the base's first, under that path.
1546 let mut members = Vec::new();
1547 let mut names = Vec::new();
1548 if let Some(base) = struct_base(ast, id) {
1549 members.push(c::Member {
1550 doc: Doc::line(
1551 "The base: an instance of the base type is the first member \
1552 (XTypes 1.3 7.5.1.4.1.3).",
1553 ),
1554 type_: type_by_name(&c_type_name(ast, base)),
1555 name: Ident::new(PARENT),
1556 dims: Vec::new(),
1557 bits: None,
1558 });
1559 }
1560 let own = ast.members_of(id).to_vec();
1561 for CMember {
1562 handle: member,
1563 path,
1564 ..
1565 } in c_members(ast, id)
1566 {
1567 let Ok(member_node) = ast.member(member) else {
1568 continue;
1569 };
1570 let field = c::escape_keyword(&member_node.name);
1571 // XTypes 1.3 §7.5.1.2.4.1: an optional member is a
1572 // pointer and "A NULL pointer shall indicate an omitted
1573 // value", so there is no flag beside it. The path is the one
1574 // a member of the base takes (`parent.m`).
1575 names.push((format!("{path}{field}"), member_node.name.clone()));
1576 if !own.contains(&member) {
1577 continue;
1578 }
1579 let Some(target) = member_node.type_.resolved() else {
1580 return Err(MaterialiseError::Unevaluated {
1581 construct: "member type".to_string(),
1582 at: member_node.name.clone(),
1583 });
1584 };
1585 members.push(c::Member {
1586 doc: if member_node.facets.is_optional() {
1587 Doc::line(
1588 "An @optional member: NULL when it is absent. In a `@mutable` \
1589 type an absent member is not written at all; outside one its \
1590 absence is written — a zero `<is_present>` byte under XCDR2 \
1591 (rule 20) or a parameter header declaring a length of zero \
1592 under XCDR1 (§7.4.1.2).",
1593 )
1594 } else {
1595 Doc::none()
1596 },
1597 type_: c_held_member_type(ast, id, member_node, target, &member_node.name)?,
1598 name: Ident::new(&field),
1599 dims: Vec::new(),
1600 bits: None,
1601 });
1602 }
1603 // C has no empty structure. C23 §6.7.2.1p8: "If the
1604 // struct-declaration-list does not contain any named
1605 // members … the behavior is undefined" — gcc and clang
1606 // accept `struct S { };` only as an extension, and the
1607 // probe builds with `-std=c11 -pedantic-errors`, where it
1608 // is an error.
1609 //
1610 // Refused by name rather than emitted: the generated header
1611 // did compile nowhere and the failure arrived as
1612 // "does not compile" against a build directory, 840 slots
1613 // at a time, with nothing naming the construct. C++ has no
1614 // such rule and its column carries the case.
1615 if members.is_empty() {
1616 return Err(MaterialiseError::NoLanguageForm {
1617 construct: format!(
1618 "`{name}`, an aggregate with no members — C23 §6.7.2.1p8 \
1619 leaves a structure with no named member undefined"
1620 ),
1621 language: "C",
1622 at: name,
1623 });
1624 }
1625 out.extend(struct_typedef(&name, members, Doc::none()));
1626 out.extend(idl_members(&name, &names)?);
1627 }
1628
1629 TypeKind::Union { discriminator, .. } => {
1630 if ghost_created_for(applied, node.node_id, "CUnion").is_none() {
1631 return Ok(());
1632 }
1633 emitted.push(name.clone());
1634 let Some(disc) = discriminator.resolved() else {
1635 return Err(MaterialiseError::Unevaluated {
1636 construct: "union discriminator".to_string(),
1637 at: name,
1638 });
1639 };
1640 let mut arms = Vec::new();
1641 let mut names = Vec::new();
1642 for case in ast.cases_of(id) {
1643 let Ok(case_node) = ast.case(*case) else {
1644 continue;
1645 };
1646 if ghost_created_for(applied, case_node.node_id, "CCase").is_none() {
1647 continue;
1648 }
1649 let Ok(member) = ast.member(case_node.member) else {
1650 continue;
1651 };
1652 let Some(target) = member.type_.resolved() else {
1653 return Err(MaterialiseError::Unevaluated {
1654 construct: "union case type".to_string(),
1655 at: member.name.clone(),
1656 });
1657 };
1658 let field = c::escape_keyword(&member.name);
1659 // The branches and not `_d`: the discriminator is this
1660 // binding's accessor name, and IDL 4.2 §7.4.1.4.4.1
1661 // gives it no member name of its own to record.
1662 names.push((field.clone(), member.name.clone()));
1663 arms.push(c::Member {
1664 doc: Doc::none(),
1665 type_: c_held_member_type(ast, id, member, target, &member.name)?,
1666 name: Ident::new(&field),
1667 dims: Vec::new(),
1668 bits: None,
1669 });
1670 }
1671 let inner = format!("{name}_u");
1672 out.push(c::Decl::Union(c::Union {
1673 doc: Doc::line("The branches. Which one is live is what `_d` says."),
1674 name: Some(Ident::new(&inner)),
1675 members: arms,
1676 }));
1677 out.extend(struct_typedef(
1678 &name,
1679 vec![
1680 c::Member {
1681 doc: Doc::none(),
1682 type_: c_member_type(ast, disc, &name)?,
1683 name: Ident::new("_d"),
1684 dims: Vec::new(),
1685 bits: None,
1686 },
1687 c::Member {
1688 doc: Doc::none(),
1689 type_: c::Type::UnionTag(Ident::new(&inner)),
1690 name: Ident::new("_u"),
1691 dims: Vec::new(),
1692 bits: None,
1693 },
1694 ],
1695 Doc::none(),
1696 ));
1697 out.extend(idl_members(&name, &names)?);
1698 }
1699
1700 TypeKind::Native => {
1701 if ghost_created_for(applied, node.node_id, "CNative").is_none() {
1702 return Ok(());
1703 }
1704 emitted.push(name.clone());
1705 // Opaque, never a guess at what the name might mean: the
1706 // concrete type comes from the facet a `#pragma` or an
1707 // annotation set, and a `void *` when there is none.
1708 let mapping = ast
1709 .any_of_type(id)
1710 .ok()
1711 .and_then(|any| ast.facets(any))
1712 .and_then(|facets| facets.corba.as_ref())
1713 .and_then(|corba| corba.native_mapping.clone());
1714 out.push(c::Decl::Typedef(c::Typedef {
1715 doc: Doc::line("A native type. Opaque unless a facet named its mapping."),
1716 type_: match mapping {
1717 Some(spelling) => c::Type::Named(Ident::new(&spelling)),
1718 None => c::Type::Pointer(Box::new(c::Type::Void)),
1719 },
1720 name: Ident::new(&name),
1721 dims: Vec::new(),
1722 }));
1723 }
1724 // An object reference belongs to the RPC arc and an annotation
1725 // type has no runtime form at all — `zerodds_ast_layout` refuses
1726 // to plan either — so a C declaration would be a shape nothing
1727 // could serialise. Named here rather than passed over, so the
1728 // coverage table and this file say the same thing.
1729 TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => {
1730 return Ok(());
1731 }
1732 // `any` for the same reason and a different one: it is in the
1733 // model because Building Block Any is one of the fifteen, and
1734 // its content is decided at run time, so there is no C type to
1735 // declare for it. The codec refuses it by name — `body_of` in
1736 // `encode_c`, `decode_c` and `default_c` — rather than emitting
1737 // nothing and reporting success.
1738 TypeKind::Any | TypeKind::ValueBase => {
1739 return Ok(());
1740 }
1741 TypeKind::ValueBox { boxed } => {
1742 let Some(target) = boxed.resolved() else {
1743 return Ok(());
1744 };
1745 emitted.push(name.clone());
1746 out.push(c::Decl::Typedef(c::Typedef {
1747 doc: Doc::line("A boxed value: a pointer, absent when null."),
1748 type_: c::Type::Pointer(Box::new(c_member_type(ast, target, &name)?)),
1749 name: Ident::new(&name),
1750 dims: Vec::new(),
1751 }));
1752 }
1753 }
1754 // The declaration's own IDL name, in one place rather than in every
1755 // arm above: an arm that emitted nothing has already returned, so
1756 // reaching here is what "this declaration was written" means.
1757 if has_idl_name(&node.kind) && !node.name.is_empty() {
1758 out.push(idl_name(&name, &node.name));
1759 }
1760 Ok(())
1761}
1762
1763/// The `ZD_IDL_MEMBERS_<name>` macro for a declaration's named parts.
1764///
1765/// `pairs` is `(the C spelling, the IDL spelling)` in IDL declaration
1766/// order — the order the member IDs are assigned in
1767/// (XTypes 1.3 §7.3.1.2.1.1) — and the entries are written even where
1768/// the two spellings are the same, because a caller computing a member
1769/// ID needs every member's IDL name and not only the ones a keyword
1770/// moved.
1771///
1772/// [`None`] for a declaration with no named parts: an empty braced
1773/// initialiser is not C, and the only declarations that reach it are an
1774/// enumeration with no enumerators and a bitset that is all padding.
1775/// The file's module documentation carries why this is a macro and not
1776/// an object.
1777/// The table only exists where the escape is invertible.
1778///
1779/// Two members whose IDL names differ and whose spellings do not are
1780/// two entries with the same left half — the table would record the
1781/// collision rather than refuse it, and a reader recovering the IDL
1782/// name would get whichever entry it hit first. The check is here
1783/// because this is the function that holds both spellings for a whole
1784/// scope; nothing upstream sees more than one member at a time.
1785///
1786/// # Errors
1787/// [`MaterialiseError::SpellingCollision`] when two members reach one
1788/// spelling.
1789fn idl_members(
1790 name: &str,
1791 pairs: &[(String, String)],
1792) -> Result<Option<c::Decl>, MaterialiseError> {
1793 no_spelling_collision(
1794 pairs
1795 .iter()
1796 .map(|(spelled, declared)| (spelled.as_str(), declared.as_str())),
1797 "C",
1798 name,
1799 )?;
1800 if pairs.is_empty() {
1801 return Ok(None);
1802 }
1803 let entries = pairs
1804 .iter()
1805 .map(|(field, idl)| {
1806 c::Expr::Initializer(vec![
1807 c::Expr::Literal(Literal::Str(field.clone())),
1808 c::Expr::Literal(Literal::Str(idl.clone())),
1809 ])
1810 })
1811 .collect();
1812 Ok(Some(c::Decl::Define(c::Define {
1813 doc: Doc::lines(&[
1814 "The IDL spelling of each member, beside the spelling this header",
1815 "uses. `zd_member_name` is the record an entry fits.",
1816 ]),
1817 name: Ident::new(&format!("{IDL_MEMBERS_PREFIX}{name}")),
1818 value: Some(c::Expr::Initializer(entries)),
1819 })))
1820}
1821
1822/// The `ZD_IDL_NAME_<name>` macro for a declaration's own IDL name.
1823fn idl_name(name: &str, idl: &str) -> c::Decl {
1824 c::Decl::Define(c::Define {
1825 doc: Doc::line("The IDL spelling of the declaration above."),
1826 name: Ident::new(&format!("{IDL_NAME_PREFIX}{name}")),
1827 value: Some(c::Expr::Literal(Literal::Str(idl.to_string()))),
1828 })
1829}
1830
1831/// Whether a type's C name was made from an IDL identifier.
1832///
1833/// The collections are the reason this is asked: `zd_seq_int32_t` is a
1834/// name this file invented for a type IDL spells `sequence<long>` and
1835/// never named, so there is no IDL spelling to record for it. The named
1836/// kinds below all take their C name from
1837/// [`c::escape_keyword`] applied to an identifier the source wrote.
1838const fn has_idl_name(kind: &TypeKind) -> bool {
1839 matches!(
1840 kind,
1841 TypeKind::Structure { .. }
1842 | TypeKind::Valuetype { .. }
1843 | TypeKind::Union { .. }
1844 | TypeKind::Enumeration { .. }
1845 | TypeKind::Bitmask { .. }
1846 | TypeKind::Bitset { .. }
1847 | TypeKind::Alias { .. }
1848 | TypeKind::Native
1849 | TypeKind::ValueBox { .. }
1850 )
1851}
1852
1853/// `typedef struct <name> <name>;` followed by the definition.
1854///
1855/// The typedef comes first and names an incomplete type, which is what
1856/// lets a member refer to the aggregate before its body is closed —
1857/// C's answer to a self-referential declaration.
1858fn struct_typedef(name: &str, members: Vec<c::Member>, doc: Doc) -> Vec<c::Decl> {
1859 vec![
1860 c::Decl::Typedef(c::Typedef {
1861 doc: Doc::none(),
1862 type_: c::Type::StructTag(Ident::new(name)),
1863 name: Ident::new(name),
1864 dims: Vec::new(),
1865 }),
1866 c::Decl::Struct(c::Struct {
1867 body_text: zerodds_lang_cst::common::BodyText::none(),
1868 doc,
1869 name: Some(Ident::new(name)),
1870 members,
1871 }),
1872 ]
1873}
1874
1875/// `struct { uint32_t _maximum; uint32_t _length; T *_buffer; bool _release; }`, with its typedef
1876/// (the sequence of [C-MAP], CORBA C mapping §1.14, as XTypes 1.3 §7.5.1 reuses it).
1877///
1878/// A length beside a pointer and not a NUL-terminated run: a sequence of
1879/// `octet` may contain a zero, and a terminator would cut the sample
1880/// short without saying so. `_maximum` is the capacity of the buffer and `_release` says
1881/// whether the sequence owns it, as the map's fields do.
1882fn sequence_decl(name: &str, element: c::Type) -> Vec<c::Decl> {
1883 struct_typedef(
1884 name,
1885 vec![
1886 c::Member {
1887 doc: Doc::none(),
1888 type_: c::Type::Primitive(c::Primitive::UInt32),
1889 name: Ident::new("_maximum"),
1890 dims: Vec::new(),
1891 bits: None,
1892 },
1893 c::Member {
1894 doc: Doc::none(),
1895 type_: c::Type::Primitive(c::Primitive::UInt32),
1896 name: Ident::new("_length"),
1897 dims: Vec::new(),
1898 bits: None,
1899 },
1900 c::Member {
1901 doc: Doc::none(),
1902 type_: c::Type::Pointer(Box::new(element)),
1903 name: Ident::new("_buffer"),
1904 dims: Vec::new(),
1905 bits: None,
1906 },
1907 c::Member {
1908 doc: Doc::none(),
1909 type_: c::Type::Primitive(c::Primitive::Bool),
1910 name: Ident::new("_release"),
1911 dims: Vec::new(),
1912 bits: None,
1913 },
1914 ],
1915 Doc::line(
1916 "A sequence: a count and the elements. The buffer is the caller's — \
1917 nothing in the write path allocates.",
1918 ),
1919 )
1920}
1921
1922/// Whether the rules created a node of `type_name` for `node`.
1923///
1924/// The same two-hop walk the Rust materialiser does: a correspondence is
1925/// an ordinary node with a link to the anchor and a link to what was
1926/// created.
1927fn ghost_created_for(applied: &Applied, node: NodeId, type_name: &str) -> Option<GhostId> {
1928 let anchor = applied
1929 .origin
1930 .iter()
1931 .find(|(_, model)| **model == node)
1932 .map(|(ghost, _)| *ghost)?;
1933 let typ = applied.graph.types.lookup(type_name)?;
1934 applied
1935 .graph
1936 .nodes_of_type(typ)
1937 .map(|created| created.id)
1938 .find(|created| {
1939 applied.graph.connected(&anchor, created)
1940 || applied
1941 .graph
1942 .parts(&anchor)
1943 .any(|part| applied.graph.connected(&part.other, created))
1944 })
1945}
1946
1947/// The C literal a constant's value takes, behind the cast its declared
1948/// type puts in front of it.
1949///
1950/// The four string-shaped forms are all `const char *` in this binding —
1951/// `zd_string`, `zd_wstring` and `zd_fixed_P_S` are the same typedef —
1952/// so each becomes a string literal and the cast names which of the
1953/// three it is. That is not a widening: a `fixed` is a decimal and a
1954/// `wstring` is UTF-8 because that is how `runtime/c` takes them, and a
1955/// constant that spelled either differently from a member of the same
1956/// type would be a second answer to what the value is.
1957///
1958/// `long double` is refused by name. CORBA gives a `long double` literal
1959/// no spelling C can carry: the compiler's own `long double` is x87
1960/// 80-bit extended on x86-64, binary128 on aarch64 and a plain `double`
1961/// on macOS arm64, which is why [`FLOAT128`] is a struct and not that
1962/// type — and a struct has no literal. Emitting `((zdw_f128)1.5)` would
1963/// not compile; emitting a `double` would put a different number in the
1964/// header than the IDL wrote.
1965fn c_constant_literal(
1966 value: &zerodds_ast::ConstValue,
1967 type_: &c::Type,
1968 at: &str,
1969) -> Result<Literal, MaterialiseError> {
1970 use zerodds_ast::ConstValue;
1971 let refuse = |construct: &str| MaterialiseError::NoLanguageForm {
1972 construct: construct.to_string(),
1973 language: "C",
1974 at: at.to_string(),
1975 };
1976 Ok(match value {
1977 ConstValue::Boolean(flag) => Literal::Bool(*flag),
1978 // Through `integer_text` and not `Literal::int`: −2^63 has no
1979 // decimal in C, and a `const long long` carrying it is the one
1980 // value where the digits are read unsigned before the minus
1981 // applies. Every other value is its own decimal, which is what
1982 // `Literal::int` wrote.
1983 ConstValue::Integer(number) => Literal::Int(zerodds_lang_render::c::integer_text(*number)),
1984 ConstValue::Char(character) => Literal::Char(*character),
1985 // The code point as a number, because `wchar` is a `uint16_t`
1986 // here: a `'ä'` character literal in C is one byte of the
1987 // execution character set and not a code unit.
1988 ConstValue::WChar(point) => Literal::int(i128::from(*point)),
1989 ConstValue::Float { mantissa, exponent } => {
1990 if matches!(type_, c::Type::Named(name) if name.as_str() == FLOAT128) {
1991 return Err(refuse("a `long double` constant, which has no C literal"));
1992 }
1993 let mut text = constant::float_text(mantissa, exponent.as_deref());
1994 // `-Wconversion` is on for every generated file, and an
1995 // initialiser whose value is a `double` narrowing into a
1996 // `float` is exactly what it reports.
1997 if matches!(type_, c::Type::Primitive(c::Primitive::Float)) {
1998 text.push('f');
1999 }
2000 Literal::Float(text)
2001 }
2002 ConstValue::Fixed { digits, scale } => {
2003 Literal::Str(constant::fixed_decimal(digits, *scale))
2004 }
2005 ConstValue::String(text) => Literal::Str(text.clone()),
2006 ConstValue::WString(points) => Literal::Str(
2007 constant::wstring_utf8(points)
2008 .ok_or_else(|| refuse("a `wstring` constant holding a lone surrogate"))?,
2009 ),
2010 // `ConstValue` is `#[non_exhaustive]`, so a form added to the
2011 // model reaches here. Refused by name rather than given a
2012 // plausible default: a constant emitted as the wrong literal is
2013 // a header that disagrees with the IDL and compiles.
2014 _ => return Err(refuse("a constant form this binding has no literal for")),
2015 })
2016}