Skip to main content

zerodds_ast/
model.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The model façade. Spec §3.
4
5use crate::error::AstError;
6use crate::facet::Facets;
7use crate::node::AnyId;
8use crate::node::annotation::{
9    AnnotationDefNode, AnnotationMemberNode, BUILTIN_ANNOTATIONS, BUILTIN_MODULE_NAME,
10};
11use crate::node::constant::{
12    ConstExpr, ConstExprNode, ConstNode, ConstValue, EvalError, Literal, MAX_EXPR_DEPTH, UnaryOp,
13};
14use crate::node::deployment::{
15    DomainNode, EndpointNode, EndpointRole, LibraryNode, ParticipantNode, QosPolicy,
16    QosProfileNode, TopicNode,
17};
18use crate::node::exception::ExceptionNode;
19use crate::node::interface::{
20    AttributeNode, InterfaceKind, InterfaceNode, OperationNode, ParamDirection, ParamNode,
21};
22use crate::node::member::{
23    BitValueNode, BitfieldNode, CaseLabel, CaseNode, EnumeratorNode, MemberNode,
24};
25use crate::node::module::ModuleNode;
26use crate::node::type_::{Completeness, PrimitiveKind, TypeKind, TypeNode};
27use alloc::collections::BTreeMap;
28use alloc::string::{String, ToString};
29use alloc::vec::Vec;
30use zerodds_ast_core::{
31    Arena, Id, IdAllocator, IdSpace, NodeId, ObserverRegistry, Ref, ScopedName,
32};
33
34/// The common AST.
35///
36/// Nodes live in per-kind generational arenas; handles are the fast
37/// access path, `NodeId` the stable one. Spec §1, §3.
38pub struct Ast {
39    pub(crate) modules: Arena<ModuleNode>,
40    pub(crate) types: Arena<TypeNode>,
41    pub(crate) members: Arena<MemberNode>,
42    pub(crate) cases: Arena<CaseNode>,
43    pub(crate) enumerators: Arena<EnumeratorNode>,
44    pub(crate) bit_values: Arena<BitValueNode>,
45    pub(crate) bitfields: Arena<BitfieldNode>,
46    pub(crate) constants: Arena<ConstNode>,
47    pub(crate) exprs: Arena<ConstExprNode>,
48    pub(crate) interfaces: Arena<InterfaceNode>,
49    pub(crate) operations: Arena<OperationNode>,
50    pub(crate) params: Arena<ParamNode>,
51    pub(crate) attributes: Arena<AttributeNode>,
52    pub(crate) exceptions: Arena<ExceptionNode>,
53    pub(crate) annotation_defs: Arena<AnnotationDefNode>,
54    pub(crate) annotation_members: Arena<AnnotationMemberNode>,
55    pub(crate) domains: Arena<DomainNode>,
56    pub(crate) topics: Arena<TopicNode>,
57    pub(crate) qos_profiles: Arena<QosProfileNode>,
58    pub(crate) participants: Arena<ParticipantNode>,
59    pub(crate) endpoints: Arena<EndpointNode>,
60    pub(crate) libraries: Arena<LibraryNode>,
61
62    pub(crate) model_ids: IdAllocator,
63    pub(crate) builtin_ids: IdAllocator,
64    pub(crate) synthetic_ids: IdAllocator,
65
66    pub(crate) by_node_id: BTreeMap<NodeId, AnyId>,
67    pub(crate) by_scoped_name: zerodds_ast_core::Index<ScopedName>,
68    pub(crate) by_type_identifier: zerodds_ast_core::Index<zerodds_ast_core::TypeIdentifierKey>,
69    pub(crate) by_ros_name: zerodds_ast_core::Index<zerodds_ast_core::RosTypeName>,
70    pub(crate) by_repository_id: zerodds_ast_core::Index<zerodds_ast_core::RepositoryId>,
71    /// Nodes waiting on each unresolved lookup key, so a newly arrived
72    /// type resolves its waiters in one step. Spec §6.
73    pub(crate) by_pending: zerodds_ast_core::Index<zerodds_ast_core::PendingRef>,
74    pub(crate) observers: ObserverRegistry,
75    pub(crate) sequence: u64,
76    /// The transaction currently open, if any. Spec §5.1.
77    pub(crate) pending: Option<zerodds_ast_core::TxnLog>,
78    /// Nodes removed during the open transaction, kept so a rollback
79    /// can restore them without serialising on the success path.
80    pub(crate) stash: Vec<Option<crate::edit::Stashed>>,
81    /// Failure of the most recent auto-committed mutation.
82    pub(crate) last_commit_error: Option<AstError>,
83
84    root: Id<ModuleNode>,
85    builtin_module: Id<ModuleNode>,
86}
87
88/// Everything needed to declare a member.
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct MemberSpec {
91    /// Simple name.
92    pub name: String,
93    /// Declared type.
94    pub type_: Ref<TypeNode>,
95    /// Per-source information.
96    pub facets: Facets,
97}
98
99impl MemberSpec {
100    /// A member with default facets.
101    #[must_use]
102    pub fn new(name: &str, type_: Ref<TypeNode>) -> Self {
103        Self {
104            name: name.to_string(),
105            type_,
106            facets: Facets::synthetic("member created through the model API"),
107        }
108    }
109}
110
111impl Default for Ast {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117impl Ast {
118    /// A model containing the root module and the built-in declarations.
119    ///
120    /// Built-in identities come from [`IdSpace::BUILTIN`] and are
121    /// allocated in a fixed order, so they are identical in every model
122    /// and snapshots from different processes compare. Spec §3.6, §9.
123    #[must_use]
124    pub fn new() -> Self {
125        let mut ast = Self::empty();
126        ast.populate_builtins();
127        ast
128    }
129
130    pub(crate) fn empty_public() -> Self {
131        Self::empty()
132    }
133
134    fn empty() -> Self {
135        let mut modules = Arena::new();
136        let mut model_ids = IdAllocator::new(IdSpace::MODEL);
137        // The root module is the first model allocation, so its identity
138        // is the same in every model.
139        let root_id = model_ids
140            .allocate()
141            .unwrap_or_else(|_| NodeId::from_parts(IdSpace::MODEL, 1));
142        let root = modules
143            .insert(ModuleNode {
144                node_id: root_id,
145                name: String::new(),
146                parent: None,
147                children: Vec::new(),
148                facets: Facets::synthetic("root module"),
149            })
150            .unwrap_or(Id::first_at(0));
151
152        let mut builtin_ids = IdAllocator::new(IdSpace::BUILTIN);
153        let builtin_module_id = builtin_ids
154            .allocate()
155            .unwrap_or_else(|_| NodeId::from_parts(IdSpace::BUILTIN, 1));
156        let builtin_module = modules
157            .insert(ModuleNode {
158                node_id: builtin_module_id,
159                name: BUILTIN_MODULE_NAME.to_string(),
160                parent: None,
161                children: Vec::new(),
162                facets: Facets::builtin(),
163            })
164            .unwrap_or(Id::first_at(1));
165
166        let mut by_node_id = BTreeMap::new();
167        let _ = by_node_id.insert(root_id, AnyId::Module(root, root_id));
168        let _ = by_node_id.insert(
169            builtin_module_id,
170            AnyId::Module(builtin_module, builtin_module_id),
171        );
172
173        Self {
174            modules,
175            types: Arena::new(),
176            members: Arena::new(),
177            cases: Arena::new(),
178            enumerators: Arena::new(),
179            bit_values: Arena::new(),
180            bitfields: Arena::new(),
181            constants: Arena::new(),
182            exprs: Arena::new(),
183            interfaces: Arena::new(),
184            operations: Arena::new(),
185            params: Arena::new(),
186            attributes: Arena::new(),
187            exceptions: Arena::new(),
188            annotation_defs: Arena::new(),
189            annotation_members: Arena::new(),
190            domains: Arena::new(),
191            topics: Arena::new(),
192            qos_profiles: Arena::new(),
193            participants: Arena::new(),
194            endpoints: Arena::new(),
195            libraries: Arena::new(),
196            model_ids,
197            builtin_ids,
198            synthetic_ids: IdAllocator::new(IdSpace::SYNTHETIC),
199            by_node_id,
200            by_scoped_name: zerodds_ast_core::Index::new(),
201            by_type_identifier: zerodds_ast_core::Index::new(),
202            by_ros_name: zerodds_ast_core::Index::new(),
203            by_repository_id: zerodds_ast_core::Index::new(),
204            by_pending: zerodds_ast_core::Index::new(),
205            observers: ObserverRegistry::new(),
206            sequence: 0,
207            pending: None,
208            stash: Vec::new(),
209            last_commit_error: None,
210            root,
211            builtin_module,
212        }
213    }
214
215    fn populate_builtins(&mut self) {
216        for name in BUILTIN_ANNOTATIONS {
217            let Ok(node_id) = self.builtin_ids.allocate() else {
218                return;
219            };
220            let Ok(id) = self.annotation_defs.insert(AnnotationDefNode {
221                node_id,
222                name: name.to_string(),
223                module: self.builtin_module,
224                members: Vec::new(),
225                facets: Facets::builtin(),
226            }) else {
227                return;
228            };
229            let any = AnyId::AnnotationDef(id, node_id);
230            let _ = self.by_node_id.insert(node_id, any);
231            if let Ok(module) = self.modules.get_mut(self.builtin_module) {
232                module.children.push(any);
233            }
234            // Every built-in annotation carries the implicit `value`
235            // member that enables the `@ann(x)` short form.
236            let Ok(member_id) = self.builtin_ids.allocate() else {
237                return;
238            };
239            let Ok(member) = self.annotation_members.insert(AnnotationMemberNode {
240                node_id: member_id,
241                name: "value".to_string(),
242                type_: Ref::Pending(zerodds_ast_core::PendingRef::ScopedName(
243                    ScopedName::absolute(alloc::vec![name.to_string()]),
244                )),
245                default: None,
246                is_value_member: true,
247                parent: id,
248                facets: Facets::builtin(),
249            }) else {
250                return;
251            };
252            let _ = self
253                .by_node_id
254                .insert(member_id, AnyId::AnnotationMember(member, member_id));
255            if let Ok(def) = self.annotation_defs.get_mut(id) {
256                def.members.push(member);
257            }
258        }
259    }
260
261    /// The anonymous global scope.
262    #[must_use]
263    pub const fn root_module(&self) -> Id<ModuleNode> {
264        self.root
265    }
266
267    /// The module holding the built-in declarations.
268    #[must_use]
269    pub const fn builtin_module(&self) -> Id<ModuleNode> {
270        self.builtin_module
271    }
272
273    /// A built-in annotation by name, without the `@`.
274    #[must_use]
275    pub fn builtin_annotation(&self, name: &str) -> Option<Id<AnnotationDefNode>> {
276        self.modules
277            .get(self.builtin_module)
278            .ok()?
279            .children
280            .iter()
281            .filter_map(|child| match child {
282                AnyId::AnnotationDef(id, _) => Some(*id),
283                _ => None,
284            })
285            .find(|id| {
286                self.annotation_defs
287                    .get(*id)
288                    .is_ok_and(|def| def.name == name)
289            })
290    }
291
292    /// Map a stable identity back to its handle and kind.
293    #[must_use]
294    pub fn resolve_node_id(&self, node: NodeId) -> Option<AnyId> {
295        self.by_node_id.get(&node).copied()
296    }
297
298    /// The observer registry, for registering change consumers.
299    pub fn observers_mut(&mut self) -> &mut ObserverRegistry {
300        &mut self.observers
301    }
302
303    /// Total number of live nodes across every arena.
304    #[must_use]
305    pub fn node_count(&self) -> usize {
306        self.by_node_id.len()
307    }
308
309    /// The sequence number of the last committed changeset.
310    ///
311    /// Zero on a fresh model; every commit increments it, so a consumer
312    /// can tell whether it has seen every changeset. Spec §5.2.
313    #[must_use]
314    pub const fn commit_sequence(&self) -> u64 {
315        self.sequence
316    }
317
318    /// Allocate an identity in [`IdSpace::SYNTHETIC`].
319    ///
320    /// For nodes a tool fabricates that correspond to nothing in any
321    /// source — an implicit wrapper, a generated helper type. Keeping
322    /// them in their own namespace means a diff between a parsed model
323    /// and a fabricated one never confuses the two. Spec §1.2.
324    ///
325    /// # Errors
326    /// [`AstError::Core`] when the synthetic counter is exhausted.
327    pub fn allocate_synthetic_id(&mut self) -> Result<NodeId, AstError> {
328        Ok(self.synthetic_ids.allocate()?)
329    }
330
331    pub(crate) fn next_model_id(&mut self) -> Result<NodeId, AstError> {
332        Ok(self.model_ids.allocate()?)
333    }
334
335    /// Record that an aggregate's part list changed.
336    ///
337    /// Adding a member changes the member *and* the struct that holds
338    /// it; without this the struct's own consumers never hear about it.
339    pub(crate) fn note_parts_changed(&mut self, owner: NodeId) {
340        let change = zerodds_ast_core::Change::Updated {
341            node: owner,
342            field: crate::field::PARTS,
343            before: zerodds_ast_core::FieldValue::Absent,
344            after: zerodds_ast_core::FieldValue::Node(owner),
345        };
346        let undo = zerodds_ast_core::UndoRecord {
347            node: owner,
348            payload: zerodds_ast_core::UndoPayload::RestoreField {
349                field: crate::field::PARTS,
350                value: zerodds_ast_core::FieldValue::Absent,
351            },
352        };
353        if let Some(log) = self.pending.as_mut() {
354            log.record(undo, change);
355        } else if let Err(error) = self.record_standalone(undo, change) {
356            self.last_commit_error = Some(error);
357        }
358    }
359
360    pub(crate) fn register(&mut self, any: AnyId) {
361        let _ = self.by_node_id.insert(any.node_id(), any);
362        self.index_node(any);
363        self.index_pending(any);
364        // Emission is deliberately infallible here: the caller has
365        // already mutated, so a rejected invariant must surface at the
366        // caller's own Result, which every add_* returns. Auto-commit
367        // rejection is reported through `last_commit_error`.
368        if let Err(error) = self.record_created(any) {
369            self.last_commit_error = Some(error);
370        }
371    }
372
373    /// Hand back a mutation's result, or the rejection that undid it.
374    ///
375    /// A mutation outside a transaction validates on the spot and is
376    /// rolled back when it fails. Returning `Ok` with a handle to a node
377    /// that no longer exists would be the worst of both: the caller
378    /// believes it succeeded and holds nothing.
379    fn committed<T>(&mut self, value: T) -> Result<T, AstError> {
380        match self.last_commit_error.take() {
381            Some(error) => Err(error),
382            None => Ok(value),
383        }
384    }
385
386    /// The error of the most recent auto-committed mutation, if it was
387    /// rejected.
388    ///
389    /// A mutation made outside an explicit transaction validates on the
390    /// spot; when it fails, the model is rolled back and the failure is
391    /// recorded here rather than lost. Taking it clears it.
392    pub fn take_commit_error(&mut self) -> Option<AstError> {
393        self.last_commit_error.take()
394    }
395
396    // -- modules ---------------------------------------------------------
397
398    /// Borrow a module.
399    ///
400    /// # Errors
401    /// [`AstError::Core`] with a stale or unknown handle.
402    pub fn module(&self, id: Id<ModuleNode>) -> Result<&ModuleNode, AstError> {
403        Ok(self.modules.get(id)?)
404    }
405
406    /// Create the named child module, or return the existing one.
407    ///
408    /// This is what makes reopening a module across translation units
409    /// converge on one node. Spec §3.1.
410    ///
411    /// # Errors
412    /// [`AstError::Core`] if the parent handle is stale or identity
413    /// space is exhausted.
414    pub fn ensure_module(
415        &mut self,
416        parent: Id<ModuleNode>,
417        name: &str,
418    ) -> Result<Id<ModuleNode>, AstError> {
419        let existing = self
420            .modules
421            .get(parent)?
422            .children
423            .iter()
424            .filter_map(|child| child.as_module())
425            .find(|id| self.modules.get(*id).is_ok_and(|m| m.name == name));
426        if let Some(id) = existing {
427            return Ok(id);
428        }
429
430        let node_id = self.next_model_id()?;
431        let id = self.modules.insert(ModuleNode {
432            node_id,
433            name: name.to_string(),
434            parent: Some(parent),
435            children: Vec::new(),
436            facets: Facets::synthetic("module created through the model API"),
437        })?;
438        let any = AnyId::Module(id, node_id);
439        self.register(any);
440        self.modules.get_mut(parent)?.children.push(any);
441        self.committed(id)
442    }
443
444    /// Iterate every module in the model, in arena order.
445    pub fn modules(&self) -> impl Iterator<Item = (Id<ModuleNode>, &ModuleNode)> {
446        self.modules.iter()
447    }
448
449    /// Look a module up by its path from the root.
450    #[must_use]
451    pub fn module_by_path(&self, path: &ScopedName) -> Option<Id<ModuleNode>> {
452        let mut current = self.root;
453        for segment in path.segments() {
454            current = self
455                .modules
456                .get(current)
457                .ok()?
458                .children
459                .iter()
460                .filter_map(|child| child.as_module())
461                .find(|id| self.modules.get(*id).is_ok_and(|m| &m.name == segment))?;
462        }
463        Some(current)
464    }
465
466    // -- types -----------------------------------------------------------
467
468    /// Borrow a type.
469    ///
470    /// # Errors
471    /// [`AstError::Core`] with a stale or unknown handle.
472    pub fn type_(&self, id: Id<TypeNode>) -> Result<&TypeNode, AstError> {
473        Ok(self.types.get(id)?)
474    }
475
476    /// Declare a type in a module.
477    ///
478    /// # Errors
479    /// [`AstError::Core`] if the module handle is stale or identity
480    /// space is exhausted.
481    pub fn add_type(
482        &mut self,
483        module: Id<ModuleNode>,
484        name: &str,
485        kind: TypeKind,
486    ) -> Result<Id<TypeNode>, AstError> {
487        let node_id = self.next_model_id()?;
488        let id = self.types.insert(TypeNode {
489            node_id,
490            name: name.to_string(),
491            module,
492            scope: None,
493            kind,
494            completeness: Completeness::Complete,
495            facets: Facets::synthetic("type created through the model API"),
496        })?;
497        let any = AnyId::Type(id, node_id);
498        self.register(any);
499        self.modules.get_mut(module)?.children.push(any);
500        self.committed(id)
501    }
502
503    /// Declare a type whose body has not arrived yet.
504    ///
505    /// Not a separate node kind: completing it later is an update, so
506    /// handles taken now stay valid. Spec §3.2.
507    ///
508    /// # Errors
509    /// As [`Ast::add_type`].
510    pub fn add_forward_type(
511        &mut self,
512        module: Id<ModuleNode>,
513        name: &str,
514        kind: TypeKind,
515    ) -> Result<Id<TypeNode>, AstError> {
516        let id = self.add_type(module, name, kind)?;
517        self.types.get_mut(id)?.completeness = Completeness::Forward;
518        self.committed(id)
519    }
520
521    /// Give a forward-declared type its body.
522    ///
523    /// An update of the existing node, not a second one: every reference
524    /// taken while only the forward declaration existed stays valid, which
525    /// is why forwardness is a state rather than a kind. Spec §3.2.
526    ///
527    /// # Errors
528    /// [`AstError::AlreadyComplete`] if the type already has a body —
529    /// two definitions of one name are a duplicate, not a completion.
530    /// [`AstError::Core`] on a stale handle.
531    pub fn complete_type(&mut self, id: Id<TypeNode>, kind: TypeKind) -> Result<(), AstError> {
532        let node = self.types.get(id)?;
533        let node_id = node.node_id;
534        if node.completeness == Completeness::Complete {
535            return Err(AstError::AlreadyComplete { node: node_id });
536        }
537        // The old kind's pending references are about to disappear; the
538        // waiter index has to lose them or a later definition of that
539        // name would rewrite a member that no longer exists.
540        self.unindex_pending(AnyId::Type(id, node_id));
541
542        let node = self.types.get_mut(id)?;
543        node.kind = kind;
544        node.completeness = Completeness::Complete;
545
546        self.index_pending(AnyId::Type(id, node_id));
547        self.record_completeness_change(node_id);
548        Ok(())
549    }
550
551    fn record_completeness_change(&mut self, node: NodeId) {
552        let change = zerodds_ast_core::Change::Updated {
553            node,
554            field: crate::field::COMPLETENESS,
555            before: zerodds_ast_core::FieldValue::Bool(false),
556            after: zerodds_ast_core::FieldValue::Bool(true),
557        };
558        let undo = zerodds_ast_core::UndoRecord {
559            node,
560            payload: zerodds_ast_core::UndoPayload::RestoreField {
561                field: crate::field::COMPLETENESS,
562                value: zerodds_ast_core::FieldValue::Bool(false),
563            },
564        };
565        if let Some(log) = self.pending.as_mut() {
566            log.record(undo, change);
567        } else if let Err(error) = self.record_standalone(undo, change) {
568            self.last_commit_error = Some(error);
569        }
570    }
571
572    /// Iterate every type in the model, in arena order.
573    pub fn types(&self) -> impl Iterator<Item = (Id<TypeNode>, &TypeNode)> {
574        self.types.iter()
575    }
576
577    // -- members and aggregate parts -------------------------------------
578
579    /// Borrow a member.
580    ///
581    /// # Errors
582    /// [`AstError::Core`] with a stale or unknown handle.
583    pub fn member(&self, id: Id<MemberNode>) -> Result<&MemberNode, AstError> {
584        Ok(self.members.get(id)?)
585    }
586
587    /// Iterate every member in the model, in arena order.
588    pub fn members(&self) -> impl Iterator<Item = (Id<MemberNode>, &MemberNode)> {
589        self.members.iter()
590    }
591
592    /// Add a member to a struct or valuetype.
593    ///
594    /// # Errors
595    /// [`AstError::WrongKind`] if the target is not an aggregate that
596    /// holds members, [`AstError::Core`] on a stale handle.
597    pub fn add_member(
598        &mut self,
599        aggregate: Id<TypeNode>,
600        spec: MemberSpec,
601    ) -> Result<Id<MemberNode>, AstError> {
602        let parent_node_id = self.types.get(aggregate)?.node_id;
603        let parent = AnyId::Type(aggregate, parent_node_id);
604        let node_id = self.next_model_id()?;
605        let id = self.members.insert(MemberNode {
606            node_id,
607            name: spec.name,
608            type_: spec.type_,
609            parent,
610            facets: spec.facets,
611        })?;
612        self.register(AnyId::Member(id, node_id));
613        match &mut self.types.get_mut(aggregate)?.kind {
614            TypeKind::Structure { members, .. } | TypeKind::Valuetype { state: members, .. } => {
615                members.push(id);
616                self.note_parts_changed(parent_node_id);
617                self.committed(id)
618            }
619            other => {
620                let found = other.as_str();
621                let _ = self.members.remove(id);
622                let _ = self.by_node_id.remove(&node_id);
623                Err(AstError::NotAnAggregate {
624                    node: parent_node_id,
625                    found,
626                })
627            }
628        }
629    }
630
631    /// [`Self::add_member`] at a position among the aggregate's members.
632    ///
633    /// DDS-XTypes 1.3 §7.5.2.7.6: "any value greater than the current
634    /// largest index value in the type shall be taken to indicate that the
635    /// new member will become the last member"; a member at `index` and
636    /// those after it "shall be shifted up by a single index value".
637    ///
638    /// # Errors
639    /// As [`Self::add_member`].
640    pub fn add_member_at(
641        &mut self,
642        aggregate: Id<TypeNode>,
643        spec: MemberSpec,
644        index: usize,
645    ) -> Result<Id<MemberNode>, AstError> {
646        let id = self.add_member(aggregate, spec)?;
647        let parent_node_id = self.types.get(aggregate)?.node_id;
648        if let TypeKind::Structure { members, .. } | TypeKind::Valuetype { state: members, .. } =
649            &mut self.types.get_mut(aggregate)?.kind
650        {
651            // The member just pushed is last; it moves to its position.
652            members.pop();
653            let at = index.min(members.len());
654            members.insert(at, id);
655            self.note_parts_changed(parent_node_id);
656        }
657        Ok(id)
658    }
659
660    /// [`Self::add_case`] at a position among the union's cases, with the
661    /// rule of [`Self::add_member_at`].
662    ///
663    /// # Errors
664    /// As [`Self::add_case`].
665    pub fn add_case_at(
666        &mut self,
667        union: Id<TypeNode>,
668        labels: Vec<CaseLabel>,
669        spec: MemberSpec,
670        index: usize,
671    ) -> Result<Id<CaseNode>, AstError> {
672        let id = self.add_case(union, labels, spec)?;
673        let union_node_id = self.types.get(union)?.node_id;
674        if let TypeKind::Union { cases, .. } = &mut self.types.get_mut(union)?.kind {
675            cases.pop();
676            let at = index.min(cases.len());
677            cases.insert(at, id);
678            self.note_parts_changed(union_node_id);
679        }
680        Ok(id)
681    }
682
683    /// Add a member to an exception.
684    ///
685    /// # Errors
686    /// [`AstError::Core`] on a stale handle.
687    pub fn add_exception_member(
688        &mut self,
689        exception: Id<ExceptionNode>,
690        spec: MemberSpec,
691    ) -> Result<Id<MemberNode>, AstError> {
692        let parent_node_id = self.exceptions.get(exception)?.node_id;
693        let node_id = self.next_model_id()?;
694        let id = self.members.insert(MemberNode {
695            node_id,
696            name: spec.name,
697            type_: spec.type_,
698            parent: AnyId::Exception(exception, parent_node_id),
699            facets: spec.facets,
700        })?;
701        self.register(AnyId::Member(id, node_id));
702        self.exceptions.get_mut(exception)?.members.push(id);
703        self.note_parts_changed(parent_node_id);
704        self.committed(id)
705    }
706
707    /// Borrow a union case.
708    ///
709    /// # Errors
710    /// [`AstError::Core`] with a stale or unknown handle.
711    pub fn case(&self, id: Id<CaseNode>) -> Result<&CaseNode, AstError> {
712        Ok(self.cases.get(id)?)
713    }
714
715    /// Iterate every cases node in the model, in arena order.
716    pub fn cases(&self) -> impl Iterator<Item = (Id<CaseNode>, &CaseNode)> {
717        self.cases.iter()
718    }
719
720    /// Add a case to a union.
721    ///
722    /// A case carrying `CaseLabel::Default` is also recorded as the
723    /// union's default case.
724    ///
725    /// # Errors
726    /// [`AstError::NotAnAggregate`] if the target is not a union.
727    pub fn add_case(
728        &mut self,
729        union: Id<TypeNode>,
730        labels: Vec<CaseLabel>,
731        spec: MemberSpec,
732    ) -> Result<Id<CaseNode>, AstError> {
733        let union_node_id = self.types.get(union)?.node_id;
734        if !matches!(self.types.get(union)?.kind, TypeKind::Union { .. }) {
735            return Err(AstError::NotAnAggregate {
736                node: union_node_id,
737                found: self.types.get(union)?.kind.as_str(),
738            });
739        }
740
741        let member_node_id = self.next_model_id()?;
742        let member = self.members.insert(MemberNode {
743            node_id: member_node_id,
744            name: spec.name,
745            type_: spec.type_,
746            parent: AnyId::Type(union, union_node_id),
747            facets: spec.facets,
748        })?;
749        self.register(AnyId::Member(member, member_node_id));
750
751        let node_id = self.next_model_id()?;
752        let is_default = labels.iter().any(|l| l.is_default());
753        let id = self.cases.insert(CaseNode {
754            node_id,
755            labels,
756            member,
757            parent: union,
758            facets: Facets::synthetic("case created through the model API"),
759        })?;
760        self.register(AnyId::Case(id, node_id));
761
762        if let TypeKind::Union {
763            cases,
764            default_case,
765            ..
766        } = &mut self.types.get_mut(union)?.kind
767        {
768            cases.push(id);
769            if is_default {
770                *default_case = Some(id);
771            }
772        }
773        self.note_parts_changed(union_node_id);
774        self.committed(id)
775    }
776
777    /// Borrow an enumerator.
778    ///
779    /// # Errors
780    /// [`AstError::Core`] with a stale or unknown handle.
781    pub fn enumerator(&self, id: Id<EnumeratorNode>) -> Result<&EnumeratorNode, AstError> {
782        Ok(self.enumerators.get(id)?)
783    }
784
785    /// Iterate every enumerator in the model, in arena order.
786    pub fn enumerators(&self) -> impl Iterator<Item = (Id<EnumeratorNode>, &EnumeratorNode)> {
787        self.enumerators.iter()
788    }
789
790    /// Borrow an enumerator mutably.
791    ///
792    /// For `@value(n)`, which a frontend applies after the enumerator
793    /// exists.
794    ///
795    /// # Errors
796    /// [`AstError::Core`] with a stale or unknown handle.
797    pub fn enumerator_mut(
798        &mut self,
799        id: Id<EnumeratorNode>,
800    ) -> Result<&mut EnumeratorNode, AstError> {
801        Ok(self.enumerators.get_mut(id)?)
802    }
803
804    /// Add an enumerator to an enumeration.
805    ///
806    /// `value` is `None` for a positional enumerator and `Some` for an
807    /// explicit `@value(n)`; the two encode differently. Spec §3.3.
808    ///
809    /// # Errors
810    /// [`AstError::NotAnAggregate`] if the target is not an enumeration.
811    pub fn add_enumerator(
812        &mut self,
813        enumeration: Id<TypeNode>,
814        name: &str,
815        value: Option<Id<ConstExprNode>>,
816    ) -> Result<Id<EnumeratorNode>, AstError> {
817        let parent_node_id = self.types.get(enumeration)?.node_id;
818        if !matches!(
819            self.types.get(enumeration)?.kind,
820            TypeKind::Enumeration { .. }
821        ) {
822            return Err(AstError::NotAnAggregate {
823                node: parent_node_id,
824                found: self.types.get(enumeration)?.kind.as_str(),
825            });
826        }
827        let node_id = self.next_model_id()?;
828        let id = self.enumerators.insert(EnumeratorNode {
829            node_id,
830            name: name.to_string(),
831            value,
832            parent: enumeration,
833            facets: Facets::synthetic("enumerator created through the model API"),
834        })?;
835        self.register(AnyId::Enumerator(id, node_id));
836        if let TypeKind::Enumeration { enumerators } = &mut self.types.get_mut(enumeration)?.kind {
837            enumerators.push(id);
838        }
839        self.note_parts_changed(parent_node_id);
840        self.committed(id)
841    }
842
843    /// Borrow a bitmask bit.
844    ///
845    /// # Errors
846    /// [`AstError::Core`] with a stale or unknown handle.
847    pub fn bit_value(&self, id: Id<BitValueNode>) -> Result<&BitValueNode, AstError> {
848        Ok(self.bit_values.get(id)?)
849    }
850
851    /// Borrow a bitmask bit mutably.
852    ///
853    /// For `@position(k)`, which a frontend applies after the bit exists.
854    ///
855    /// # Errors
856    /// [`AstError::Core`] with a stale or unknown handle.
857    pub fn bit_value_mut(&mut self, id: Id<BitValueNode>) -> Result<&mut BitValueNode, AstError> {
858        Ok(self.bit_values.get_mut(id)?)
859    }
860
861    /// Iterate every bit values node in the model, in arena order.
862    pub fn bit_values(&self) -> impl Iterator<Item = (Id<BitValueNode>, &BitValueNode)> {
863        self.bit_values.iter()
864    }
865
866    /// Add a bit to a bitmask.
867    ///
868    /// `position` is `None` for a positional bit and `Some` for an
869    /// explicit `@position(k)`. Spec §3.3.
870    ///
871    /// # Errors
872    /// [`AstError::NotAnAggregate`] if the target is not a bitmask.
873    pub fn add_bit_value(
874        &mut self,
875        bitmask: Id<TypeNode>,
876        name: &str,
877        position: Option<u16>,
878    ) -> Result<Id<BitValueNode>, AstError> {
879        let parent_node_id = self.types.get(bitmask)?.node_id;
880        if !matches!(self.types.get(bitmask)?.kind, TypeKind::Bitmask { .. }) {
881            return Err(AstError::NotAnAggregate {
882                node: parent_node_id,
883                found: self.types.get(bitmask)?.kind.as_str(),
884            });
885        }
886        let node_id = self.next_model_id()?;
887        let id = self.bit_values.insert(BitValueNode {
888            node_id,
889            name: name.to_string(),
890            position,
891            parent: bitmask,
892            facets: Facets::synthetic("bit value created through the model API"),
893        })?;
894        self.register(AnyId::BitValue(id, node_id));
895        if let TypeKind::Bitmask { bits } = &mut self.types.get_mut(bitmask)?.kind {
896            bits.push(id);
897        }
898        self.note_parts_changed(parent_node_id);
899        self.committed(id)
900    }
901
902    /// Borrow a bitset field.
903    ///
904    /// # Errors
905    /// [`AstError::Core`] with a stale or unknown handle.
906    pub fn bitfield(&self, id: Id<BitfieldNode>) -> Result<&BitfieldNode, AstError> {
907        Ok(self.bitfields.get(id)?)
908    }
909
910    /// Add a field to a bitset. `name` is `None` for anonymous padding.
911    ///
912    /// # Errors
913    /// [`AstError::NotAnAggregate`] if the target is not a bitset.
914    pub fn add_bitfield(
915        &mut self,
916        bitset: Id<TypeNode>,
917        name: Option<&str>,
918        bits: u16,
919        holder_type: Option<Ref<TypeNode>>,
920    ) -> Result<Id<BitfieldNode>, AstError> {
921        let parent_node_id = self.types.get(bitset)?.node_id;
922        if !matches!(self.types.get(bitset)?.kind, TypeKind::Bitset { .. }) {
923            return Err(AstError::NotAnAggregate {
924                node: parent_node_id,
925                found: self.types.get(bitset)?.kind.as_str(),
926            });
927        }
928        let node_id = self.next_model_id()?;
929        let id = self.bitfields.insert(BitfieldNode {
930            node_id,
931            name: name.map(ToString::to_string),
932            bits,
933            holder_type,
934            parent: bitset,
935            facets: Facets::synthetic("bitfield created through the model API"),
936        })?;
937        self.register(AnyId::Bitfield(id, node_id));
938        if let TypeKind::Bitset { fields, .. } = &mut self.types.get_mut(bitset)?.kind {
939            fields.push(id);
940        }
941        self.note_parts_changed(parent_node_id);
942        self.committed(id)
943    }
944
945    // -- constants -------------------------------------------------------
946
947    /// Borrow an expression node.
948    ///
949    /// # Errors
950    /// [`AstError::Core`] with a stale or unknown handle.
951    pub fn expr(&self, id: Id<ConstExprNode>) -> Result<&ConstExprNode, AstError> {
952        Ok(self.exprs.get(id)?)
953    }
954
955    /// Add an expression node.
956    ///
957    /// # Errors
958    /// [`AstError::Core`] when identity or slot space is exhausted.
959    pub fn add_expr(&mut self, expr: ConstExpr) -> Result<Id<ConstExprNode>, AstError> {
960        let node_id = self.next_model_id()?;
961        let id = self.exprs.insert(ConstExprNode {
962            node_id,
963            expr,
964            facets: Facets::synthetic("expression created through the model API"),
965        })?;
966        self.register(AnyId::ConstExpr(id, node_id));
967        self.committed(id)
968    }
969
970    /// Add an integer literal expression, the common case.
971    ///
972    /// # Errors
973    /// As [`Ast::add_expr`].
974    pub fn add_int_expr(&mut self, value: i128) -> Result<Id<ConstExprNode>, AstError> {
975        self.add_expr(ConstExpr::Literal(Literal::Integer(value)))
976    }
977
978    /// Borrow a constant.
979    ///
980    /// # Errors
981    /// [`AstError::Core`] with a stale or unknown handle.
982    pub fn constant(&self, id: Id<ConstNode>) -> Result<&ConstNode, AstError> {
983        Ok(self.constants.get(id)?)
984    }
985
986    /// Iterate every constants node in the model, in arena order.
987    pub fn constants(&self) -> impl Iterator<Item = (Id<ConstNode>, &ConstNode)> {
988        self.constants.iter()
989    }
990
991    /// Declare a constant in a module.
992    ///
993    /// # Errors
994    /// [`AstError::Core`] on a stale module handle.
995    pub fn add_const(
996        &mut self,
997        module: Id<ModuleNode>,
998        name: &str,
999        type_: Ref<TypeNode>,
1000        expr: Id<ConstExprNode>,
1001    ) -> Result<Id<ConstNode>, AstError> {
1002        let node_id = self.next_model_id()?;
1003        let id = self.constants.insert(ConstNode {
1004            node_id,
1005            name: name.to_string(),
1006            module,
1007            scope: None,
1008            type_,
1009            expr,
1010            facets: Facets::synthetic("constant created through the model API"),
1011        })?;
1012        let any = AnyId::Const(id, node_id);
1013        self.register(any);
1014        self.modules.get_mut(module)?.children.push(any);
1015        self.committed(id)
1016    }
1017
1018    /// Evaluate an expression to an integral value.
1019    ///
1020    /// The tree is not mutated: evaluation is a query, so the written
1021    /// form stays available to the formatter and the linter. Spec §3.5.
1022    ///
1023    /// # Errors
1024    /// [`EvalError::Unresolved`] when the tree reaches a reference that
1025    /// is not resolved — a partially discovered model fails loudly
1026    /// rather than substituting a default. Also
1027    /// [`EvalError::DivisionByZero`], [`EvalError::Overflow`],
1028    /// [`EvalError::InvalidShift`], [`EvalError::NotIntegral`],
1029    /// [`EvalError::DanglingExpr`] and [`EvalError::TooDeep`].
1030    pub fn eval_integral(&self, expr: Id<ConstExprNode>) -> Result<i128, EvalError> {
1031        self.eval_integral_at(expr, 0)
1032    }
1033
1034    /// Evaluate an expression to its value, whatever kind that is.
1035    ///
1036    /// [`Self::eval_integral`] is the right question wherever the wire
1037    /// needs a number, and the wrong one wherever a backend only needs to
1038    /// know what a constant says. `const string MODULE_ID = "…";` is
1039    /// evaluated and has no integer, so an integral evaluator reports
1040    /// `NotIntegral` and a caller that has nothing else to ask cannot emit
1041    /// the constant at all.
1042    ///
1043    /// An expression with an operator in it is integral and comes back as
1044    /// [`ConstValue::Integer`], unless a floating-point operand stands in
1045    /// it: then it is evaluated in `double` as IDL 4.2 §7.4.1.4.2 says and
1046    /// comes back as [`ConstValue::Float`]; a fixed-point operand makes it
1047    /// exact decimal arithmetic after Table 7-11, back as
1048    /// [`ConstValue::Fixed`].
1049    ///
1050    /// # Errors
1051    /// As [`Self::eval_integral`]; for floating arithmetic also
1052    /// [`EvalError::MixedOperands`], [`EvalError::NotFloatingOperator`]
1053    /// and [`EvalError::LongDouble`], for fixed-point arithmetic
1054    /// [`EvalError::NotFixedOperator`]. A literal alone always has a value.
1055    pub fn eval_const(&self, expr: Id<ConstExprNode>) -> Result<ConstValue, EvalError> {
1056        self.eval_const_at(expr, expr, 0)
1057    }
1058
1059    /// `root` is the whole expression `expr` is part of: the constant it
1060    /// belongs to, if any, decides the precision of floating arithmetic.
1061    fn eval_const_at(
1062        &self,
1063        expr: Id<ConstExprNode>,
1064        root: Id<ConstExprNode>,
1065        depth: usize,
1066    ) -> Result<ConstValue, EvalError> {
1067        if depth > MAX_EXPR_DEPTH {
1068            return Err(EvalError::TooDeep);
1069        }
1070        let node = self.exprs.get(expr).map_err(|_| EvalError::DanglingExpr)?;
1071        let at = node.node_id;
1072        match &node.expr {
1073            ConstExpr::Literal(Literal::Boolean(value)) => Ok(ConstValue::Boolean(*value)),
1074            ConstExpr::Literal(Literal::Integer(value)) => Ok(ConstValue::Integer(*value)),
1075            ConstExpr::Literal(Literal::Char(value)) => Ok(ConstValue::Char(*value)),
1076            ConstExpr::Literal(Literal::WChar(value)) => Ok(ConstValue::WChar(*value)),
1077            ConstExpr::Literal(Literal::String(text)) => Ok(ConstValue::String(text.clone())),
1078            ConstExpr::Literal(Literal::WString(units)) => Ok(ConstValue::WString(units.clone())),
1079            ConstExpr::Literal(Literal::Float { mantissa, exponent }) => Ok(ConstValue::Float {
1080                mantissa: mantissa.clone(),
1081                exponent: exponent.clone(),
1082            }),
1083            ConstExpr::Literal(Literal::Fixed { digits, scale }) => Ok(ConstValue::Fixed {
1084                digits: digits.clone(),
1085                scale: *scale,
1086            }),
1087            ConstExpr::Literal(Literal::Enumerator(_)) => {
1088                self.eval_integral_at(expr, depth).map(ConstValue::Integer)
1089            }
1090            ConstExpr::NameRef(reference) => {
1091                let target = reference.resolved().ok_or(EvalError::Unresolved { at })?;
1092                let constant = self
1093                    .constants
1094                    .get(target)
1095                    .map_err(|_| EvalError::DanglingExpr)?;
1096                self.eval_const_at(constant.expr, constant.expr, depth + 1)
1097            }
1098            // A sign in front of a floating literal, before the integral
1099            // evaluator gets it.
1100            //
1101            // Every unary and binary node used to go straight to
1102            // `eval_integral_at`, whatever its operands were, so
1103            // `const double SLOPE = -1.5;` asked for the integer value
1104            // of a `Float` literal and came back "does not evaluate".
1105            // IDL 4.2 §7.4.1.4.2 has `<const_exp>` over the floating
1106            // types as much as over the integer ones, and the Ada column
1107            // hit this the moment it wanted a negative constant in a
1108            // sample.
1109            //
1110            // Only the sign is answered here, and answering it needs no
1111            // arithmetic at all: `ConstValue::Float` keeps the mantissa
1112            // as the text the source wrote, so a `-` is one character in
1113            // front of it. Nothing is rounded and nothing is parsed; the
1114            // rest of floating-point arithmetic, which does round, is
1115            // `eval_floating_root` below.
1116            ConstExpr::Unary {
1117                op: op @ (UnaryOp::Minus | UnaryOp::Plus),
1118                operand,
1119            } => {
1120                let inner = self.eval_const_at(*operand, root, depth + 1)?;
1121                match inner {
1122                    ConstValue::Float { mantissa, exponent } if *op == UnaryOp::Minus => {
1123                        Ok(ConstValue::Float {
1124                            mantissa: match mantissa.strip_prefix('-') {
1125                                // `- -1.5` is `1.5`: two signs cancel
1126                                // rather than stacking into `--1.5`,
1127                                // which no reader of the text would take
1128                                // for a number.
1129                                Some(rest) => rest.to_string(),
1130                                None => alloc::format!("-{mantissa}"),
1131                            },
1132                            exponent,
1133                        })
1134                    }
1135                    ConstValue::Float { .. } => Ok(inner),
1136                    // A fixed-point value's sign is text as well: "Prefix
1137                    // operators do not affect the precision" (§7.4.1.4.2).
1138                    ConstValue::Fixed { digits, scale } => {
1139                        let value = crate::decimal::Decimal::parse(&digits, scale)
1140                            .map_err(|_| EvalError::NotIntegral { at })?;
1141                        let value = if *op == UnaryOp::Minus {
1142                            value.negated()
1143                        } else {
1144                            value
1145                        };
1146                        let (digits, scale) =
1147                            value.to_parts().map_err(|_| EvalError::Overflow { at })?;
1148                        Ok(ConstValue::Fixed { digits, scale })
1149                    }
1150                    _ => self.eval_integral_at(expr, depth).map(ConstValue::Integer),
1151                }
1152            }
1153            ConstExpr::Unary { .. } | ConstExpr::Binary { .. } => {
1154                if self.is_floating_at(expr, depth)? {
1155                    self.eval_floating_root(expr, root, depth)
1156                } else if self.is_fixed_at(expr, depth)? {
1157                    self.eval_fixed_root(expr, root, depth)
1158                } else {
1159                    self.eval_integral_at(expr, depth).map(ConstValue::Integer)
1160                }
1161            }
1162        }
1163    }
1164
1165    /// Whether an expression is floating-point arithmetic: a floating
1166    /// literal, or a constant that evaluates to one, stands among its
1167    /// operands. Mixtures are decided later, by
1168    /// [`Self::eval_floating_at`], which refuses them.
1169    fn is_floating_at(&self, expr: Id<ConstExprNode>, depth: usize) -> Result<bool, EvalError> {
1170        if depth > MAX_EXPR_DEPTH {
1171            return Err(EvalError::TooDeep);
1172        }
1173        let node = self.exprs.get(expr).map_err(|_| EvalError::DanglingExpr)?;
1174        match &node.expr {
1175            ConstExpr::Literal(Literal::Float { .. }) => Ok(true),
1176            ConstExpr::Literal(_) => Ok(false),
1177            ConstExpr::NameRef(reference) => {
1178                let target = reference
1179                    .resolved()
1180                    .ok_or(EvalError::Unresolved { at: node.node_id })?;
1181                let constant = self
1182                    .constants
1183                    .get(target)
1184                    .map_err(|_| EvalError::DanglingExpr)?;
1185                self.is_floating_at(constant.expr, depth + 1)
1186            }
1187            ConstExpr::Unary { operand, .. } => self.is_floating_at(*operand, depth + 1),
1188            ConstExpr::Binary { lhs, rhs, .. } => Ok(
1189                self.is_floating_at(*lhs, depth + 1)? || self.is_floating_at(*rhs, depth + 1)?
1190            ),
1191        }
1192    }
1193
1194    /// Whether an expression is fixed-point arithmetic: a fixed-point
1195    /// literal, or a constant that evaluates to one, stands among its
1196    /// operands. Mixtures are refused by [`Self::eval_fixed_at`].
1197    fn is_fixed_at(&self, expr: Id<ConstExprNode>, depth: usize) -> Result<bool, EvalError> {
1198        if depth > MAX_EXPR_DEPTH {
1199            return Err(EvalError::TooDeep);
1200        }
1201        let node = self.exprs.get(expr).map_err(|_| EvalError::DanglingExpr)?;
1202        match &node.expr {
1203            ConstExpr::Literal(Literal::Fixed { .. }) => Ok(true),
1204            ConstExpr::Literal(_) => Ok(false),
1205            ConstExpr::NameRef(reference) => {
1206                let target = reference
1207                    .resolved()
1208                    .ok_or(EvalError::Unresolved { at: node.node_id })?;
1209                let constant = self
1210                    .constants
1211                    .get(target)
1212                    .map_err(|_| EvalError::DanglingExpr)?;
1213                self.is_fixed_at(constant.expr, depth + 1)
1214            }
1215            ConstExpr::Unary { operand, .. } => self.is_fixed_at(*operand, depth + 1),
1216            ConstExpr::Binary { lhs, rhs, .. } => {
1217                Ok(self.is_fixed_at(*lhs, depth + 1)? || self.is_fixed_at(*rhs, depth + 1)?)
1218            }
1219        }
1220    }
1221
1222    /// Fixed-point arithmetic, IDL 4.2 §7.4.1.4.2 and Table 7-11: each
1223    /// pairwise computation exact, a result of more than 31 significant
1224    /// digits cut to 31 on the right with no rounding, a quotient run to
1225    /// 31 significant digits. A constant of a `fixed<D,S>` type keeps
1226    /// what fits: places past `S` are discarded ("Truncation on the right
1227    /// for fixed point types is OK") and more than `D - S` whole digits
1228    /// are an [`EvalError::Overflow`] ("too large to fit … shall be
1229    /// treated as an error").
1230    fn eval_fixed_root(
1231        &self,
1232        expr: Id<ConstExprNode>,
1233        root: Id<ConstExprNode>,
1234        depth: usize,
1235    ) -> Result<ConstValue, EvalError> {
1236        let at = self
1237            .exprs
1238            .get(expr)
1239            .map_err(|_| EvalError::DanglingExpr)?
1240            .node_id;
1241        let mut value = self.eval_fixed_at(expr, depth)?;
1242        let declared = match self
1243            .constants
1244            .iter()
1245            .find(|(_, constant)| constant.expr == root)
1246        {
1247            Some((_, constant)) => self.fixed_type_of(&constant.type_, at)?,
1248            None => None,
1249        };
1250        if let Some((digits, scale)) = declared {
1251            value = value
1252                .fitted(digits, scale)
1253                .ok_or(EvalError::Overflow { at })?;
1254        }
1255        let (digits, scale) = value.to_parts().map_err(|_| EvalError::Overflow { at })?;
1256        Ok(ConstValue::Fixed { digits, scale })
1257    }
1258
1259    /// `fixed<D,S>` of a constant's type, through typedefs; `None` for any
1260    /// other type.
1261    fn fixed_type_of(&self, ty: &Ref<TypeNode>, at: NodeId) -> Result<Option<(u8, u8)>, EvalError> {
1262        let mut next = ty.resolved().ok_or(EvalError::Unresolved { at })?;
1263        for _ in 0..MAX_EXPR_DEPTH {
1264            let node = self.types.get(next).map_err(|_| EvalError::DanglingExpr)?;
1265            match &node.kind {
1266                TypeKind::Fixed { digits, scale } => return Ok(Some((*digits, *scale))),
1267                TypeKind::Alias { aliased } => {
1268                    next = aliased.resolved().ok_or(EvalError::Unresolved { at })?;
1269                }
1270                _ => return Ok(None),
1271            }
1272        }
1273        Err(EvalError::TooDeep)
1274    }
1275
1276    fn eval_fixed_at(
1277        &self,
1278        expr: Id<ConstExprNode>,
1279        depth: usize,
1280    ) -> Result<crate::decimal::Decimal, EvalError> {
1281        use crate::decimal::{Decimal, DecimalError};
1282        if depth > MAX_EXPR_DEPTH {
1283            return Err(EvalError::TooDeep);
1284        }
1285        let node = self.exprs.get(expr).map_err(|_| EvalError::DanglingExpr)?;
1286        let at = node.node_id;
1287        let failed = |error: DecimalError| match error {
1288            DecimalError::DivisionByZero => EvalError::DivisionByZero { at },
1289            DecimalError::TooLarge => EvalError::Overflow { at },
1290            DecimalError::NotDecimal => EvalError::NotIntegral { at },
1291        };
1292        match &node.expr {
1293            ConstExpr::Literal(Literal::Fixed { digits, scale }) => {
1294                Decimal::parse(digits, *scale).map_err(failed)
1295            }
1296            ConstExpr::Literal(_) => Err(EvalError::MixedOperands { at }),
1297            ConstExpr::NameRef(reference) => {
1298                let target = reference.resolved().ok_or(EvalError::Unresolved { at })?;
1299                let constant = self
1300                    .constants
1301                    .get(target)
1302                    .map_err(|_| EvalError::DanglingExpr)?;
1303                self.eval_fixed_at(constant.expr, depth + 1)
1304            }
1305            ConstExpr::Unary { op, operand } => {
1306                let value = self.eval_fixed_at(*operand, depth + 1)?;
1307                match op {
1308                    UnaryOp::Plus => Ok(value),
1309                    UnaryOp::Minus => Ok(value.negated()),
1310                    UnaryOp::Not => Err(EvalError::NotFixedOperator { at }),
1311                }
1312            }
1313            ConstExpr::Binary { op, lhs, rhs } => {
1314                use crate::node::constant::BinaryOp as B;
1315                let left = self.eval_fixed_at(*lhs, depth + 1)?;
1316                let right = self.eval_fixed_at(*rhs, depth + 1)?;
1317                match op {
1318                    B::Add => left.add(&right).map_err(failed),
1319                    B::Sub => left.subtract(&right).map_err(failed),
1320                    B::Mul => left.multiply(&right).map_err(failed),
1321                    B::Div => left.divide(&right).map_err(failed),
1322                    B::Or | B::Xor | B::And | B::Shl | B::Shr | B::Mod => {
1323                        Err(EvalError::NotFixedOperator { at })
1324                    }
1325                }
1326            }
1327        }
1328    }
1329
1330    /// Floating-point arithmetic, IDL 4.2 §7.4.1.4.2: "If the type of a
1331    /// floating-point constant is double, then each sub-expression of the
1332    /// associated constant expression is treated as a double", and "If
1333    /// either argument is long double, it shall use long double. Otherwise
1334    /// it shall use double." A `float` constant is no exception — the
1335    /// clause has no float arithmetic — and its final value has to fit
1336    /// the `float` range.
1337    ///
1338    /// The result is the shortest decimal that reads back as the same
1339    /// `double` (`{:?}` on `f64`), split into the two halves
1340    /// [`ConstValue::Float`] keeps.
1341    fn eval_floating_root(
1342        &self,
1343        expr: Id<ConstExprNode>,
1344        root: Id<ConstExprNode>,
1345        depth: usize,
1346    ) -> Result<ConstValue, EvalError> {
1347        let at = self
1348            .exprs
1349            .get(expr)
1350            .map_err(|_| EvalError::DanglingExpr)?
1351            .node_id;
1352        // The constant this expression is the value of; an expression
1353        // that belongs to none (an annotation parameter, a case label) is
1354        // evaluated in `double`, which is what the clause gives a
1355        // sub-expression without a long double operand.
1356        let declared = match self
1357            .constants
1358            .iter()
1359            .find(|(_, constant)| constant.expr == root)
1360        {
1361            Some((_, constant)) => self.floating_kind_of(&constant.type_, at)?,
1362            None => None,
1363        };
1364        if declared == Some(PrimitiveKind::Float128) {
1365            return Err(EvalError::LongDouble { at });
1366        }
1367        let value = self.eval_floating_at(expr, depth)?;
1368        if declared == Some(PrimitiveKind::Float32) && value.abs() > f64::from(f32::MAX) {
1369            return Err(EvalError::Overflow { at });
1370        }
1371        let text = alloc::format!("{value:?}");
1372        Ok(match text.split_once('e') {
1373            Some((mantissa, exponent)) => ConstValue::Float {
1374                mantissa: mantissa.to_string(),
1375                exponent: Some(exponent.to_string()),
1376            },
1377            None => ConstValue::Float {
1378                mantissa: text,
1379                exponent: None,
1380            },
1381        })
1382    }
1383
1384    /// The floating-point kind a constant's type is, through typedefs;
1385    /// `None` for any other type.
1386    ///
1387    /// # Errors
1388    /// [`EvalError::Unresolved`] at `at` when the type or a typedef on the
1389    /// way is not resolved: whether the arithmetic may run in `double`
1390    /// depends on it, and a partial model is not taken for a `double`
1391    /// one. [`EvalError::DanglingExpr`] for a stale handle and
1392    /// [`EvalError::TooDeep`] for a typedef chain longer than evaluation
1393    /// follows.
1394    fn floating_kind_of(
1395        &self,
1396        ty: &Ref<TypeNode>,
1397        at: NodeId,
1398    ) -> Result<Option<PrimitiveKind>, EvalError> {
1399        let mut next = ty.resolved().ok_or(EvalError::Unresolved { at })?;
1400        for _ in 0..MAX_EXPR_DEPTH {
1401            let node = self.types.get(next).map_err(|_| EvalError::DanglingExpr)?;
1402            match &node.kind {
1403                TypeKind::Primitive(
1404                    kind @ (PrimitiveKind::Float32
1405                    | PrimitiveKind::Float64
1406                    | PrimitiveKind::Float128),
1407                ) => return Ok(Some(*kind)),
1408                TypeKind::Alias { aliased } => {
1409                    next = aliased.resolved().ok_or(EvalError::Unresolved { at })?;
1410                }
1411                _ => return Ok(None),
1412            }
1413        }
1414        Err(EvalError::TooDeep)
1415    }
1416
1417    fn eval_floating_at(&self, expr: Id<ConstExprNode>, depth: usize) -> Result<f64, EvalError> {
1418        if depth > MAX_EXPR_DEPTH {
1419            return Err(EvalError::TooDeep);
1420        }
1421        let node = self.exprs.get(expr).map_err(|_| EvalError::DanglingExpr)?;
1422        let at = node.node_id;
1423        let finite = |value: f64| {
1424            if value.is_finite() {
1425                Ok(value)
1426            } else {
1427                Err(EvalError::Overflow { at })
1428            }
1429        };
1430        match &node.expr {
1431            ConstExpr::Literal(Literal::Float { mantissa, exponent }) => {
1432                let text = match exponent {
1433                    Some(exponent) => alloc::format!("{mantissa}e{exponent}"),
1434                    None => mantissa.clone(),
1435                };
1436                let value: f64 = text.parse().map_err(|_| EvalError::NotIntegral { at })?;
1437                // "Constant floating point literals are considered double
1438                // unless the value is too large, then they are considered
1439                // long double."
1440                if value.is_finite() {
1441                    Ok(value)
1442                } else {
1443                    Err(EvalError::LongDouble { at })
1444                }
1445            }
1446            ConstExpr::Literal(_) => Err(EvalError::MixedOperands { at }),
1447            ConstExpr::NameRef(reference) => {
1448                let target = reference.resolved().ok_or(EvalError::Unresolved { at })?;
1449                let constant = self
1450                    .constants
1451                    .get(target)
1452                    .map_err(|_| EvalError::DanglingExpr)?;
1453                if self.floating_kind_of(&constant.type_, at)? == Some(PrimitiveKind::Float128) {
1454                    return Err(EvalError::LongDouble { at });
1455                }
1456                self.eval_floating_at(constant.expr, depth + 1)
1457            }
1458            ConstExpr::Unary { op, operand } => {
1459                let value = self.eval_floating_at(*operand, depth + 1)?;
1460                match op {
1461                    UnaryOp::Plus => Ok(value),
1462                    UnaryOp::Minus => Ok(-value),
1463                    UnaryOp::Not => Err(EvalError::NotFloatingOperator { at }),
1464                }
1465            }
1466            ConstExpr::Binary { op, lhs, rhs } => {
1467                use crate::node::constant::BinaryOp as B;
1468                let left = self.eval_floating_at(*lhs, depth + 1)?;
1469                let right = self.eval_floating_at(*rhs, depth + 1)?;
1470                match op {
1471                    B::Add => finite(left + right),
1472                    B::Sub => finite(left - right),
1473                    B::Mul => finite(left * right),
1474                    B::Div if right == 0.0 => Err(EvalError::DivisionByZero { at }),
1475                    B::Div => finite(left / right),
1476                    B::Or | B::Xor | B::And | B::Shl | B::Shr | B::Mod => {
1477                        Err(EvalError::NotFloatingOperator { at })
1478                    }
1479                }
1480            }
1481        }
1482    }
1483
1484    fn eval_integral_at(&self, expr: Id<ConstExprNode>, depth: usize) -> Result<i128, EvalError> {
1485        if depth > MAX_EXPR_DEPTH {
1486            return Err(EvalError::TooDeep);
1487        }
1488        let node = self.exprs.get(expr).map_err(|_| EvalError::DanglingExpr)?;
1489        let at = node.node_id;
1490        match &node.expr {
1491            ConstExpr::Literal(Literal::Integer(value)) => Ok(*value),
1492            ConstExpr::Literal(Literal::Boolean(value)) => Ok(i128::from(*value)),
1493            ConstExpr::Literal(Literal::Char(value)) => Ok(i128::from(u32::from(*value))),
1494            ConstExpr::Literal(Literal::WChar(value)) => Ok(i128::from(*value)),
1495            // An enumerator's value is not an expression of its own: it
1496            // is `@value(n)` or its position among its siblings. OMG IDL
1497            // 4.2 §7.4.1.4.2 admits it wherever a `<const_expr>` is
1498            // admitted, and §7.4.1.4.4.4.2 rule (54) makes a union's
1499            // `<case_label>` one of those places.
1500            ConstExpr::Literal(Literal::Enumerator(reference)) => {
1501                let target = reference.resolved().ok_or(EvalError::Unresolved { at })?;
1502                self.effective_enumerator_value(target)
1503                    .ok_or(EvalError::Unresolved { at })
1504            }
1505            ConstExpr::Literal(_) => Err(EvalError::NotIntegral { at }),
1506            ConstExpr::NameRef(reference) => {
1507                let target = reference.resolved().ok_or(EvalError::Unresolved { at })?;
1508                let constant = self
1509                    .constants
1510                    .get(target)
1511                    .map_err(|_| EvalError::DanglingExpr)?;
1512                self.eval_integral_at(constant.expr, depth + 1)
1513            }
1514            ConstExpr::Unary { op, operand } => {
1515                let value = self.eval_integral_at(*operand, depth + 1)?;
1516                match op {
1517                    UnaryOp::Plus => Ok(value),
1518                    UnaryOp::Minus => value.checked_neg().ok_or(EvalError::Overflow { at }),
1519                    UnaryOp::Not => Ok(!value),
1520                }
1521            }
1522            ConstExpr::Binary { op, lhs, rhs } => {
1523                let left = self.eval_integral_at(*lhs, depth + 1)?;
1524                let right = self.eval_integral_at(*rhs, depth + 1)?;
1525                Self::apply_binary(*op, left, right, at)
1526            }
1527        }
1528    }
1529
1530    fn apply_binary(
1531        op: crate::node::constant::BinaryOp,
1532        left: i128,
1533        right: i128,
1534        at: NodeId,
1535    ) -> Result<i128, EvalError> {
1536        use crate::node::constant::BinaryOp as B;
1537        match op {
1538            B::Or => Ok(left | right),
1539            B::Xor => Ok(left ^ right),
1540            B::And => Ok(left & right),
1541            B::Shl | B::Shr => {
1542                let distance = u32::try_from(right).map_err(|_| EvalError::InvalidShift { at })?;
1543                if distance >= 128 {
1544                    return Err(EvalError::InvalidShift { at });
1545                }
1546                if matches!(op, B::Shl) {
1547                    left.checked_shl(distance).ok_or(EvalError::Overflow { at })
1548                } else {
1549                    left.checked_shr(distance).ok_or(EvalError::Overflow { at })
1550                }
1551            }
1552            B::Add => left.checked_add(right).ok_or(EvalError::Overflow { at }),
1553            B::Sub => left.checked_sub(right).ok_or(EvalError::Overflow { at }),
1554            B::Mul => left.checked_mul(right).ok_or(EvalError::Overflow { at }),
1555            B::Div => {
1556                if right == 0 {
1557                    Err(EvalError::DivisionByZero { at })
1558                } else {
1559                    left.checked_div(right).ok_or(EvalError::Overflow { at })
1560                }
1561            }
1562            B::Mod => {
1563                if right == 0 {
1564                    Err(EvalError::DivisionByZero { at })
1565                } else {
1566                    left.checked_rem(right).ok_or(EvalError::Overflow { at })
1567                }
1568            }
1569        }
1570    }
1571
1572    // -- interfaces ------------------------------------------------------
1573
1574    /// Borrow an interface.
1575    ///
1576    /// # Errors
1577    /// [`AstError::Core`] with a stale or unknown handle.
1578    pub fn interface(&self, id: Id<InterfaceNode>) -> Result<&InterfaceNode, AstError> {
1579        Ok(self.interfaces.get(id)?)
1580    }
1581
1582    /// Declare an interface in a module.
1583    ///
1584    /// # Errors
1585    /// [`AstError::Core`] on a stale module handle.
1586    pub fn add_interface(
1587        &mut self,
1588        module: Id<ModuleNode>,
1589        name: &str,
1590        kind: InterfaceKind,
1591        bases: Vec<Ref<InterfaceNode>>,
1592    ) -> Result<Id<InterfaceNode>, AstError> {
1593        let node_id = self.next_model_id()?;
1594        let id = self.interfaces.insert(InterfaceNode {
1595            node_id,
1596            name: name.to_string(),
1597            module,
1598            kind,
1599            bases,
1600            operations: Vec::new(),
1601            attributes: Vec::new(),
1602            completeness: Completeness::Complete,
1603            facets: Facets::synthetic("interface created through the model API"),
1604        })?;
1605        let any = AnyId::Interface(id, node_id);
1606        self.register(any);
1607        self.modules.get_mut(module)?.children.push(any);
1608        self.committed(id)
1609    }
1610
1611    /// Declare an interface whose body has not arrived.
1612    ///
1613    /// `interface I;` — IDL 4.2 §7.4.3, rule (75). The same shape
1614    /// [`Self::add_forward_type`] gives a struct or a union, and for the
1615    /// same reason: forwardness is a state of one node, so a reference
1616    /// taken against the declaration stays valid when the definition
1617    /// completes it.
1618    ///
1619    /// # Errors
1620    /// As [`Self::add_interface`].
1621    pub fn add_forward_interface(
1622        &mut self,
1623        module: Id<ModuleNode>,
1624        name: &str,
1625        kind: InterfaceKind,
1626    ) -> Result<Id<InterfaceNode>, AstError> {
1627        let id = self.add_interface(module, name, kind, Vec::new())?;
1628        self.interfaces.get_mut(id)?.completeness = Completeness::Forward;
1629        self.committed(id)
1630    }
1631
1632    /// Give a forward-declared interface its body.
1633    ///
1634    /// An update of the existing node, not a second one — the reason
1635    /// [`Self::complete_type`] gives.
1636    ///
1637    /// # Errors
1638    /// [`AstError::AlreadyComplete`] if the interface already has a body:
1639    /// two definitions of one name are a duplicate, not a completion.
1640    /// [`AstError::Core`] on a stale handle.
1641    pub fn complete_interface(
1642        &mut self,
1643        id: Id<InterfaceNode>,
1644        kind: InterfaceKind,
1645        bases: Vec<Ref<InterfaceNode>>,
1646    ) -> Result<(), AstError> {
1647        let node = self.interfaces.get(id)?;
1648        let node_id = node.node_id;
1649        if node.completeness == Completeness::Complete {
1650            return Err(AstError::AlreadyComplete { node: node_id });
1651        }
1652        // The bases the declaration is about to gain are pending
1653        // references; the waiter index has to see them, and the empty
1654        // list it holds now has to go.
1655        self.unindex_pending(AnyId::Interface(id, node_id));
1656
1657        let node = self.interfaces.get_mut(id)?;
1658        node.kind = kind;
1659        node.bases = bases;
1660        node.completeness = Completeness::Complete;
1661
1662        self.index_pending(AnyId::Interface(id, node_id));
1663        self.record_completeness_change(node_id);
1664        Ok(())
1665    }
1666
1667    /// Iterate every interface in the model, in arena order.
1668    pub fn interfaces(&self) -> impl Iterator<Item = (Id<InterfaceNode>, &InterfaceNode)> {
1669        self.interfaces.iter()
1670    }
1671
1672    /// Iterate every operation in the model, in arena order.
1673    ///
1674    /// An operation's owner is [`OperationNode::parent`], which is an
1675    /// [`AnyId`] because a valuetype declares operations too. There is
1676    /// no list on the valuetype for the exported ones — only for its
1677    /// initialisers, which is what rule (105) distinguishes — so this
1678    /// is how a consumer finds them.
1679    pub fn operations(&self) -> impl Iterator<Item = (Id<OperationNode>, &OperationNode)> {
1680        self.operations.iter()
1681    }
1682
1683    /// Rewrite an interface's form and base list.
1684    ///
1685    /// Distinct from [`Self::complete_interface`], which gives a forward
1686    /// declaration the body it was still waiting for. This restates a
1687    /// declaration that already has one: CCM 4.0 §6.2's equivalent
1688    /// interface is the component declaration with a different form and
1689    /// a different base list, and the identity has to survive the change
1690    /// so that references taken against the component keep pointing at
1691    /// it.
1692    ///
1693    /// The waiter index is rebuilt, because the old bases' pending keys
1694    /// have to go and the new ones have to be seen.
1695    ///
1696    /// # Errors
1697    /// [`AstError::Core`] on a stale handle.
1698    pub fn set_interface_form(
1699        &mut self,
1700        id: Id<InterfaceNode>,
1701        kind: InterfaceKind,
1702        bases: Vec<Ref<InterfaceNode>>,
1703    ) -> Result<(), AstError> {
1704        let node_id = self.interfaces.get(id)?.node_id;
1705        self.unindex_pending(AnyId::Interface(id, node_id));
1706        let node = self.interfaces.get_mut(id)?;
1707        node.kind = kind;
1708        node.bases = bases;
1709        self.index_pending(AnyId::Interface(id, node_id));
1710        Ok(())
1711    }
1712
1713    /// Add a base to a valuetype.
1714    ///
1715    /// A method rather than mutable access to the list, for the reason
1716    /// [`Self::add_raises`] gives: a reference added behind the model's
1717    /// back is never indexed, and an unindexed pending reference is one
1718    /// that never resolves.
1719    ///
1720    /// # Errors
1721    /// [`AstError::NotAnAggregate`] when the type is not a valuetype,
1722    /// [`AstError::Core`] on a stale handle.
1723    pub fn add_valuetype_base(
1724        &mut self,
1725        id: Id<TypeNode>,
1726        base: Ref<TypeNode>,
1727    ) -> Result<(), AstError> {
1728        let node_id = self.types.get(id)?.node_id;
1729        if !matches!(self.types.get(id)?.kind, TypeKind::Valuetype { .. }) {
1730            return Err(AstError::NotAnAggregate {
1731                node: node_id,
1732                found: self.types.get(id)?.kind.as_str(),
1733            });
1734        }
1735        if let TypeKind::Valuetype { bases, .. } = &mut self.types.get_mut(id)?.kind {
1736            bases.push(base);
1737        }
1738        self.reindex_pending(AnyId::Type(id, node_id));
1739        Ok(())
1740    }
1741
1742    /// The type that is an object reference to `interface`.
1743    ///
1744    /// An interface name written where a type is expected is an object
1745    /// reference — OMG IDL 4.2 §7.4.6 — and `struct S { I x; }` is the
1746    /// ordinary way to write one. The reference in `S::x` is a
1747    /// `Ref<TypeNode>` and an interface is not a type, so the model needs
1748    /// a type node standing for "a reference to `I`"; this returns it,
1749    /// creating it the first time and returning the same one after.
1750    ///
1751    /// It carries the interface's own name so a backend renders `I`, and
1752    /// it is deliberately left out of the name index: the name in the
1753    /// scope belongs to the interface, and indexing both would make every
1754    /// use of it ambiguous.
1755    ///
1756    /// # Errors
1757    /// [`AstError::Core`] on a stale interface handle.
1758    pub fn object_reference_to(
1759        &mut self,
1760        interface: Id<InterfaceNode>,
1761    ) -> Result<Id<TypeNode>, AstError> {
1762        let existing = self.types.iter().find_map(|(id, node)| {
1763            matches!(
1764                &node.kind,
1765                TypeKind::ObjectRef {
1766                    interface: Some(Ref::Resolved(target)),
1767                } if *target == interface
1768            )
1769            .then_some(id)
1770        });
1771        if let Some(id) = existing {
1772            return Ok(id);
1773        }
1774        let node = self.interfaces.get(interface)?;
1775        let (module, name) = (node.module, node.name.clone());
1776        self.add_type(
1777            module,
1778            &name,
1779            TypeKind::ObjectRef {
1780                interface: Some(Ref::Resolved(interface)),
1781            },
1782        )
1783    }
1784
1785    /// Borrow an operation.
1786    ///
1787    /// # Errors
1788    /// [`AstError::Core`] with a stale or unknown handle.
1789    pub fn operation(&self, id: Id<OperationNode>) -> Result<&OperationNode, AstError> {
1790        Ok(self.operations.get(id)?)
1791    }
1792
1793    /// Add an operation to an interface.
1794    ///
1795    /// # Errors
1796    /// [`AstError::Core`] on a stale handle.
1797    pub fn add_operation(
1798        &mut self,
1799        interface: Id<InterfaceNode>,
1800        name: &str,
1801        return_type: Option<Ref<TypeNode>>,
1802        oneway: bool,
1803    ) -> Result<Id<OperationNode>, AstError> {
1804        let parent_node_id = self.interfaces.get(interface)?.node_id;
1805        let node_id = self.next_model_id()?;
1806        let id = self.operations.insert(OperationNode {
1807            node_id,
1808            name: name.to_string(),
1809            return_type,
1810            params: Vec::new(),
1811            raises: Vec::new(),
1812            oneway,
1813            contexts: Vec::new(),
1814            parent: AnyId::Interface(interface, parent_node_id),
1815            facets: Facets::synthetic("operation created through the model API"),
1816        })?;
1817        self.register(AnyId::Operation(id, node_id));
1818        self.interfaces.get_mut(interface)?.operations.push(id);
1819        self.committed(id)
1820    }
1821
1822    /// Add an initialiser or an exported operation to a valuetype.
1823    ///
1824    /// OMG IDL 4.2 rule (105): a `valuetype` body holds `factory`
1825    /// initialisers and ordinary operations alongside its state.
1826    /// [`TypeKind::Valuetype::initializers`] was in the model from the
1827    /// start and nothing could fill it — [`Self::add_operation`] takes
1828    /// an [`Id<InterfaceNode>`], so every `factory` in every IDL file
1829    /// this tree read was parsed, kept in the CST and dropped at the
1830    /// model boundary with no diagnostic.
1831    ///
1832    /// `initializer` decides which list the operation joins: an
1833    /// initialiser goes into `initializers`, an exported operation is
1834    /// reachable through its `parent` and is not one.
1835    ///
1836    /// # Errors
1837    /// [`AstError::Core`] on a stale handle, and
1838    /// [`AstError::Core`] when the type is not a valuetype.
1839    pub fn add_valuetype_operation(
1840        &mut self,
1841        valuetype: Id<TypeNode>,
1842        name: &str,
1843        return_type: Option<Ref<TypeNode>>,
1844        initializer: bool,
1845    ) -> Result<Id<OperationNode>, AstError> {
1846        let parent_node_id = self.types.get(valuetype)?.node_id;
1847        if !matches!(self.types.get(valuetype)?.kind, TypeKind::Valuetype { .. }) {
1848            return Err(AstError::NotAnAggregate {
1849                node: parent_node_id,
1850                found: "not a valuetype",
1851            });
1852        }
1853        let node_id = self.next_model_id()?;
1854        let id = self.operations.insert(OperationNode {
1855            node_id,
1856            name: name.to_string(),
1857            return_type,
1858            params: Vec::new(),
1859            raises: Vec::new(),
1860            oneway: false,
1861            contexts: Vec::new(),
1862            parent: AnyId::Type(valuetype, parent_node_id),
1863            facets: Facets::synthetic("valuetype operation created through the model API"),
1864        })?;
1865        self.register(AnyId::Operation(id, node_id));
1866        if initializer
1867            && let TypeKind::Valuetype { initializers, .. } =
1868                &mut self.types.get_mut(valuetype)?.kind
1869        {
1870            initializers.push(id);
1871        }
1872        self.committed(id)
1873    }
1874
1875    /// Mark an operation `oneway`.
1876    ///
1877    /// `@oneway` writes the same flag as the keyword form; both reach
1878    /// the operation through here.
1879    ///
1880    /// # Errors
1881    /// [`AstError::Core`] with a stale or unknown handle.
1882    pub fn set_oneway(&mut self, id: Id<OperationNode>, oneway: bool) -> Result<(), AstError> {
1883        self.operations.get_mut(id)?.oneway = oneway;
1884        Ok(())
1885    }
1886
1887    /// Add an exception to an operation's `raises` clause.
1888    ///
1889    /// A method rather than mutable access to the list: a reference
1890    /// added behind the model's back is never indexed, and an
1891    /// unindexed pending reference is one that never resolves.
1892    ///
1893    /// # Errors
1894    /// [`AstError::Core`] with a stale or unknown handle.
1895    pub fn add_raises(
1896        &mut self,
1897        id: Id<OperationNode>,
1898        exception: Ref<ExceptionNode>,
1899    ) -> Result<(), AstError> {
1900        let node_id = self.operations.get(id)?.node_id;
1901        self.operations.get_mut(id)?.raises.push(exception);
1902        self.reindex_pending(AnyId::Operation(id, node_id));
1903        Ok(())
1904    }
1905
1906    /// Add a `context` identifier to an operation.
1907    ///
1908    /// # Errors
1909    /// [`AstError::Core`] with a stale or unknown handle.
1910    pub fn add_context(&mut self, id: Id<OperationNode>, context: &str) -> Result<(), AstError> {
1911        self.operations
1912            .get_mut(id)?
1913            .contexts
1914            .push(context.to_string());
1915        Ok(())
1916    }
1917
1918    /// Borrow a parameter.
1919    ///
1920    /// # Errors
1921    /// [`AstError::Core`] with a stale or unknown handle.
1922    pub fn param(&self, id: Id<ParamNode>) -> Result<&ParamNode, AstError> {
1923        Ok(self.params.get(id)?)
1924    }
1925
1926    /// Add a parameter to an operation.
1927    ///
1928    /// # Errors
1929    /// [`AstError::Core`] on a stale handle.
1930    pub fn add_param(
1931        &mut self,
1932        operation: Id<OperationNode>,
1933        name: &str,
1934        direction: ParamDirection,
1935        type_: Ref<TypeNode>,
1936    ) -> Result<Id<ParamNode>, AstError> {
1937        let node_id = self.next_model_id()?;
1938        let id = self.params.insert(ParamNode {
1939            node_id,
1940            name: name.to_string(),
1941            direction,
1942            type_,
1943            parent: operation,
1944            facets: Facets::synthetic("parameter created through the model API"),
1945        })?;
1946        self.register(AnyId::Param(id, node_id));
1947        self.operations.get_mut(operation)?.params.push(id);
1948        self.committed(id)
1949    }
1950
1951    /// Borrow an attribute.
1952    ///
1953    /// # Errors
1954    /// [`AstError::Core`] with a stale or unknown handle.
1955    pub fn attribute(&self, id: Id<AttributeNode>) -> Result<&AttributeNode, AstError> {
1956        Ok(self.attributes.get(id)?)
1957    }
1958
1959    /// Add an exception to an attribute's `getraises` or `setraises`
1960    /// clause.
1961    ///
1962    /// # Errors
1963    /// [`AstError::Core`] with a stale or unknown handle.
1964    pub fn add_attribute_raises(
1965        &mut self,
1966        id: Id<AttributeNode>,
1967        exception: Ref<ExceptionNode>,
1968        on_set: bool,
1969    ) -> Result<(), AstError> {
1970        let node_id = self.attributes.get(id)?.node_id;
1971        let node = self.attributes.get_mut(id)?;
1972        if on_set {
1973            node.set_raises.push(exception);
1974        } else {
1975            node.get_raises.push(exception);
1976        }
1977        self.reindex_pending(AnyId::Attribute(id, node_id));
1978        Ok(())
1979    }
1980
1981    /// Add an attribute to an interface.
1982    ///
1983    /// # Errors
1984    /// [`AstError::Core`] on a stale handle.
1985    pub fn add_attribute(
1986        &mut self,
1987        interface: Id<InterfaceNode>,
1988        name: &str,
1989        type_: Ref<TypeNode>,
1990        readonly: bool,
1991    ) -> Result<Id<AttributeNode>, AstError> {
1992        let node_id = self.next_model_id()?;
1993        let id = self.attributes.insert(AttributeNode {
1994            node_id,
1995            name: name.to_string(),
1996            type_,
1997            readonly,
1998            get_raises: Vec::new(),
1999            set_raises: Vec::new(),
2000            parent: interface,
2001            facets: Facets::synthetic("attribute created through the model API"),
2002        })?;
2003        self.register(AnyId::Attribute(id, node_id));
2004        self.interfaces.get_mut(interface)?.attributes.push(id);
2005        self.committed(id)
2006    }
2007
2008    /// Borrow an exception.
2009    ///
2010    /// # Errors
2011    /// [`AstError::Core`] with a stale or unknown handle.
2012    pub fn exception(&self, id: Id<ExceptionNode>) -> Result<&ExceptionNode, AstError> {
2013        Ok(self.exceptions.get(id)?)
2014    }
2015
2016    /// Iterate every exceptions node in the model, in arena order.
2017    pub fn exceptions(&self) -> impl Iterator<Item = (Id<ExceptionNode>, &ExceptionNode)> {
2018        self.exceptions.iter()
2019    }
2020
2021    /// Record that a type, constant or exception is declared inside the
2022    /// interface or value type `scope` rather than directly in its module.
2023    ///
2024    /// # Errors
2025    /// [`AstError::Core`] on a stale handle; [`AstError::WrongKind`] for a
2026    /// node of another kind, which no interface body declares.
2027    pub fn set_declaration_scope(&mut self, any: AnyId, scope: &str) -> Result<(), AstError> {
2028        let slot = match any {
2029            AnyId::Type(id, _) => &mut self.types.get_mut(id)?.scope,
2030            AnyId::Const(id, _) => &mut self.constants.get_mut(id)?.scope,
2031            AnyId::Exception(id, _) => &mut self.exceptions.get_mut(id)?.scope,
2032            other => {
2033                return Err(AstError::WrongKind {
2034                    node: other.node_id(),
2035                    expected: crate::NodeKind::Type,
2036                    found: other.kind(),
2037                });
2038            }
2039        };
2040        *slot = Some(scope.to_string());
2041        // The name the node was indexed under when it was declared did
2042        // not have the scope in it yet.
2043        self.by_scoped_name.remove_node(any.node_id());
2044        if let Some(name) = self.scoped_name_of(any) {
2045            self.by_scoped_name.insert(name, any.node_id());
2046        }
2047        Ok(())
2048    }
2049
2050    /// Declare an exception in a module.
2051    ///
2052    /// # Errors
2053    /// [`AstError::Core`] on a stale module handle.
2054    pub fn add_exception(
2055        &mut self,
2056        module: Id<ModuleNode>,
2057        name: &str,
2058    ) -> Result<Id<ExceptionNode>, AstError> {
2059        let node_id = self.next_model_id()?;
2060        let id = self.exceptions.insert(ExceptionNode {
2061            node_id,
2062            name: name.to_string(),
2063            module,
2064            scope: None,
2065            members: Vec::new(),
2066            facets: Facets::synthetic("exception created through the model API"),
2067        })?;
2068        let any = AnyId::Exception(id, node_id);
2069        self.register(any);
2070        self.modules.get_mut(module)?.children.push(any);
2071        self.committed(id)
2072    }
2073
2074    // -- annotations -----------------------------------------------------
2075
2076    /// Every annotation declaration in the model, in arena order.
2077    ///
2078    /// The counterpart of [`Ast::types`] for the other declaration kind
2079    /// that has a `TypeObject`: Annex B gives `MinimalTypeObject` a
2080    /// `case TK_ANNOTATION: MinimalAnnotationType annotation_type;`, and
2081    /// an `@annotation` declaration is not a `TypeNode` in this model,
2082    /// so a caller that wants to build one had no way to reach it.
2083    pub fn annotation_defs(
2084        &self,
2085    ) -> impl Iterator<Item = (Id<AnnotationDefNode>, &AnnotationDefNode)> {
2086        self.annotation_defs.iter()
2087    }
2088
2089    /// Borrow an annotation declaration.
2090    ///
2091    /// # Errors
2092    /// [`AstError::Core`] with a stale or unknown handle.
2093    pub fn annotation_def(
2094        &self,
2095        id: Id<AnnotationDefNode>,
2096    ) -> Result<&AnnotationDefNode, AstError> {
2097        Ok(self.annotation_defs.get(id)?)
2098    }
2099
2100    /// Declare an annotation in a module.
2101    ///
2102    /// # Errors
2103    /// [`AstError::Core`] on a stale module handle.
2104    pub fn add_annotation_def(
2105        &mut self,
2106        module: Id<ModuleNode>,
2107        name: &str,
2108    ) -> Result<Id<AnnotationDefNode>, AstError> {
2109        let node_id = self.next_model_id()?;
2110        let id = self.annotation_defs.insert(AnnotationDefNode {
2111            node_id,
2112            name: name.to_string(),
2113            module,
2114            members: Vec::new(),
2115            facets: Facets::synthetic("annotation created through the model API"),
2116        })?;
2117        let any = AnyId::AnnotationDef(id, node_id);
2118        self.register(any);
2119        self.modules.get_mut(module)?.children.push(any);
2120        self.committed(id)
2121    }
2122
2123    /// Borrow an annotation member.
2124    ///
2125    /// # Errors
2126    /// [`AstError::Core`] with a stale or unknown handle.
2127    pub fn annotation_member(
2128        &self,
2129        id: Id<AnnotationMemberNode>,
2130    ) -> Result<&AnnotationMemberNode, AstError> {
2131        Ok(self.annotation_members.get(id)?)
2132    }
2133
2134    /// Add a member to an annotation declaration.
2135    ///
2136    /// # Errors
2137    /// [`AstError::Core`] on a stale handle.
2138    pub fn add_annotation_member(
2139        &mut self,
2140        annotation: Id<AnnotationDefNode>,
2141        name: &str,
2142        type_: Ref<TypeNode>,
2143        default: Option<Id<ConstExprNode>>,
2144    ) -> Result<Id<AnnotationMemberNode>, AstError> {
2145        let node_id = self.next_model_id()?;
2146        let is_value_member = name == "value";
2147        let id = self.annotation_members.insert(AnnotationMemberNode {
2148            node_id,
2149            name: name.to_string(),
2150            type_,
2151            default,
2152            is_value_member,
2153            parent: annotation,
2154            facets: Facets::synthetic("annotation member created through the model API"),
2155        })?;
2156        self.register(AnyId::AnnotationMember(id, node_id));
2157        self.annotation_defs.get_mut(annotation)?.members.push(id);
2158        self.committed(id)
2159    }
2160
2161    // -- deployment ------------------------------------------------------
2162
2163    /// Borrow a library.
2164    ///
2165    /// # Errors
2166    /// [`AstError::Core`] with a stale or unknown handle.
2167    pub fn library(&self, id: Id<LibraryNode>) -> Result<&LibraryNode, AstError> {
2168        Ok(self.libraries.get(id)?)
2169    }
2170
2171    /// Declare a deployment library.
2172    ///
2173    /// # Errors
2174    /// [`AstError::Core`] when identity space is exhausted.
2175    pub fn add_library(&mut self, name: &str) -> Result<Id<LibraryNode>, AstError> {
2176        let node_id = self.next_model_id()?;
2177        let id = self.libraries.insert(LibraryNode {
2178            node_id,
2179            name: name.to_string(),
2180            children: Vec::new(),
2181            facets: Facets::synthetic("library created through the model API"),
2182        })?;
2183        self.register(AnyId::Library(id, node_id));
2184        self.committed(id)
2185    }
2186
2187    /// Borrow a domain.
2188    ///
2189    /// # Errors
2190    /// [`AstError::Core`] with a stale or unknown handle.
2191    pub fn domain(&self, id: Id<DomainNode>) -> Result<&DomainNode, AstError> {
2192        Ok(self.domains.get(id)?)
2193    }
2194
2195    /// Declare a domain in a library.
2196    ///
2197    /// # Errors
2198    /// [`AstError::Core`] on a stale handle.
2199    pub fn add_domain(
2200        &mut self,
2201        library: Id<LibraryNode>,
2202        name: &str,
2203        domain_id: u32,
2204    ) -> Result<Id<DomainNode>, AstError> {
2205        let node_id = self.next_model_id()?;
2206        let id = self.domains.insert(DomainNode {
2207            node_id,
2208            name: name.to_string(),
2209            domain_id,
2210            topics: Vec::new(),
2211            parent: library,
2212            facets: Facets::synthetic("domain created through the model API"),
2213        })?;
2214        let any = AnyId::Domain(id, node_id);
2215        self.register(any);
2216        self.libraries.get_mut(library)?.children.push(any);
2217        self.committed(id)
2218    }
2219
2220    /// Borrow a topic.
2221    ///
2222    /// # Errors
2223    /// [`AstError::Core`] with a stale or unknown handle.
2224    pub fn topic(&self, id: Id<TopicNode>) -> Result<&TopicNode, AstError> {
2225        Ok(self.topics.get(id)?)
2226    }
2227
2228    /// Declare a topic in a domain.
2229    ///
2230    /// # Errors
2231    /// [`AstError::Core`] on a stale handle.
2232    pub fn add_topic(
2233        &mut self,
2234        domain: Id<DomainNode>,
2235        name: &str,
2236        type_: Ref<TypeNode>,
2237        qos: Option<Ref<QosProfileNode>>,
2238    ) -> Result<Id<TopicNode>, AstError> {
2239        let node_id = self.next_model_id()?;
2240        let id = self.topics.insert(TopicNode {
2241            node_id,
2242            name: name.to_string(),
2243            type_,
2244            qos,
2245            parent: domain,
2246            facets: Facets::synthetic("topic created through the model API"),
2247        })?;
2248        self.register(AnyId::Topic(id, node_id));
2249        self.domains.get_mut(domain)?.topics.push(id);
2250        self.committed(id)
2251    }
2252
2253    /// Borrow a QoS profile.
2254    ///
2255    /// # Errors
2256    /// [`AstError::Core`] with a stale or unknown handle.
2257    pub fn qos_profile(&self, id: Id<QosProfileNode>) -> Result<&QosProfileNode, AstError> {
2258        Ok(self.qos_profiles.get(id)?)
2259    }
2260
2261    /// Declare a QoS profile in a library.
2262    ///
2263    /// # Errors
2264    /// [`AstError::Core`] on a stale handle.
2265    pub fn add_qos_profile(
2266        &mut self,
2267        library: Id<LibraryNode>,
2268        name: &str,
2269        base: Option<Ref<QosProfileNode>>,
2270        policies: Vec<QosPolicy>,
2271    ) -> Result<Id<QosProfileNode>, AstError> {
2272        let node_id = self.next_model_id()?;
2273        let id = self.qos_profiles.insert(QosProfileNode {
2274            node_id,
2275            name: name.to_string(),
2276            base,
2277            policies,
2278            parent: library,
2279            facets: Facets::synthetic("QoS profile created through the model API"),
2280        })?;
2281        let any = AnyId::QosProfile(id, node_id);
2282        self.register(any);
2283        self.libraries.get_mut(library)?.children.push(any);
2284        self.committed(id)
2285    }
2286
2287    /// Borrow a participant.
2288    ///
2289    /// # Errors
2290    /// [`AstError::Core`] with a stale or unknown handle.
2291    pub fn participant(&self, id: Id<ParticipantNode>) -> Result<&ParticipantNode, AstError> {
2292        Ok(self.participants.get(id)?)
2293    }
2294
2295    /// Declare a participant in a library.
2296    ///
2297    /// # Errors
2298    /// [`AstError::Core`] on a stale handle.
2299    pub fn add_participant(
2300        &mut self,
2301        library: Id<LibraryNode>,
2302        name: &str,
2303        domain: Ref<DomainNode>,
2304        qos: Option<Ref<QosProfileNode>>,
2305    ) -> Result<Id<ParticipantNode>, AstError> {
2306        let node_id = self.next_model_id()?;
2307        let id = self.participants.insert(ParticipantNode {
2308            node_id,
2309            name: name.to_string(),
2310            domain,
2311            qos,
2312            endpoints: Vec::new(),
2313            parent: library,
2314            facets: Facets::synthetic("participant created through the model API"),
2315        })?;
2316        let any = AnyId::Participant(id, node_id);
2317        self.register(any);
2318        self.libraries.get_mut(library)?.children.push(any);
2319        self.committed(id)
2320    }
2321
2322    /// Borrow an endpoint.
2323    ///
2324    /// # Errors
2325    /// [`AstError::Core`] with a stale or unknown handle.
2326    pub fn endpoint(&self, id: Id<EndpointNode>) -> Result<&EndpointNode, AstError> {
2327        Ok(self.endpoints.get(id)?)
2328    }
2329
2330    /// Declare an endpoint on a participant.
2331    ///
2332    /// # Errors
2333    /// [`AstError::Core`] on a stale handle.
2334    pub fn add_endpoint(
2335        &mut self,
2336        participant: Id<ParticipantNode>,
2337        name: &str,
2338        role: EndpointRole,
2339        topic: Ref<TopicNode>,
2340        qos: Option<Ref<QosProfileNode>>,
2341    ) -> Result<Id<EndpointNode>, AstError> {
2342        let node_id = self.next_model_id()?;
2343        let id = self.endpoints.insert(EndpointNode {
2344            node_id,
2345            name: name.to_string(),
2346            role,
2347            topic,
2348            qos,
2349            parent: participant,
2350            facets: Facets::synthetic("endpoint created through the model API"),
2351        })?;
2352        self.register(AnyId::Endpoint(id, node_id));
2353        self.participants.get_mut(participant)?.endpoints.push(id);
2354        self.committed(id)
2355    }
2356
2357    // -- facet access ----------------------------------------------------
2358
2359    /// The facets of any node.
2360    #[must_use]
2361    pub fn facets(&self, any: AnyId) -> Option<&Facets> {
2362        Some(match any {
2363            AnyId::Module(id, _) => &self.modules.get(id).ok()?.facets,
2364            AnyId::Type(id, _) => &self.types.get(id).ok()?.facets,
2365            AnyId::Member(id, _) => &self.members.get(id).ok()?.facets,
2366            AnyId::Case(id, _) => &self.cases.get(id).ok()?.facets,
2367            AnyId::Enumerator(id, _) => &self.enumerators.get(id).ok()?.facets,
2368            AnyId::BitValue(id, _) => &self.bit_values.get(id).ok()?.facets,
2369            AnyId::Bitfield(id, _) => &self.bitfields.get(id).ok()?.facets,
2370            AnyId::Const(id, _) => &self.constants.get(id).ok()?.facets,
2371            AnyId::ConstExpr(id, _) => &self.exprs.get(id).ok()?.facets,
2372            AnyId::Interface(id, _) => &self.interfaces.get(id).ok()?.facets,
2373            AnyId::Operation(id, _) => &self.operations.get(id).ok()?.facets,
2374            AnyId::Param(id, _) => &self.params.get(id).ok()?.facets,
2375            AnyId::Attribute(id, _) => &self.attributes.get(id).ok()?.facets,
2376            AnyId::Exception(id, _) => &self.exceptions.get(id).ok()?.facets,
2377            AnyId::AnnotationDef(id, _) => &self.annotation_defs.get(id).ok()?.facets,
2378            AnyId::AnnotationMember(id, _) => &self.annotation_members.get(id).ok()?.facets,
2379            AnyId::Domain(id, _) => &self.domains.get(id).ok()?.facets,
2380            AnyId::Topic(id, _) => &self.topics.get(id).ok()?.facets,
2381            AnyId::QosProfile(id, _) => &self.qos_profiles.get(id).ok()?.facets,
2382            AnyId::Participant(id, _) => &self.participants.get(id).ok()?.facets,
2383            AnyId::Endpoint(id, _) => &self.endpoints.get(id).ok()?.facets,
2384            AnyId::Library(id, _) => &self.libraries.get(id).ok()?.facets,
2385        })
2386    }
2387
2388    /// The facets of any node, mutably.
2389    pub fn facets_mut(&mut self, any: AnyId) -> Option<&mut Facets> {
2390        Some(match any {
2391            AnyId::Module(id, _) => &mut self.modules.get_mut(id).ok()?.facets,
2392            AnyId::Type(id, _) => &mut self.types.get_mut(id).ok()?.facets,
2393            AnyId::Member(id, _) => &mut self.members.get_mut(id).ok()?.facets,
2394            AnyId::Case(id, _) => &mut self.cases.get_mut(id).ok()?.facets,
2395            AnyId::Enumerator(id, _) => &mut self.enumerators.get_mut(id).ok()?.facets,
2396            AnyId::BitValue(id, _) => &mut self.bit_values.get_mut(id).ok()?.facets,
2397            AnyId::Bitfield(id, _) => &mut self.bitfields.get_mut(id).ok()?.facets,
2398            AnyId::Const(id, _) => &mut self.constants.get_mut(id).ok()?.facets,
2399            AnyId::ConstExpr(id, _) => &mut self.exprs.get_mut(id).ok()?.facets,
2400            AnyId::Interface(id, _) => &mut self.interfaces.get_mut(id).ok()?.facets,
2401            AnyId::Operation(id, _) => &mut self.operations.get_mut(id).ok()?.facets,
2402            AnyId::Param(id, _) => &mut self.params.get_mut(id).ok()?.facets,
2403            AnyId::Attribute(id, _) => &mut self.attributes.get_mut(id).ok()?.facets,
2404            AnyId::Exception(id, _) => &mut self.exceptions.get_mut(id).ok()?.facets,
2405            AnyId::AnnotationDef(id, _) => &mut self.annotation_defs.get_mut(id).ok()?.facets,
2406            AnyId::AnnotationMember(id, _) => &mut self.annotation_members.get_mut(id).ok()?.facets,
2407            AnyId::Domain(id, _) => &mut self.domains.get_mut(id).ok()?.facets,
2408            AnyId::Topic(id, _) => &mut self.topics.get_mut(id).ok()?.facets,
2409            AnyId::QosProfile(id, _) => &mut self.qos_profiles.get_mut(id).ok()?.facets,
2410            AnyId::Participant(id, _) => &mut self.participants.get_mut(id).ok()?.facets,
2411            AnyId::Endpoint(id, _) => &mut self.endpoints.get_mut(id).ok()?.facets,
2412            AnyId::Library(id, _) => &mut self.libraries.get_mut(id).ok()?.facets,
2413        })
2414    }
2415
2416    /// The `AnyId` for a type handle.
2417    ///
2418    /// # Errors
2419    /// [`AstError::Core`] with a stale handle.
2420    pub fn any_of_type(&self, id: Id<TypeNode>) -> Result<AnyId, AstError> {
2421        Ok(AnyId::Type(id, self.types.get(id)?.node_id))
2422    }
2423
2424    /// The `AnyId` for a member handle.
2425    ///
2426    /// # Errors
2427    /// [`AstError::Core`] with a stale handle.
2428    pub fn any_of_member(&self, id: Id<MemberNode>) -> Result<AnyId, AstError> {
2429        Ok(AnyId::Member(id, self.members.get(id)?.node_id))
2430    }
2431}
2432
2433/// Convenience: the primitive kinds a frontend most often needs, as a
2434/// helper for building small models in tests and tools.
2435#[must_use]
2436pub fn primitive(kind: PrimitiveKind) -> TypeKind {
2437    TypeKind::Primitive(kind)
2438}