Skip to main content

shape_value/v2/
concrete_type.rs

1//! Concrete monomorphized type for v2 runtime.
2//!
3//! `ConcreteType` replaces `SlotKind` with richer type information that flows
4//! from type inference through the bytecode compiler, VM, and JIT. Every local,
5//! parameter, field, return value, and collection element has a `ConcreteType`
6//! at compile time — no unresolved type variables survive past compilation.
7//!
8//! This is the foundation for monomorphization: generic functions like
9//! `map<T, U>` are specialized per `ConcreteType` instantiation.
10
11use serde::{Deserialize, Serialize};
12
13/// Opaque ID into a registry of struct layouts (resolved at compile time).
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct StructLayoutId(pub u32);
16
17/// Opaque ID into a registry of enum layouts (resolved at compile time).
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct EnumLayoutId(pub u32);
20
21/// Opaque ID into a registry of closure capture layouts.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub struct ClosureTypeId(pub u32);
24
25/// Opaque ID into a registry of function signatures.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
27pub struct FunctionTypeId(pub u32);
28
29/// Fully resolved, monomorphized type. No type variables, no generics.
30///
31/// Every expression and local slot in compiled bytecode has exactly one
32/// `ConcreteType`. The compiler resolves all `Type::Variable` and
33/// `Type::Generic` to `ConcreteType` after type inference.
34///
35/// The discriminant is stored as `u8` for compact bytecode encoding.
36#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
37pub enum ConcreteType {
38    /// f64 — the default `number` type.
39    F64,
40    /// f32 — 4-byte single-precision float. ADR-006 §2.7.5 amendment
41    /// (Round 19 S1.5, 2026-05-14): scalar concrete type introduced
42    /// alongside `NativeKind::Float32` for `Array<f32>` v2-raw
43    /// producer paths.
44    F32,
45    /// `char` — 4-byte Unicode scalar (UTF-32 subset of `u32`). ADR-006
46    /// §2.7.5 amendment (Round 19 S1.5, 2026-05-14): scalar concrete
47    /// type introduced alongside `NativeKind::Char` for `Array<char>`
48    /// v2-raw producer paths.
49    Char,
50    /// i64 — the default `int` type (i48 in NaN-boxed representation).
51    I64,
52    /// i32
53    I32,
54    /// i16
55    I16,
56    /// i8
57    I8,
58    /// u64
59    U64,
60    /// u32
61    U32,
62    /// u16
63    U16,
64    /// u8
65    U8,
66    /// bool
67    Bool,
68    /// Interned string (*const StringObj).
69    String,
70    /// Typed struct with compile-time field layout.
71    Struct(StructLayoutId),
72    /// Homogeneous typed array with known element type.
73    /// `Array<number>` → `Array(Box::new(ConcreteType::F64))`.
74    Array(Box<ConcreteType>),
75    /// Typed hash map with known key and value types.
76    HashMap(Box<ConcreteType>, Box<ConcreteType>),
77    /// Nullable type — `T?` / `Option<T>`.
78    Option(Box<ConcreteType>),
79    /// Result type — `Result<T, E>`.
80    Result(Box<ConcreteType>, Box<ConcreteType>),
81    /// Typed enum with compile-time variant layouts.
82    Enum(EnumLayoutId),
83    /// Closure with typed capture slots.
84    Closure(ClosureTypeId),
85    /// Function pointer with known signature.
86    Function(FunctionTypeId),
87    /// Raw typed pointer (for FFI / extern C).
88    Pointer(Box<ConcreteType>),
89    /// Tuple with known element types.
90    Tuple(Vec<ConcreteType>),
91    /// Void (unit) — no value.
92    Void,
93    /// Decimal (rust_decimal::Decimal).
94    Decimal,
95    /// BigInt (arbitrary precision integer).
96    BigInt,
97    /// DateTime.
98    DateTime,
99    // ── Phase 3 cluster-0 Round 11-trinity 11E (2026-05-13) ─────────────
100    //
101    // Collection-container and concurrency-primitive arms surfaced by
102    // Round 10's W12-jit-call-method-shell-rebuild close: the JIT-side
103    // §2.7.5 producing-site conduit cannot stamp parametric return kinds
104    // (`HashMap.get → Option<V>`, `Mutex.get → T`, `Lazy.force → T`) and
105    // the JIT-side `EnumStore` collection-ctor consumer (Round 10) needed
106    // a §2.7.5 in-band classifier rather than an out-of-band MIR-shape
107    // override at `mir_compiler/types.rs:467` — the audit's
108    // "W17-collection-concrete-types tracked follow-up" closing here.
109    //
110    // **Single-discriminator discipline (ADR-005 §1).** None of these
111    // arms project 1:1 to `HeapKind`. Each parametric arm wraps a
112    // `Box<ConcreteType>` for the inner kind (mirror of the existing
113    // `Array(Box<ConcreteType>)` / `HashMap(Box<_>, Box<_>)` shape).
114    // Nullary arms (`PriorityQueue`, `Atomic`) match a §2.7.18 / §2.7.25
115    // landing-time storage decision (i64-only payload) — the ConcreteType
116    // is genuinely nullary at landing, not "stripped to be a discriminator".
117    // When the typed-payload follow-up amendments land (Phase 2c per
118    // §2.7.18 / §2.7.25 "Out-of-scope" notes) these arms grow a
119    // `Box<ConcreteType>` parameter at that point.
120    //
121    /// HashSet with known element type. String-only at landing per
122    /// ADR-006 §2.7.15 (`HeapKind::HashSet`); the inner `ConcreteType`
123    /// is `String` at construction sites today, and the parametric arm
124    /// shape preserves room for the future typed-payload extension.
125    HashSet(Box<ConcreteType>),
126    /// Heterogeneous-element double-ended queue. Storage at the
127    /// `Arc<DequeData>` tier is `VecDeque<Arc<HeapValue>>` (§2.7.17
128    /// Q19 deferral); the ConcreteType inner element kind is the
129    /// per-element kind the producing-site classification stamps when
130    /// the bytecode compiler can prove it (e.g. `Deque<int>` literal
131    /// construction), else `ConcreteType::Void` placeholder.
132    Deque(Box<ConcreteType>),
133    /// i64-priority min-heap. i64-only at landing per ADR-006 §2.7.18
134    /// (`HeapKind::PriorityQueue`); no element-kind variance. Typed-
135    /// payload PriorityQueue is the Phase-2c amendment (§2.7.18
136    /// "Out-of-scope") tracked separately.
137    PriorityQueue,
138    /// MPSC-style channel with typed payload kind. Storage at the
139    /// `Arc<ChannelData>` tier holds `KindedSlot` elements (§2.7.20);
140    /// the ConcreteType inner kind is the element kind for `Channel<T>`
141    /// landings. `ConcreteType::Void` placeholder when the producing
142    /// site can't prove the element kind.
143    Channel(Box<ConcreteType>),
144    /// `Mutex<T>` concurrency primitive — single typed payload protected
145    /// by `Mutex<MutexInner>` per ADR-006 §2.7.25. Inner `ConcreteType`
146    /// is the wrapped value's kind (`Mutex(int)` → `Mutex(I64)`); the
147    /// payload is mutable at runtime and the parametric arm captures the
148    /// declared inner kind for the `m.get() → T` parametric-return
149    /// classifier at the §2.7.5 conduit.
150    Mutex(Box<ConcreteType>),
151    /// `Atomic<i64>` concurrency primitive — wraps
152    /// `std::sync::atomic::AtomicI64` per ADR-006 §2.7.25. i64-only at
153    /// landing per the "typed-payload deferral" precedent
154    /// (W15-priority-queue i64-only, W13-hashset string-only); typed-
155    /// payload `Atomic<T>` is the Phase-2c amendment tracked separately.
156    Atomic,
157    /// `Lazy<T>` initialize-once carrier — wraps an initializer closure
158    /// + cached value slot per ADR-006 §2.7.25. Inner `ConcreteType` is
159    /// the cached value's kind (the closure's return type), enabling the
160    /// `l.get() → T` parametric-return classifier at the §2.7.5 conduit.
161    Lazy(Box<ConcreteType>),
162}
163
164impl ConcreteType {
165    /// Size in bytes for stack storage (all values stored as 8-byte slots).
166    #[inline]
167    pub fn stack_size(&self) -> usize {
168        8 // All values occupy one 8-byte stack slot
169    }
170
171    /// Natural alignment for this type when stored in a struct.
172    #[inline]
173    pub fn alignment(&self) -> usize {
174        match self {
175            ConcreteType::I8 | ConcreteType::U8 | ConcreteType::Bool => 1,
176            ConcreteType::I16 | ConcreteType::U16 => 2,
177            // Round 19 S1.5 (2026-05-14): F32 and Char are 4-byte
178            // scalars per the §2.7.5 amendment.
179            ConcreteType::I32
180            | ConcreteType::U32
181            | ConcreteType::F32
182            | ConcreteType::Char => 4,
183            _ => 8, // f64, i64, u64, pointers, etc.
184        }
185    }
186
187    /// Size in bytes when stored in a struct field (not on stack).
188    #[inline]
189    pub fn field_size(&self) -> usize {
190        match self {
191            ConcreteType::I8 | ConcreteType::U8 | ConcreteType::Bool => 1,
192            ConcreteType::I16 | ConcreteType::U16 => 2,
193            // Round 19 S1.5 (2026-05-14): F32 and Char are 4-byte
194            // scalars per the §2.7.5 amendment.
195            ConcreteType::I32
196            | ConcreteType::U32
197            | ConcreteType::F32
198            | ConcreteType::Char => 4,
199            _ => 8,
200        }
201    }
202
203    /// Whether this type is a numeric type (integer or float).
204    #[inline]
205    pub fn is_numeric(&self) -> bool {
206        matches!(
207            self,
208            ConcreteType::F64
209                | ConcreteType::F32
210                | ConcreteType::I64
211                | ConcreteType::I32
212                | ConcreteType::I16
213                | ConcreteType::I8
214                | ConcreteType::U64
215                | ConcreteType::U32
216                | ConcreteType::U16
217                | ConcreteType::U8
218                | ConcreteType::Decimal
219                | ConcreteType::BigInt
220        )
221    }
222
223    /// Whether this type is an integer type.
224    #[inline]
225    pub fn is_integer(&self) -> bool {
226        matches!(
227            self,
228            ConcreteType::I64
229                | ConcreteType::I32
230                | ConcreteType::I16
231                | ConcreteType::I8
232                | ConcreteType::U64
233                | ConcreteType::U32
234                | ConcreteType::U16
235                | ConcreteType::U8
236        )
237    }
238
239    /// Whether this type is a heap-allocated reference type.
240    #[inline]
241    pub fn is_heap(&self) -> bool {
242        matches!(
243            self,
244            ConcreteType::String
245                | ConcreteType::Struct(_)
246                | ConcreteType::Array(_)
247                | ConcreteType::HashMap(_, _)
248                | ConcreteType::Enum(_)
249                | ConcreteType::Closure(_)
250                | ConcreteType::Pointer(_)
251                | ConcreteType::BigInt
252                | ConcreteType::Decimal
253                | ConcreteType::DateTime
254                // ── Phase 3 cluster-0 Round 11-trinity 11E ─────────────
255                // All Round-11 collection / concurrency carriers are
256                // heap-allocated (typed Arc<XData> per §2.7.15 /
257                // §2.7.17-§2.7.20 / §2.7.25). Mirror of the existing
258                // Array / HashMap heap classification.
259                | ConcreteType::HashSet(_)
260                | ConcreteType::Deque(_)
261                | ConcreteType::PriorityQueue
262                | ConcreteType::Channel(_)
263                | ConcreteType::Mutex(_)
264                | ConcreteType::Atomic
265                | ConcreteType::Lazy(_)
266        )
267    }
268
269    /// Whether this is a primitive scalar that fits in a register.
270    #[inline]
271    pub fn is_scalar(&self) -> bool {
272        matches!(
273            self,
274            ConcreteType::F64
275                | ConcreteType::F32
276                | ConcreteType::I64
277                | ConcreteType::I32
278                | ConcreteType::I16
279                | ConcreteType::I8
280                | ConcreteType::U64
281                | ConcreteType::U32
282                | ConcreteType::U16
283                | ConcreteType::U8
284                | ConcreteType::Bool
285                | ConcreteType::Char
286        )
287    }
288
289    /// Convert to the corresponding `FieldKind` for struct layout computation.
290    pub fn to_field_kind(&self) -> super::struct_layout::FieldKind {
291        use super::struct_layout::FieldKind;
292        match self {
293            ConcreteType::F64 => FieldKind::F64,
294            ConcreteType::I64 => FieldKind::I64,
295            ConcreteType::I32 => FieldKind::I32,
296            ConcreteType::I16 => FieldKind::I16,
297            ConcreteType::I8 => FieldKind::I8,
298            ConcreteType::U64 => FieldKind::U64,
299            ConcreteType::U32 => FieldKind::U32,
300            ConcreteType::U16 => FieldKind::U16,
301            ConcreteType::U8 => FieldKind::U8,
302            ConcreteType::Bool => FieldKind::Bool,
303            // Round 19 S1.5 (2026-05-14): F32 and Char are 4-byte
304            // scalars per the §2.7.5 amendment. FieldKind has no
305            // dedicated F32 / Char variants, so the bit-equivalent
306            // 4-byte FieldKind::U32 is the struct-layout carrier (size
307            // + alignment + load/store width all match). Semantic
308            // float-vs-bits / codepoint-vs-bits distinction is preserved
309            // at the NativeKind layer, NOT the struct-layout layer.
310            // FieldKind cardinality extension is a follow-up sub-cluster
311            // (cluster-1 hardening) if struct-field-layout typing of F32
312            // / Char becomes load-bearing.
313            ConcreteType::F32 | ConcreteType::Char => FieldKind::U32,
314            // All reference/heap types are pointer-sized
315            _ => FieldKind::Ptr,
316        }
317    }
318
319    /// Generate a monomorphization key string for specialization caching.
320    /// e.g., `"f64"`, `"array_i64"`, `"hashmap_string_f64"`
321    pub fn mono_key(&self) -> String {
322        match self {
323            ConcreteType::F64 => "f64".into(),
324            ConcreteType::F32 => "f32".into(),
325            ConcreteType::Char => "char".into(),
326            ConcreteType::I64 => "i64".into(),
327            ConcreteType::I32 => "i32".into(),
328            ConcreteType::I16 => "i16".into(),
329            ConcreteType::I8 => "i8".into(),
330            ConcreteType::U64 => "u64".into(),
331            ConcreteType::U32 => "u32".into(),
332            ConcreteType::U16 => "u16".into(),
333            ConcreteType::U8 => "u8".into(),
334            ConcreteType::Bool => "bool".into(),
335            ConcreteType::String => "string".into(),
336            ConcreteType::Struct(id) => format!("struct_{}", id.0),
337            ConcreteType::Array(elem) => format!("array_{}", elem.mono_key()),
338            ConcreteType::HashMap(k, v) => {
339                format!("hashmap_{}_{}", k.mono_key(), v.mono_key())
340            }
341            ConcreteType::Option(inner) => format!("option_{}", inner.mono_key()),
342            ConcreteType::Result(ok, err) => {
343                format!("result_{}_{}", ok.mono_key(), err.mono_key())
344            }
345            ConcreteType::Enum(id) => format!("enum_{}", id.0),
346            ConcreteType::Closure(id) => format!("closure_{}", id.0),
347            ConcreteType::Function(id) => format!("fn_{}", id.0),
348            ConcreteType::Pointer(inner) => format!("ptr_{}", inner.mono_key()),
349            ConcreteType::Tuple(elems) => {
350                let parts: Vec<_> = elems.iter().map(|e| e.mono_key()).collect();
351                format!("tuple_{}", parts.join("_"))
352            }
353            ConcreteType::Void => "void".into(),
354            ConcreteType::Decimal => "decimal".into(),
355            ConcreteType::BigInt => "bigint".into(),
356            ConcreteType::DateTime => "datetime".into(),
357            // ── Phase 3 cluster-0 Round 11-trinity 11E ─────────────────
358            ConcreteType::HashSet(elem) => format!("hashset_{}", elem.mono_key()),
359            ConcreteType::Deque(elem) => format!("deque_{}", elem.mono_key()),
360            ConcreteType::PriorityQueue => "priority_queue".into(),
361            ConcreteType::Channel(elem) => format!("channel_{}", elem.mono_key()),
362            ConcreteType::Mutex(inner) => format!("mutex_{}", inner.mono_key()),
363            ConcreteType::Atomic => "atomic".into(),
364            ConcreteType::Lazy(inner) => format!("lazy_{}", inner.mono_key()),
365        }
366    }
367
368    /// Compact type tag for bytecode encoding (single byte).
369    pub fn type_tag(&self) -> u8 {
370        match self {
371            ConcreteType::F64 => 0,
372            ConcreteType::I64 => 1,
373            ConcreteType::I32 => 2,
374            ConcreteType::I16 => 3,
375            ConcreteType::I8 => 4,
376            ConcreteType::U64 => 5,
377            ConcreteType::U32 => 6,
378            ConcreteType::U16 => 7,
379            ConcreteType::U8 => 8,
380            ConcreteType::Bool => 9,
381            ConcreteType::String => 10,
382            ConcreteType::Struct(_) => 11,
383            ConcreteType::Array(_) => 12,
384            ConcreteType::HashMap(_, _) => 13,
385            ConcreteType::Option(_) => 14,
386            ConcreteType::Result(_, _) => 15,
387            ConcreteType::Enum(_) => 16,
388            ConcreteType::Closure(_) => 17,
389            ConcreteType::Function(_) => 18,
390            ConcreteType::Pointer(_) => 19,
391            ConcreteType::Tuple(_) => 20,
392            ConcreteType::Void => 21,
393            ConcreteType::Decimal => 22,
394            ConcreteType::BigInt => 23,
395            ConcreteType::DateTime => 24,
396            // ── Phase 3 cluster-0 Round 11-trinity 11E ─────────────────
397            ConcreteType::HashSet(_) => 25,
398            ConcreteType::Deque(_) => 26,
399            ConcreteType::PriorityQueue => 27,
400            ConcreteType::Channel(_) => 28,
401            ConcreteType::Mutex(_) => 29,
402            ConcreteType::Atomic => 30,
403            ConcreteType::Lazy(_) => 31,
404            // ── Round 19 S1.5 W12-nativekind-scalar-additions ──────────
405            // (2026-05-14) — ADR-006 §2.7.5 amendment adds F32 + Char
406            // as 4-byte scalar concrete types. Tags 32 and 33 allocated
407            // contiguously after the Round-11 collection/concurrency arms.
408            ConcreteType::F32 => 32,
409            ConcreteType::Char => 33,
410        }
411    }
412}
413
414/// Convert from `FieldKind` (struct layout) to `ConcreteType`.
415impl From<super::struct_layout::FieldKind> for ConcreteType {
416    fn from(fk: super::struct_layout::FieldKind) -> Self {
417        use super::struct_layout::FieldKind;
418        match fk {
419            FieldKind::F64 => ConcreteType::F64,
420            FieldKind::I64 => ConcreteType::I64,
421            FieldKind::I32 => ConcreteType::I32,
422            FieldKind::I16 => ConcreteType::I16,
423            FieldKind::I8 => ConcreteType::I8,
424            FieldKind::U64 => ConcreteType::U64,
425            FieldKind::U32 => ConcreteType::U32,
426            FieldKind::U16 => ConcreteType::U16,
427            FieldKind::U8 => ConcreteType::U8,
428            FieldKind::Bool => ConcreteType::Bool,
429            // Ptr is an opaque pointer — caller must know the pointed-to type
430            FieldKind::Ptr => ConcreteType::Pointer(Box::new(ConcreteType::Void)),
431        }
432    }
433}
434
435impl std::fmt::Display for ConcreteType {
436    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437        match self {
438            ConcreteType::F64 => write!(f, "number"),
439            ConcreteType::F32 => write!(f, "f32"),
440            ConcreteType::Char => write!(f, "char"),
441            ConcreteType::I64 => write!(f, "int"),
442            ConcreteType::I32 => write!(f, "i32"),
443            ConcreteType::I16 => write!(f, "i16"),
444            ConcreteType::I8 => write!(f, "i8"),
445            ConcreteType::U64 => write!(f, "u64"),
446            ConcreteType::U32 => write!(f, "u32"),
447            ConcreteType::U16 => write!(f, "u16"),
448            ConcreteType::U8 => write!(f, "u8"),
449            ConcreteType::Bool => write!(f, "bool"),
450            ConcreteType::String => write!(f, "string"),
451            ConcreteType::Struct(id) => write!(f, "Struct#{}", id.0),
452            ConcreteType::Array(elem) => write!(f, "Array<{elem}>"),
453            ConcreteType::HashMap(k, v) => write!(f, "HashMap<{k}, {v}>"),
454            ConcreteType::Option(inner) => write!(f, "{inner}?"),
455            ConcreteType::Result(ok, err) => write!(f, "Result<{ok}, {err}>"),
456            ConcreteType::Enum(id) => write!(f, "Enum#{}", id.0),
457            ConcreteType::Closure(id) => write!(f, "Closure#{}", id.0),
458            ConcreteType::Function(id) => write!(f, "Function#{}", id.0),
459            ConcreteType::Pointer(inner) => write!(f, "ptr<{inner}>"),
460            ConcreteType::Tuple(elems) => {
461                write!(f, "(")?;
462                for (i, e) in elems.iter().enumerate() {
463                    if i > 0 {
464                        write!(f, ", ")?;
465                    }
466                    write!(f, "{e}")?;
467                }
468                write!(f, ")")
469            }
470            ConcreteType::Void => write!(f, "void"),
471            ConcreteType::Decimal => write!(f, "decimal"),
472            ConcreteType::BigInt => write!(f, "bigint"),
473            ConcreteType::DateTime => write!(f, "DateTime"),
474            // ── Phase 3 cluster-0 Round 11-trinity 11E ─────────────────
475            ConcreteType::HashSet(elem) => write!(f, "HashSet<{elem}>"),
476            ConcreteType::Deque(elem) => write!(f, "Deque<{elem}>"),
477            ConcreteType::PriorityQueue => write!(f, "PriorityQueue"),
478            ConcreteType::Channel(elem) => write!(f, "Channel<{elem}>"),
479            ConcreteType::Mutex(inner) => write!(f, "Mutex<{inner}>"),
480            ConcreteType::Atomic => write!(f, "Atomic"),
481            ConcreteType::Lazy(inner) => write!(f, "Lazy<{inner}>"),
482        }
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use super::*;
489
490    #[test]
491    fn test_mono_key_primitives() {
492        assert_eq!(ConcreteType::F64.mono_key(), "f64");
493        assert_eq!(ConcreteType::I64.mono_key(), "i64");
494        assert_eq!(ConcreteType::Bool.mono_key(), "bool");
495        assert_eq!(ConcreteType::String.mono_key(), "string");
496    }
497
498    #[test]
499    fn test_mono_key_composites() {
500        let arr_f64 = ConcreteType::Array(Box::new(ConcreteType::F64));
501        assert_eq!(arr_f64.mono_key(), "array_f64");
502
503        let map = ConcreteType::HashMap(
504            Box::new(ConcreteType::String),
505            Box::new(ConcreteType::I64),
506        );
507        assert_eq!(map.mono_key(), "hashmap_string_i64");
508
509        let nested = ConcreteType::Array(Box::new(ConcreteType::Array(Box::new(
510            ConcreteType::I32,
511        ))));
512        assert_eq!(nested.mono_key(), "array_array_i32");
513    }
514
515    #[test]
516    fn test_type_tags_unique() {
517        let types = vec![
518            ConcreteType::F64,
519            ConcreteType::I64,
520            ConcreteType::I32,
521            ConcreteType::I16,
522            ConcreteType::I8,
523            ConcreteType::U64,
524            ConcreteType::U32,
525            ConcreteType::U16,
526            ConcreteType::U8,
527            ConcreteType::Bool,
528            ConcreteType::String,
529            ConcreteType::Struct(StructLayoutId(0)),
530            ConcreteType::Array(Box::new(ConcreteType::F64)),
531            ConcreteType::HashMap(Box::new(ConcreteType::String), Box::new(ConcreteType::F64)),
532            ConcreteType::Option(Box::new(ConcreteType::I64)),
533            ConcreteType::Result(Box::new(ConcreteType::I64), Box::new(ConcreteType::String)),
534            ConcreteType::Enum(EnumLayoutId(0)),
535            ConcreteType::Closure(ClosureTypeId(0)),
536            ConcreteType::Function(FunctionTypeId(0)),
537            ConcreteType::Pointer(Box::new(ConcreteType::U8)),
538            ConcreteType::Tuple(vec![ConcreteType::I64, ConcreteType::F64]),
539            ConcreteType::Void,
540            ConcreteType::Decimal,
541            ConcreteType::BigInt,
542            ConcreteType::DateTime,
543            // ── Phase 3 cluster-0 Round 11-trinity 11E ─────────────────
544            ConcreteType::HashSet(Box::new(ConcreteType::String)),
545            ConcreteType::Deque(Box::new(ConcreteType::I64)),
546            ConcreteType::PriorityQueue,
547            ConcreteType::Channel(Box::new(ConcreteType::I64)),
548            ConcreteType::Mutex(Box::new(ConcreteType::I64)),
549            ConcreteType::Atomic,
550            ConcreteType::Lazy(Box::new(ConcreteType::I64)),
551            // ── Round 19 S1.5 W12-nativekind-scalar-additions ─────────
552            ConcreteType::F32,
553            ConcreteType::Char,
554        ];
555        let tags: Vec<u8> = types.iter().map(|t| t.type_tag()).collect();
556        let unique: std::collections::HashSet<u8> = tags.iter().copied().collect();
557        assert_eq!(tags.len(), unique.len(), "type tags must be unique");
558    }
559
560    /// Round 19 S1.5 (2026-05-14): F32 + Char additions ride the same
561    /// scalar dispatch shape as the existing 4-byte scalars (I32 / U32).
562    #[test]
563    fn test_round_19_f32_char_scalars() {
564        assert_eq!(ConcreteType::F32.mono_key(), "f32");
565        assert_eq!(ConcreteType::Char.mono_key(), "char");
566        assert_eq!(format!("{}", ConcreteType::F32), "f32");
567        assert_eq!(format!("{}", ConcreteType::Char), "char");
568        // 4-byte alignment + field-size match I32 / U32.
569        assert_eq!(ConcreteType::F32.alignment(), 4);
570        assert_eq!(ConcreteType::F32.field_size(), 4);
571        assert_eq!(ConcreteType::Char.alignment(), 4);
572        assert_eq!(ConcreteType::Char.field_size(), 4);
573        // Both are scalar; F32 is numeric (float-family), Char is NOT
574        // numeric (UTF-32 codepoint, not a numeric type).
575        assert!(ConcreteType::F32.is_scalar());
576        assert!(ConcreteType::Char.is_scalar());
577        assert!(ConcreteType::F32.is_numeric());
578        assert!(!ConcreteType::Char.is_numeric());
579        assert!(!ConcreteType::F32.is_integer());
580        assert!(!ConcreteType::Char.is_integer());
581        // Neither is heap-allocated.
582        assert!(!ConcreteType::F32.is_heap());
583        assert!(!ConcreteType::Char.is_heap());
584    }
585
586    #[test]
587    fn test_round_11_collection_concurrency_arms_mono_key_and_display() {
588        // Phase 3 cluster-0 Round 11-trinity 11E: collection/concurrency
589        // arms round-trip through mono_key and Display.
590        let hs = ConcreteType::HashSet(Box::new(ConcreteType::String));
591        assert_eq!(hs.mono_key(), "hashset_string");
592        assert_eq!(format!("{hs}"), "HashSet<string>");
593
594        let dq = ConcreteType::Deque(Box::new(ConcreteType::I64));
595        assert_eq!(dq.mono_key(), "deque_i64");
596        assert_eq!(format!("{dq}"), "Deque<int>");
597
598        let pq = ConcreteType::PriorityQueue;
599        assert_eq!(pq.mono_key(), "priority_queue");
600        assert_eq!(format!("{pq}"), "PriorityQueue");
601
602        let ch = ConcreteType::Channel(Box::new(ConcreteType::I64));
603        assert_eq!(ch.mono_key(), "channel_i64");
604        assert_eq!(format!("{ch}"), "Channel<int>");
605
606        let mx = ConcreteType::Mutex(Box::new(ConcreteType::I64));
607        assert_eq!(mx.mono_key(), "mutex_i64");
608        assert_eq!(format!("{mx}"), "Mutex<int>");
609
610        let at = ConcreteType::Atomic;
611        assert_eq!(at.mono_key(), "atomic");
612        assert_eq!(format!("{at}"), "Atomic");
613
614        let lz = ConcreteType::Lazy(Box::new(ConcreteType::Bool));
615        assert_eq!(lz.mono_key(), "lazy_bool");
616        assert_eq!(format!("{lz}"), "Lazy<bool>");
617    }
618
619    #[test]
620    fn test_round_11_arms_are_heap() {
621        // All 7 new arms are heap-allocated typed-Arc carriers per
622        // ADR-006 §2.7.15 / §2.7.17-§2.7.20 / §2.7.25.
623        assert!(ConcreteType::HashSet(Box::new(ConcreteType::String)).is_heap());
624        assert!(ConcreteType::Deque(Box::new(ConcreteType::I64)).is_heap());
625        assert!(ConcreteType::PriorityQueue.is_heap());
626        assert!(ConcreteType::Channel(Box::new(ConcreteType::I64)).is_heap());
627        assert!(ConcreteType::Mutex(Box::new(ConcreteType::I64)).is_heap());
628        assert!(ConcreteType::Atomic.is_heap());
629        assert!(ConcreteType::Lazy(Box::new(ConcreteType::I64)).is_heap());
630
631        // None are scalar / numeric / integer.
632        assert!(!ConcreteType::HashSet(Box::new(ConcreteType::String)).is_scalar());
633        assert!(!ConcreteType::Mutex(Box::new(ConcreteType::I64)).is_numeric());
634        assert!(!ConcreteType::Atomic.is_integer());
635    }
636
637    #[test]
638    fn test_field_kind_roundtrip() {
639        use super::super::struct_layout::FieldKind;
640        let kinds = [
641            FieldKind::F64,
642            FieldKind::I64,
643            FieldKind::I32,
644            FieldKind::I16,
645            FieldKind::I8,
646            FieldKind::U64,
647            FieldKind::U32,
648            FieldKind::U16,
649            FieldKind::U8,
650            FieldKind::Bool,
651        ];
652        for kind in kinds {
653            let ct = ConcreteType::from(kind);
654            let back = ct.to_field_kind();
655            assert_eq!(kind, back);
656        }
657    }
658
659    #[test]
660    fn test_is_numeric() {
661        assert!(ConcreteType::F64.is_numeric());
662        assert!(ConcreteType::I64.is_numeric());
663        assert!(ConcreteType::U8.is_numeric());
664        assert!(ConcreteType::Decimal.is_numeric());
665        assert!(!ConcreteType::Bool.is_numeric());
666        assert!(!ConcreteType::String.is_numeric());
667    }
668
669    #[test]
670    fn test_is_heap() {
671        assert!(ConcreteType::String.is_heap());
672        assert!(ConcreteType::Array(Box::new(ConcreteType::F64)).is_heap());
673        assert!(!ConcreteType::F64.is_heap());
674        assert!(!ConcreteType::Bool.is_heap());
675    }
676
677    #[test]
678    fn test_display() {
679        assert_eq!(format!("{}", ConcreteType::F64), "number");
680        assert_eq!(format!("{}", ConcreteType::I64), "int");
681        assert_eq!(
682            format!("{}", ConcreteType::Array(Box::new(ConcreteType::F64))),
683            "Array<number>"
684        );
685        assert_eq!(
686            format!("{}", ConcreteType::Option(Box::new(ConcreteType::I64))),
687            "int?"
688        );
689    }
690}