Skip to main content

zerodds_ast/node/
type_.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Type nodes. Spec §3.2.
4
5use crate::facet::Facets;
6use crate::node::annotation::AnnotationDefNode;
7use crate::node::constant::ConstExprNode;
8use crate::node::interface::{InterfaceNode, OperationNode};
9use crate::node::member::{BitValueNode, BitfieldNode, CaseNode, EnumeratorNode, MemberNode};
10use crate::node::module::ModuleNode;
11use alloc::string::String;
12use alloc::vec::Vec;
13use zerodds_ast_core::{Id, NodeId, Ref};
14
15/// Built-in scalar kinds. Spec §3.2, primitive row.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
17pub enum PrimitiveKind {
18    /// `boolean`
19    Boolean,
20    /// `octet`
21    Octet,
22    /// `char`
23    Char,
24    /// `wchar`
25    WChar,
26    /// `int8`
27    Int8,
28    /// `uint8`
29    UInt8,
30    /// `short`
31    Int16,
32    /// `unsigned short`
33    UInt16,
34    /// `long`
35    Int32,
36    /// `unsigned long`
37    UInt32,
38    /// `long long`
39    Int64,
40    /// `unsigned long long`
41    UInt64,
42    /// `float`
43    Float32,
44    /// `double`
45    Float64,
46    /// `long double`.
47    ///
48    /// Representable regardless of any target language's ability to hold
49    /// it: the model describes the type, it does not evaluate it. The
50    /// known Rust `f128` gap constrains backends, not this model.
51    Float128,
52}
53
54impl PrimitiveKind {
55    /// Every primitive, in specification order.
56    ///
57    /// Used by conformance tests to assert the set is complete rather
58    /// than merely claimed complete.
59    pub const ALL: [Self; 15] = [
60        Self::Boolean,
61        Self::Octet,
62        Self::Char,
63        Self::WChar,
64        Self::Int8,
65        Self::UInt8,
66        Self::Int16,
67        Self::UInt16,
68        Self::Int32,
69        Self::UInt32,
70        Self::Int64,
71        Self::UInt64,
72        Self::Float32,
73        Self::Float64,
74        Self::Float128,
75    ];
76
77    /// IDL keyword spelling.
78    #[must_use]
79    pub const fn as_str(self) -> &'static str {
80        match self {
81            Self::Boolean => "boolean",
82            Self::Octet => "octet",
83            Self::Char => "char",
84            Self::WChar => "wchar",
85            Self::Int8 => "int8",
86            Self::UInt8 => "uint8",
87            Self::Int16 => "short",
88            Self::UInt16 => "unsigned short",
89            Self::Int32 => "long",
90            Self::UInt32 => "unsigned long",
91            Self::Int64 => "long long",
92            Self::UInt64 => "unsigned long long",
93            Self::Float32 => "float",
94            Self::Float64 => "double",
95            Self::Float128 => "long double",
96        }
97    }
98
99    /// Whether the primitive is an integral type. Bitset holder types
100    /// and enum bit bounds both need this distinction.
101    #[must_use]
102    pub const fn is_integral(self) -> bool {
103        matches!(
104            self,
105            Self::Octet
106                | Self::Int8
107                | Self::UInt8
108                | Self::Int16
109                | Self::UInt16
110                | Self::Int32
111                | Self::UInt32
112                | Self::Int64
113                | Self::UInt64
114        )
115    }
116}
117
118/// Narrow or wide character element.
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120pub enum CharWidth {
121    /// `char` / `string`
122    Char,
123    /// `wchar` / `wstring`
124    WChar,
125}
126
127/// Collection bound.
128///
129/// Retains the expression, not only its value, so the declaration can be
130/// rendered back to source. Spec §3.2.
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132pub enum Bound {
133    /// No bound.
134    Unbounded,
135    /// Bounded by a constant expression.
136    Const(Id<ConstExprNode>),
137}
138
139impl Bound {
140    /// The bounding expression, if any.
141    #[must_use]
142    pub const fn expr(self) -> Option<Id<ConstExprNode>> {
143        match self {
144            Self::Unbounded => None,
145            Self::Const(id) => Some(id),
146        }
147    }
148
149    /// Whether the collection is bounded.
150    #[must_use]
151    pub const fn is_bounded(self) -> bool {
152        matches!(self, Self::Const(_))
153    }
154}
155
156/// Valuetype modifier. Spec §3.2.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub enum ValuetypeKind {
159    /// Plain valuetype.
160    Regular,
161    /// `abstract valuetype`
162    Abstract,
163    /// `custom valuetype`
164    Custom,
165    /// `truncatable`
166    Truncatable,
167}
168
169impl ValuetypeKind {
170    /// Every modifier, for completeness assertions.
171    pub const ALL: [Self; 4] = [
172        Self::Regular,
173        Self::Abstract,
174        Self::Custom,
175        Self::Truncatable,
176    ];
177}
178
179/// Whether a declaration's body has arrived.
180///
181/// A forward declaration is not a separate kind: it is a node whose body
182/// is still `Forward`, so handles taken before the definition arrives
183/// stay valid when it does. Spec §3.2.
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
185pub enum Completeness {
186    /// Declared, body not yet seen.
187    Forward,
188    /// Body present.
189    Complete,
190}
191
192/// What kind of type this is.
193///
194/// Spec §3.2 — the table there is normative and this enum mirrors it row
195/// for row.
196///
197/// Exhaustive on purpose, for the same reason as [`crate::NodeKind`]: a
198/// consumer mapping types onto another representation has to account for
199/// each one, and a catch-all would let a new kind pass as nothing.
200#[derive(Debug, Clone, PartialEq, Eq)]
201pub enum TypeKind {
202    /// Built-in scalar.
203    Primitive(PrimitiveKind),
204    /// `fixed<digits, scale>`.
205    Fixed {
206        /// Total digits.
207        digits: u8,
208        /// Digits after the point.
209        scale: u8,
210    },
211    /// `string` or `wstring`.
212    String {
213        /// Character width.
214        width: CharWidth,
215        /// Optional bound.
216        bound: Bound,
217    },
218    /// `sequence<T>` or `sequence<T, N>`.
219    Sequence {
220        /// Element type.
221        element: Ref<TypeNode>,
222        /// Optional bound.
223        bound: Bound,
224    },
225    /// `T name[d1][d2]`.
226    Array {
227        /// Element type.
228        element: Ref<TypeNode>,
229        /// Dimensions, outermost first.
230        dims: Vec<Id<ConstExprNode>>,
231    },
232    /// `map<K, V>` or `map<K, V, N>`.
233    Map {
234        /// Key type.
235        key: Ref<TypeNode>,
236        /// Value type.
237        value: Ref<TypeNode>,
238        /// Optional bound.
239        bound: Bound,
240    },
241    /// `struct`.
242    Structure {
243        /// Base type for single inheritance.
244        base: Option<Ref<TypeNode>>,
245        /// Members in declaration order.
246        members: Vec<Id<MemberNode>>,
247    },
248    /// `union`.
249    Union {
250        /// Discriminator type.
251        discriminator: Ref<TypeNode>,
252        /// Cases in declaration order.
253        cases: Vec<Id<CaseNode>>,
254        /// The `default:` case, if declared.
255        default_case: Option<Id<CaseNode>>,
256    },
257    /// `enum`.
258    Enumeration {
259        /// Enumerators in declaration order.
260        enumerators: Vec<Id<EnumeratorNode>>,
261    },
262    /// `bitmask`.
263    Bitmask {
264        /// Bit values in declaration order.
265        bits: Vec<Id<BitValueNode>>,
266    },
267    /// `bitset`.
268    Bitset {
269        /// Inherited bitset.
270        base: Option<Ref<TypeNode>>,
271        /// Bitfields in declaration order.
272        fields: Vec<Id<BitfieldNode>>,
273    },
274    /// `typedef`.
275    Alias {
276        /// The aliased type.
277        aliased: Ref<TypeNode>,
278    },
279    /// `valuetype`.
280    Valuetype {
281        /// Modifier.
282        kind: ValuetypeKind,
283        /// Base valuetypes.
284        bases: Vec<Ref<TypeNode>>,
285        /// State members in declaration order.
286        state: Vec<Id<MemberNode>>,
287        /// Initialisers.
288        initializers: Vec<Id<OperationNode>>,
289        /// Supported interfaces.
290        supported: Vec<Ref<InterfaceNode>>,
291    },
292    /// Boxed value.
293    ValueBox {
294        /// The boxed type.
295        boxed: Ref<TypeNode>,
296    },
297    /// `native` — opaque, name only.
298    Native,
299    /// `ValueBase`. OMG IDL 4.2 §7.4.7, rules (131) and (132).
300    ///
301    /// The abstract base every valuetype has, written as a type: a value
302    /// of any valuetype, whichever one arrives. It carries no reference
303    /// for the same reason `Object` carries none — rule (131) is
304    /// `<value_base_type> ::= "ValueBase"` and names nothing — and no
305    /// state, because the base declares none.
306    ///
307    /// CORBA CDR encodes one as a chunked value with its repository id
308    /// ahead of it. DDS-XTypes has neither valuetypes nor this, so the
309    /// layout planner refuses it by name.
310    ValueBase,
311    /// `any`. OMG IDL 4.2 §7.4.2, rules (69) and (70).
312    ///
313    /// A value of any type, carrying its own type description. Building
314    /// Block Any is one of the fifteen and this project takes all
315    /// fifteen, so it is in the model — but it has no wire form under
316    /// DDS-XTypes, which has no `any` at all. CORBA CDR writes a
317    /// `TypeCode` and then the value; that is the only encoding in which
318    /// it means anything, and the layout planner refuses it by name for
319    /// every other one.
320    ///
321    /// It carries no reference: what an `any` holds is decided at run
322    /// time and is not part of the declaration.
323    Any,
324    /// An object reference.
325    ///
326    /// Either the generic one — `Object`, OMG IDL 4.2 §7.4.6 rules (117)
327    /// and (118) — or an interface used as a data type.
328    ObjectRef {
329        /// The referenced interface, absent for the generic `Object`.
330        ///
331        /// Rule (117) is `<object_type> ::= "Object"` and names no
332        /// interface at all, so there is nothing to point at. `None`
333        /// says that, where a reference to an invented `CORBA::Object`
334        /// declaration would say the file had one.
335        interface: Option<Ref<InterfaceNode>>,
336    },
337    /// A type introduced by an `@annotation` declaration.
338    AnnotationType {
339        /// The declaration.
340        definition: Id<AnnotationDefNode>,
341    },
342}
343
344impl TypeKind {
345    /// Short name, for diagnostics.
346    #[must_use]
347    pub const fn as_str(&self) -> &'static str {
348        match self {
349            Self::Primitive(_) => "primitive",
350            Self::Fixed { .. } => "fixed",
351            Self::String { .. } => "string",
352            Self::Sequence { .. } => "sequence",
353            Self::Array { .. } => "array",
354            Self::Map { .. } => "map",
355            Self::Structure { .. } => "struct",
356            Self::Union { .. } => "union",
357            Self::Enumeration { .. } => "enum",
358            Self::Bitmask { .. } => "bitmask",
359            Self::Bitset { .. } => "bitset",
360            Self::Alias { .. } => "typedef",
361            Self::Valuetype { .. } => "valuetype",
362            Self::ValueBox { .. } => "value box",
363            Self::Native => "native",
364            Self::Any => "any",
365            Self::ValueBase => "ValueBase",
366            Self::ObjectRef { .. } => "object reference",
367            Self::AnnotationType { .. } => "annotation type",
368        }
369    }
370
371    /// Whether this kind aggregates members that carry member ids —
372    /// struct and valuetype state. Union cases carry ids through their
373    /// member, which is why unions are not listed here.
374    #[must_use]
375    pub const fn is_member_aggregate(&self) -> bool {
376        matches!(self, Self::Structure { .. } | Self::Valuetype { .. })
377    }
378
379    /// The members of an aggregate, empty for every other kind.
380    #[must_use]
381    pub fn members(&self) -> &[Id<MemberNode>] {
382        match self {
383            Self::Structure { members, .. } => members,
384            Self::Valuetype { state, .. } => state,
385            _ => &[],
386        }
387    }
388
389    /// Every type this kind references directly, resolved or not.
390    ///
391    /// Drives cycle-safe traversal and layout dependency collection.
392    #[must_use]
393    pub fn type_refs(&self) -> Vec<&Ref<TypeNode>> {
394        match self {
395            Self::Sequence { element, .. } | Self::Array { element, .. } => {
396                alloc::vec![element]
397            }
398            Self::Map { key, value, .. } => alloc::vec![key, value],
399            Self::Alias { aliased } => alloc::vec![aliased],
400            Self::ValueBox { boxed } => alloc::vec![boxed],
401            Self::Union { discriminator, .. } => alloc::vec![discriminator],
402            Self::Structure { base, .. } | Self::Bitset { base, .. } => {
403                base.as_ref().map_or_else(Vec::new, |b| alloc::vec![b])
404            }
405            Self::Valuetype { bases, .. } => bases.iter().collect(),
406            _ => Vec::new(),
407        }
408    }
409
410    /// The same references, mutably and in the same order.
411    ///
412    /// The order is the slot numbering the undo log uses to name one
413    /// reference of a kind that holds several, so the two functions have
414    /// to stay in step; both are written as one match over every kind.
415    pub fn type_refs_mut(&mut self) -> Vec<&mut Ref<TypeNode>> {
416        match self {
417            Self::Sequence { element, .. } | Self::Array { element, .. } => {
418                alloc::vec![element]
419            }
420            Self::Map { key, value, .. } => alloc::vec![key, value],
421            Self::Alias { aliased } => alloc::vec![aliased],
422            Self::ValueBox { boxed } => alloc::vec![boxed],
423            Self::Union { discriminator, .. } => alloc::vec![discriminator],
424            Self::Structure { base, .. } | Self::Bitset { base, .. } => {
425                base.as_mut().map_or_else(Vec::new, |b| alloc::vec![b])
426            }
427            Self::Valuetype { bases, .. } => bases.iter_mut().collect(),
428            _ => Vec::new(),
429        }
430    }
431}
432
433impl TypeNode {
434    /// The node's facets. A borrow helper, so a caller iterating types
435    /// does not have to name the field through a temporary.
436    #[must_use]
437    pub const fn facets_ref(&self) -> &Facets {
438        &self.facets
439    }
440}
441
442/// A named type. Spec §3.2.
443#[derive(Debug, Clone, PartialEq, Eq)]
444pub struct TypeNode {
445    /// Stable identity.
446    pub node_id: NodeId,
447    /// Simple name.
448    pub name: String,
449    /// Enclosing module.
450    pub module: Id<ModuleNode>,
451    /// The interface or value type this is declared in, when it is not
452    /// declared directly in a module (OMG IDL 4.2 §7.4.3.4: an interface
453    /// body holds type, constant and exception declarations). Its name is
454    /// a segment of the scoped name and of the repository id:
455    /// `CosNaming::NamingContext::NotFound`.
456    pub scope: Option<String>,
457    /// What kind of type.
458    pub kind: TypeKind,
459    /// Whether the body has arrived.
460    pub completeness: Completeness,
461    /// Per-source information. Spec §4.
462    pub facets: Facets,
463}