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}