Skip to main content

gruel_air/
types.rs

1//! Type system for Gruel.
2//!
3//! Currently very minimal - just i32. Will be extended as the language grows.
4
5use gruel_builtins::{Posture, ThreadSafety};
6
7/// A unique identifier for a struct definition.
8///
9/// As of Phase 3 (ADR-0024), the inner value is a pool index into `TypeInternPool`,
10/// not a vector index into a separate struct definitions array.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
12pub struct StructId(pub u32);
13
14impl StructId {
15    /// Create a StructId from a pool index.
16    ///
17    /// This is the primary way to create StructIds during Phase 3+.
18    /// The pool index is the raw index into `TypeInternPool.types`.
19    #[inline]
20    pub fn from_pool_index(pool_index: u32) -> Self {
21        StructId(pool_index)
22    }
23
24    /// Get the pool index for this struct.
25    ///
26    /// This is the index into `TypeInternPool.types`.
27    #[inline]
28    pub fn pool_index(self) -> u32 {
29        self.0
30    }
31}
32
33/// A unique identifier for an enum definition.
34///
35/// As of Phase 3 (ADR-0024), the inner value is a pool index into `TypeInternPool`,
36/// not a vector index into a separate enum definitions array.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
38pub struct EnumId(pub u32);
39
40impl EnumId {
41    /// Create an EnumId from a pool index.
42    ///
43    /// This is the primary way to create EnumIds during Phase 3+.
44    /// The pool index is the raw index into `TypeInternPool.types`.
45    #[inline]
46    pub fn from_pool_index(pool_index: u32) -> Self {
47        EnumId(pool_index)
48    }
49
50    /// Get the pool index for this enum.
51    ///
52    /// This is the index into `TypeInternPool.types`.
53    #[inline]
54    pub fn pool_index(self) -> u32 {
55        self.0
56    }
57}
58
59/// A unique identifier for an array type.
60/// This is needed because Type is Copy, so we can't use Box<Type> for the element type.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
62pub struct ArrayTypeId(pub u32);
63
64impl ArrayTypeId {
65    /// Create an ArrayTypeId from a pool index.
66    ///
67    /// This is used during Phase 2B to create ArrayTypeIds from pool indices.
68    /// The pool index is the raw index into `TypeInternPool.types`.
69    #[inline]
70    pub fn from_pool_index(pool_index: u32) -> Self {
71        ArrayTypeId(pool_index)
72    }
73
74    /// Get the pool index for this array type.
75    ///
76    /// Returns the raw index into the TypeInternPool.
77    #[inline]
78    pub fn pool_index(self) -> u32 {
79        self.0
80    }
81}
82
83/// A unique identifier for a `ptr const T` type.
84/// This is needed because Type is Copy, so we can't use Box<Type> for the pointee type.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
86pub struct PtrConstTypeId(pub u32);
87
88impl PtrConstTypeId {
89    /// Create a PtrConstTypeId from a pool index.
90    #[inline]
91    pub fn from_pool_index(pool_index: u32) -> Self {
92        PtrConstTypeId(pool_index)
93    }
94
95    /// Get the pool index for this pointer type.
96    #[inline]
97    pub fn pool_index(self) -> u32 {
98        self.0
99    }
100}
101
102/// A unique identifier for a `ptr mut T` type.
103/// This is needed because Type is Copy, so we can't use Box<Type> for the pointee type.
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
105pub struct PtrMutTypeId(pub u32);
106
107impl PtrMutTypeId {
108    /// Create a PtrMutTypeId from a pool index.
109    #[inline]
110    pub fn from_pool_index(pool_index: u32) -> Self {
111        PtrMutTypeId(pool_index)
112    }
113
114    /// Get the pool index for this pointer type.
115    #[inline]
116    pub fn pool_index(self) -> u32 {
117        self.0
118    }
119}
120
121/// A unique identifier for a `Ref(T)` type (ADR-0062).
122/// Mirrors `PtrConstTypeId` — uses pool indices for type interning.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
124pub struct RefTypeId(pub u32);
125
126impl RefTypeId {
127    #[inline]
128    pub fn from_pool_index(pool_index: u32) -> Self {
129        RefTypeId(pool_index)
130    }
131
132    #[inline]
133    pub fn pool_index(self) -> u32 {
134        self.0
135    }
136}
137
138/// A unique identifier for a `MutRef(T)` type (ADR-0062).
139/// Mirrors `PtrMutTypeId`.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
141pub struct MutRefTypeId(pub u32);
142
143impl MutRefTypeId {
144    #[inline]
145    pub fn from_pool_index(pool_index: u32) -> Self {
146        MutRefTypeId(pool_index)
147    }
148
149    #[inline]
150    pub fn pool_index(self) -> u32 {
151        self.0
152    }
153}
154
155/// A unique identifier for a `Slice(T)` type (ADR-0064).
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
157pub struct SliceTypeId(pub u32);
158
159impl SliceTypeId {
160    #[inline]
161    pub fn from_pool_index(pool_index: u32) -> Self {
162        SliceTypeId(pool_index)
163    }
164
165    #[inline]
166    pub fn pool_index(self) -> u32 {
167        self.0
168    }
169}
170
171/// A unique identifier for a `MutSlice(T)` type (ADR-0064).
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
173pub struct MutSliceTypeId(pub u32);
174
175impl MutSliceTypeId {
176    #[inline]
177    pub fn from_pool_index(pool_index: u32) -> Self {
178        MutSliceTypeId(pool_index)
179    }
180
181    #[inline]
182    pub fn pool_index(self) -> u32 {
183        self.0
184    }
185}
186
187/// A unique identifier for a `Vec(T)` type (ADR-0066).
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
189pub struct VecTypeId(pub u32);
190
191impl VecTypeId {
192    #[inline]
193    pub fn from_pool_index(pool_index: u32) -> Self {
194        VecTypeId(pool_index)
195    }
196
197    #[inline]
198    pub fn pool_index(self) -> u32 {
199        self.0
200    }
201}
202
203/// A unique identifier for an interface declaration (ADR-0056).
204///
205/// Mirrors `StructId` / `EnumId`: the inner value is a pool index into
206/// `TypeInternPool`. Interfaces are nominal in the sense that two `interface`
207/// declarations with the same name still produce distinct IDs (and we reject
208/// that at gather time); structural conformance happens at the *use* site
209/// against this nominal ID.
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
211pub struct InterfaceId(pub u32);
212
213impl InterfaceId {
214    #[inline]
215    pub fn from_pool_index(pool_index: u32) -> Self {
216        InterfaceId(pool_index)
217    }
218
219    #[inline]
220    pub fn pool_index(self) -> u32 {
221        self.0
222    }
223}
224
225/// A unique identifier for a module (imported file).
226///
227/// Modules are created by `@import("path.gruel")` and represent the public
228/// declarations of an imported file.
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
230pub struct ModuleId(pub u32);
231
232impl ModuleId {
233    /// Create a ModuleId from an index.
234    #[inline]
235    pub fn new(index: u32) -> Self {
236        ModuleId(index)
237    }
238
239    /// Get the index for this module.
240    #[inline]
241    pub fn index(self) -> u32 {
242        self.0
243    }
244}
245
246/// The kind of a type - used for pattern matching.
247///
248/// This enum mirrors the structure of the `Type` enum but is designed for
249/// pattern matching. During the migration to `Type(InternedType)`, code that
250/// pattern matches on types will use `ty.kind()` to get a `TypeKind`.
251///
252/// This separation allows incremental migration: all pattern matches can be
253/// updated to use `.kind()` while `Type` is still an enum, then `Type` can be
254/// replaced with `Type(InternedType)` without breaking existing code.
255#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
256pub enum TypeKind {
257    /// 8-bit signed integer
258    I8,
259    /// 16-bit signed integer
260    I16,
261    /// 32-bit signed integer
262    I32,
263    /// 64-bit signed integer
264    I64,
265    /// 8-bit unsigned integer
266    U8,
267    /// 16-bit unsigned integer
268    U16,
269    /// 32-bit unsigned integer
270    U32,
271    /// 64-bit unsigned integer
272    U64,
273    /// Pointer-sized signed integer
274    Isize,
275    /// Pointer-sized unsigned integer
276    Usize,
277    /// IEEE 754 binary16 (half-precision float)
278    F16,
279    /// IEEE 754 binary32 (single-precision float)
280    F32,
281    /// IEEE 754 binary64 (double-precision float)
282    F64,
283    /// Boolean
284    Bool,
285    /// Unicode scalar value (ADR-0071)
286    Char,
287    /// The unit type (for functions that don't return a value)
288    Unit,
289    /// ADR-0086: C `signed char` — target-resolved (8-bit signed on every blessed target).
290    CSchar,
291    /// ADR-0086: C `short` — target-resolved (16-bit signed on every blessed target).
292    CShort,
293    /// ADR-0086: C `int` — target-resolved (32-bit signed on every blessed target).
294    /// Canonical discriminant type for `@mark(c) enum`.
295    CInt,
296    /// ADR-0086: C `long` — target-resolved (64-bit signed on every blessed LP64 target;
297    /// 32-bit on hypothetical LLP64 targets like Windows).
298    CLong,
299    /// ADR-0086: C `long long` — target-resolved (64-bit signed on every blessed target).
300    CLonglong,
301    /// ADR-0086: C `unsigned char` — target-resolved (8-bit unsigned on every blessed target).
302    CUchar,
303    /// ADR-0086: C `unsigned short` — target-resolved (16-bit unsigned on every blessed target).
304    CUshort,
305    /// ADR-0086: C `unsigned int` — target-resolved (32-bit unsigned on every blessed target).
306    CUint,
307    /// ADR-0086: C `unsigned long` — target-resolved (64-bit unsigned on every blessed LP64 target).
308    CUlong,
309    /// ADR-0086: C `unsigned long long` — target-resolved (64-bit unsigned on every blessed target).
310    CUlonglong,
311    /// ADR-0086: C `float` — IEEE 754 binary32 on every blessed target.
312    CFloat,
313    /// ADR-0086: C `double` — IEEE 754 binary64 on every blessed target.
314    CDouble,
315    /// ADR-0086: C `void` — incomplete type. No values, no size, no alignment.
316    /// Only usable through `Ptr(c_void)` / `MutPtr(c_void)`. Cannot appear in
317    /// value-bearing positions (let, return, parameter by value, struct/enum field
318    /// by value). Use `()` (unit) for C `void` return types.
319    CVoid,
320    /// User-defined struct type
321    Struct(StructId),
322    /// User-defined enum type
323    Enum(EnumId),
324    /// User-defined interface type (ADR-0056). Used both as the bound on a
325    /// `comptime T: I` parameter and (Phase 4) as a runtime type behind a
326    /// borrowing parameter.
327    Interface(InterfaceId),
328    /// Fixed-size array type: [T; N]
329    Array(ArrayTypeId),
330    /// Raw pointer to immutable data: ptr const T
331    PtrConst(PtrConstTypeId),
332    /// Raw pointer to mutable data: ptr mut T
333    PtrMut(PtrMutTypeId),
334    /// Immutable reference (ADR-0062): `Ref(T)` — scope-bound, exclusivity-checked.
335    Ref(RefTypeId),
336    /// Mutable reference (ADR-0062): `MutRef(T)` — scope-bound, exclusive.
337    MutRef(MutRefTypeId),
338    /// Immutable slice (ADR-0064): `Slice(T)` — scope-bound fat pointer `{ptr, len}`.
339    Slice(SliceTypeId),
340    /// Mutable slice (ADR-0064): `MutSlice(T)` — scope-bound exclusive fat pointer.
341    MutSlice(MutSliceTypeId),
342    /// Owned, growable vector (ADR-0066): `Vec(T)` — heap-allocated `{ptr, len, cap}`.
343    Vec(VecTypeId),
344    /// A module type (from @import)
345    Module(ModuleId),
346    /// An error type (used during type checking to continue after errors)
347    Error,
348    /// The never type - represents computations that don't return
349    Never,
350    /// The comptime type - the type of types themselves
351    ComptimeType,
352    /// The comptime string type - compile-time only string values
353    ComptimeStr,
354    /// The comptime integer type - compile-time only integer values
355    ComptimeInt,
356}
357
358/// A type in the Gruel type system.
359///
360/// After Phase 4.1 of ADR-0024, `Type` is a newtype wrapping a u32 index.
361/// This enables O(1) type equality via u32 comparison.
362///
363/// # Encoding
364///
365/// The u32 value uses a tag-based encoding:
366/// - Primitives (0-20): I8=0, I16=1, I32=2, I64=3, U8=4, U16=5, U32=6, U64=7,
367///   Isize=8, Usize=9, F16=10, F32=11, F64=12,
368///   Bool=13, Unit=14, Error=15, Never=16, ComptimeType=17, ComptimeStr=18,
369///   ComptimeInt=19, Char=20.
370/// - ADR-0086 C named primitives (21-33): CSchar=21, CShort=22, CInt=23,
371///   CLong=24, CLonglong=25, CUchar=26, CUshort=27, CUint=28, CUlong=29,
372///   CUlonglong=30, CFloat=31, CDouble=32, CVoid=33. Signed integers are
373///   contiguous at 21–25, unsigned at 26–30, floats at 31–32, the incomplete
374///   `c_void` at 33.
375/// - Composites: low byte is tag (TAG_STRUCT, TAG_ENUM, TAG_ARRAY, TAG_MODULE),
376///   high 24 bits are the ID
377///
378/// # Usage
379///
380/// Use the associated constants for primitive types:
381/// ```ignore
382/// let ty = Type::I32;
383/// ```
384///
385/// Use constructor methods for composite types:
386/// ```ignore
387/// let ty = Type::new_struct(struct_id);
388/// ```
389///
390/// Use `kind()` for pattern matching:
391/// ```ignore
392/// match ty.kind() {
393///     TypeKind::I32 => { /* ... */ }
394///     TypeKind::Struct(id) => { /* ... */ }
395///     _ => { /* ... */ }
396/// }
397/// ```
398#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
399pub struct Type(u32);
400
401impl Default for Type {
402    fn default() -> Self {
403        Type::UNIT
404    }
405}
406
407impl std::fmt::Debug for Type {
408    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409        // Provide a readable debug format
410        match self.kind() {
411            TypeKind::I8 => write!(f, "Type::I8"),
412            TypeKind::I16 => write!(f, "Type::I16"),
413            TypeKind::I32 => write!(f, "Type::I32"),
414            TypeKind::I64 => write!(f, "Type::I64"),
415            TypeKind::U8 => write!(f, "Type::U8"),
416            TypeKind::U16 => write!(f, "Type::U16"),
417            TypeKind::U32 => write!(f, "Type::U32"),
418            TypeKind::U64 => write!(f, "Type::U64"),
419            TypeKind::Isize => write!(f, "Type::ISIZE"),
420            TypeKind::Usize => write!(f, "Type::USIZE"),
421            TypeKind::F16 => write!(f, "Type::F16"),
422            TypeKind::F32 => write!(f, "Type::F32"),
423            TypeKind::F64 => write!(f, "Type::F64"),
424            TypeKind::Bool => write!(f, "Type::BOOL"),
425            TypeKind::Char => write!(f, "Type::CHAR"),
426            TypeKind::Unit => write!(f, "Type::UNIT"),
427            TypeKind::Error => write!(f, "Type::ERROR"),
428            TypeKind::Never => write!(f, "Type::NEVER"),
429            TypeKind::ComptimeType => write!(f, "Type::COMPTIME_TYPE"),
430            TypeKind::ComptimeStr => write!(f, "Type::COMPTIME_STR"),
431            TypeKind::ComptimeInt => write!(f, "Type::COMPTIME_INT"),
432            TypeKind::CSchar => write!(f, "Type::C_SCHAR"),
433            TypeKind::CShort => write!(f, "Type::C_SHORT"),
434            TypeKind::CInt => write!(f, "Type::C_INT"),
435            TypeKind::CLong => write!(f, "Type::C_LONG"),
436            TypeKind::CLonglong => write!(f, "Type::C_LONGLONG"),
437            TypeKind::CUchar => write!(f, "Type::C_UCHAR"),
438            TypeKind::CUshort => write!(f, "Type::C_USHORT"),
439            TypeKind::CUint => write!(f, "Type::C_UINT"),
440            TypeKind::CUlong => write!(f, "Type::C_ULONG"),
441            TypeKind::CUlonglong => write!(f, "Type::C_ULONGLONG"),
442            TypeKind::CFloat => write!(f, "Type::C_FLOAT"),
443            TypeKind::CDouble => write!(f, "Type::C_DOUBLE"),
444            TypeKind::CVoid => write!(f, "Type::C_VOID"),
445            TypeKind::Struct(id) => write!(f, "Type::new_struct(StructId({}))", id.0),
446            TypeKind::Enum(id) => write!(f, "Type::new_enum(EnumId({}))", id.0),
447            TypeKind::Array(id) => write!(f, "Type::new_array(ArrayTypeId({}))", id.0),
448            TypeKind::PtrConst(id) => write!(f, "Type::new_ptr_const(PtrConstTypeId({}))", id.0),
449            TypeKind::PtrMut(id) => write!(f, "Type::new_ptr_mut(PtrMutTypeId({}))", id.0),
450            TypeKind::Ref(id) => write!(f, "Type::new_ref(RefTypeId({}))", id.0),
451            TypeKind::MutRef(id) => write!(f, "Type::new_mut_ref(MutRefTypeId({}))", id.0),
452            TypeKind::Slice(id) => write!(f, "Type::new_slice(SliceTypeId({}))", id.0),
453            TypeKind::MutSlice(id) => write!(f, "Type::new_mut_slice(MutSliceTypeId({}))", id.0),
454            TypeKind::Vec(id) => write!(f, "Type::new_vec(VecTypeId({}))", id.0),
455            TypeKind::Module(id) => write!(f, "Type::new_module(ModuleId({}))", id.0),
456            TypeKind::Interface(id) => write!(f, "Type::new_interface(InterfaceId({}))", id.0),
457        }
458    }
459}
460
461// Composite type tag constants
462// These are used in the low byte of the u32 encoding to identify composite types.
463// The high 24 bits contain the ID (StructId, EnumId, ArrayTypeId, ModuleId, or pointer type IDs).
464const TAG_STRUCT: u32 = 100;
465const TAG_ENUM: u32 = 101;
466const TAG_ARRAY: u32 = 102;
467const TAG_MODULE: u32 = 103;
468const TAG_PTR_CONST: u32 = 104;
469const TAG_PTR_MUT: u32 = 105;
470const TAG_INTERFACE: u32 = 106;
471const TAG_REF: u32 = 107;
472const TAG_MUT_REF: u32 = 108;
473const TAG_SLICE: u32 = 109;
474const TAG_MUT_SLICE: u32 = 110;
475const TAG_VEC: u32 = 111;
476
477// Primitive type constants
478impl Type {
479    /// 8-bit signed integer
480    pub const I8: Type = Type(0);
481    /// 16-bit signed integer
482    pub const I16: Type = Type(1);
483    /// 32-bit signed integer
484    pub const I32: Type = Type(2);
485    /// 64-bit signed integer
486    pub const I64: Type = Type(3);
487    /// 8-bit unsigned integer
488    pub const U8: Type = Type(4);
489    /// 16-bit unsigned integer
490    pub const U16: Type = Type(5);
491    /// 32-bit unsigned integer
492    pub const U32: Type = Type(6);
493    /// 64-bit unsigned integer
494    pub const U64: Type = Type(7);
495    /// Pointer-sized signed integer
496    pub const ISIZE: Type = Type(8);
497    /// Pointer-sized unsigned integer
498    pub const USIZE: Type = Type(9);
499    /// IEEE 754 binary16 (half-precision float)
500    pub const F16: Type = Type(10);
501    /// IEEE 754 binary32 (single-precision float)
502    pub const F32: Type = Type(11);
503    /// IEEE 754 binary64 (double-precision float)
504    pub const F64: Type = Type(12);
505    /// Boolean
506    pub const BOOL: Type = Type(13);
507    /// The unit type (for functions that don't return a value)
508    pub const UNIT: Type = Type(14);
509    /// An error type (used during type checking to continue after errors)
510    pub const ERROR: Type = Type(15);
511    /// The never type - represents computations that don't return
512    pub const NEVER: Type = Type(16);
513    /// The comptime type - the type of types themselves
514    pub const COMPTIME_TYPE: Type = Type(17);
515    /// The comptime string type - compile-time only string values
516    pub const COMPTIME_STR: Type = Type(18);
517    /// The comptime integer type - compile-time only integer values
518    pub const COMPTIME_INT: Type = Type(19);
519    /// Unicode scalar value (ADR-0071)
520    pub const CHAR: Type = Type(20);
521
522    // ADR-0086 C named primitive types. Signed integers are tags 21-25,
523    // unsigned 26-30, floats 31-32, the incomplete `c_void` at 33.
524    /// C `signed char` (ADR-0086)
525    pub const C_SCHAR: Type = Type(21);
526    /// C `short` (ADR-0086)
527    pub const C_SHORT: Type = Type(22);
528    /// C `int` (ADR-0086) — canonical `@mark(c) enum` discriminant
529    pub const C_INT: Type = Type(23);
530    /// C `long` (ADR-0086)
531    pub const C_LONG: Type = Type(24);
532    /// C `long long` (ADR-0086)
533    pub const C_LONGLONG: Type = Type(25);
534    /// C `unsigned char` (ADR-0086)
535    pub const C_UCHAR: Type = Type(26);
536    /// C `unsigned short` (ADR-0086)
537    pub const C_USHORT: Type = Type(27);
538    /// C `unsigned int` (ADR-0086)
539    pub const C_UINT: Type = Type(28);
540    /// C `unsigned long` (ADR-0086)
541    pub const C_ULONG: Type = Type(29);
542    /// C `unsigned long long` (ADR-0086)
543    pub const C_ULONGLONG: Type = Type(30);
544    /// C `float` (ADR-0086)
545    pub const C_FLOAT: Type = Type(31);
546    /// C `double` (ADR-0086)
547    pub const C_DOUBLE: Type = Type(32);
548    /// C `void` (ADR-0086) — incomplete type, pointer-only use
549    pub const C_VOID: Type = Type(33);
550}
551
552// Composite type constructors
553impl Type {
554    /// Create a struct type from a StructId.
555    #[inline]
556    pub const fn new_struct(id: StructId) -> Type {
557        Type(TAG_STRUCT | (id.0 << 8))
558    }
559
560    /// Create an enum type from an EnumId.
561    #[inline]
562    pub const fn new_enum(id: EnumId) -> Type {
563        Type(TAG_ENUM | (id.0 << 8))
564    }
565
566    /// Create an array type from an ArrayTypeId.
567    #[inline]
568    pub const fn new_array(id: ArrayTypeId) -> Type {
569        Type(TAG_ARRAY | (id.0 << 8))
570    }
571
572    /// Create a raw const pointer type from a PtrConstTypeId.
573    #[inline]
574    pub const fn new_ptr_const(id: PtrConstTypeId) -> Type {
575        Type(TAG_PTR_CONST | (id.0 << 8))
576    }
577
578    /// Create a raw mut pointer type from a PtrMutTypeId.
579    #[inline]
580    pub const fn new_ptr_mut(id: PtrMutTypeId) -> Type {
581        Type(TAG_PTR_MUT | (id.0 << 8))
582    }
583
584    /// Create an immutable reference type (ADR-0062) from a RefTypeId.
585    #[inline]
586    pub const fn new_ref(id: RefTypeId) -> Type {
587        Type(TAG_REF | (id.0 << 8))
588    }
589
590    /// Create a mutable reference type (ADR-0062) from a MutRefTypeId.
591    #[inline]
592    pub const fn new_mut_ref(id: MutRefTypeId) -> Type {
593        Type(TAG_MUT_REF | (id.0 << 8))
594    }
595
596    /// Create an immutable slice type (ADR-0064) from a SliceTypeId.
597    #[inline]
598    pub const fn new_slice(id: SliceTypeId) -> Type {
599        Type(TAG_SLICE | (id.0 << 8))
600    }
601
602    /// Create a mutable slice type (ADR-0064) from a MutSliceTypeId.
603    #[inline]
604    pub const fn new_mut_slice(id: MutSliceTypeId) -> Type {
605        Type(TAG_MUT_SLICE | (id.0 << 8))
606    }
607
608    /// Create an owned vector type (ADR-0066) from a VecTypeId.
609    #[inline]
610    pub const fn new_vec(id: VecTypeId) -> Type {
611        Type(TAG_VEC | (id.0 << 8))
612    }
613
614    /// Create a module type from a ModuleId.
615    #[inline]
616    pub const fn new_module(id: ModuleId) -> Type {
617        Type(TAG_MODULE | (id.0 << 8))
618    }
619
620    /// Create an interface type from an InterfaceId.
621    #[inline]
622    pub const fn new_interface(id: InterfaceId) -> Type {
623        Type(TAG_INTERFACE | (id.0 << 8))
624    }
625}
626
627impl StructDef {
628    /// Returns true if this struct was synthesised to represent a tuple
629    /// (ADR-0048): fields named "0", "1", ..., "N-1", in order, no methods,
630    /// and the anon-struct name prefix.
631    pub fn is_tuple_shaped(&self) -> bool {
632        if !self.name.starts_with("__anon_struct_") {
633            return false;
634        }
635        if self.fields.is_empty() {
636            return false;
637        }
638        self.fields
639            .iter()
640            .enumerate()
641            .all(|(i, f)| f.name == i.to_string())
642    }
643
644    /// If this struct is tuple-shaped, render its tuple-syntax name:
645    /// `(T0, T1, ...)` for arity ≥ 2, `(T0,)` for arity 1.
646    /// The formatter is passed a callback to render each field type name.
647    pub fn tuple_display_name<F>(&self, mut fmt_ty: F) -> Option<String>
648    where
649        F: FnMut(Type) -> String,
650    {
651        if !self.is_tuple_shaped() {
652            return None;
653        }
654        let mut s = String::from("(");
655        for (i, f) in self.fields.iter().enumerate() {
656            if i > 0 {
657                s.push_str(", ");
658            }
659            s.push_str(&fmt_ty(f.ty));
660        }
661        if self.fields.len() == 1 {
662            s.push(',');
663        }
664        s.push(')');
665        Some(s)
666    }
667}
668
669/// Definition of an interface (ADR-0056).
670///
671/// Stores the resolved method-signature requirements. The order of `methods`
672/// is significant: it is the vtable slot order used by the runtime-dispatch
673/// path (Phase 4). It also controls error reporting, where missing methods
674/// are listed in declaration order.
675///
676/// `is_pub` and `file_id` are populated now and consumed in later phases
677/// (visibility checks during cross-module conformance).
678#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
679pub struct InterfaceDef {
680    /// Interface name (as written in source).
681    pub name: String,
682    /// Required method signatures, in declaration order.
683    pub methods: Vec<InterfaceMethodReq>,
684    /// Whether this interface is public (module-system future work).
685    pub is_pub: bool,
686    /// File ID this interface was declared in.
687    pub file_id: gruel_util::FileId,
688}
689
690/// A type slot inside an interface method signature (ADR-0060).
691///
692/// Interface signatures may mention `Self` — the type that conforms to the
693/// interface — in parameter or return position. `IfaceTy` carries that
694/// distinction so `check_conforms` can substitute the candidate's concrete
695/// type at compare time. Concrete (non-`Self`) slots are resolved during
696/// `validate_interface_decls` via the regular `resolve_type` path.
697#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
698pub enum IfaceTy {
699    /// `Self` — substituted with the candidate type at conformance time.
700    SelfType,
701    /// A concrete type resolved against the surrounding scope.
702    Concrete(Type),
703}
704
705impl IfaceTy {
706    /// Substitute `Self` with the supplied candidate type, leaving concrete
707    /// slots unchanged.
708    pub fn substitute_self(&self, candidate: Type) -> Type {
709        match self {
710            IfaceTy::SelfType => candidate,
711            IfaceTy::Concrete(t) => *t,
712        }
713    }
714
715    /// Returns `true` if this slot is `Self`.
716    pub fn is_self(&self) -> bool {
717        matches!(self, IfaceTy::SelfType)
718    }
719}
720
721/// Receiver mode for an interface method (ADR-0060).
722///
723/// Mirrors the parameter modes available on regular methods. `check_conforms`
724/// requires the candidate method's receiver mode to match exactly. Per
725/// ADR-0076, the surface forms are `self : Self`, `self : Ref(Self)`, and
726/// `self : MutRef(Self)`.
727#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
728pub enum ReceiverMode {
729    /// `self` / `self : Self` — by-value receiver.
730    ByValue,
731    /// `self : MutRef(Self)` — exclusive mutable borrow receiver.
732    MutRef,
733    /// `self : Ref(Self)` — shared immutable borrow receiver.
734    Ref,
735}
736
737impl ReceiverMode {
738    /// Render the receiver as it would appear in source.
739    pub fn render(&self) -> &'static str {
740        match self {
741            ReceiverMode::ByValue => "self",
742            ReceiverMode::MutRef => "self: MutRef(Self)",
743            ReceiverMode::Ref => "self: Ref(Self)",
744        }
745    }
746}
747
748/// A single required method signature inside an `InterfaceDef`.
749///
750/// Per ADR-0060, parameter and return slots are `IfaceTy` so that `Self` can
751/// be substituted with the candidate at conformance check time.
752#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
753pub struct InterfaceMethodReq {
754    /// Method name.
755    pub name: String,
756    /// Receiver mode (`self`, `inout self`, or `borrow self`).
757    pub receiver: ReceiverMode,
758    /// Resolved parameter slots in declaration order (excluding the receiver).
759    pub param_types: Vec<IfaceTy>,
760    /// Resolved return slot.
761    pub return_type: IfaceTy,
762    /// ADR-0088: whether this signature is `@mark(unchecked)`. Conformance
763    /// requires the implementor's method `is_unchecked` to match this
764    /// field exactly. Call sites that resolve through the interface
765    /// signature (comptime-T monomorphisation via `comptime T: I` after
766    /// specialization, or fat-pointer dispatch via `Ref(I)` / `MutRef(I)`)
767    /// read this flag to decide whether a `checked { }` block is required.
768    #[serde(default)]
769    pub is_unchecked: bool,
770}
771
772impl InterfaceDef {
773    /// Find a required method by name. Returns its slot index plus the
774    /// requirement. Used by later phases for vtable lookup.
775    pub fn find_method(&self, name: &str) -> Option<(usize, &InterfaceMethodReq)> {
776        self.methods
777            .iter()
778            .enumerate()
779            .find(|(_, m)| m.name == name)
780    }
781}
782
783/// Definition of a struct type.
784#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
785pub struct StructDef {
786    /// Struct name
787    pub name: String,
788    /// Fields in declaration order
789    pub fields: Vec<StructField>,
790    /// Final ownership posture (ADR-0080). Decides whether values of this
791    /// struct are Copy, Affine (default — move on use, implicit drop), or
792    /// Linear (must be explicitly consumed). Set by `register_type_names`
793    /// from the declared keyword/`@mark(...)`, then filled in from
794    /// structural inference for unmarked types.
795    pub posture: Posture,
796    /// Whether this struct is marked with @derive(Clone) (compiler-synthesized
797    /// recursive clone). ADR-0065. Independent of `posture`: every Copy type
798    /// is automatically Clone, but `@derive(Clone)` can also be applied to
799    /// affine types whose fields are all `Clone`.
800    pub is_clone: bool,
801    /// ADR-0084: final thread-safety classification (`Unsend < Send < Sync`).
802    /// Computed during `validate_consistency` as the structural minimum
803    /// over fields, then overridden by any `@mark(unsend)` /
804    /// `@mark(checked_send)` / `@mark(checked_sync)` directive on the
805    /// declaration. Until that pass runs, the value reflects only the
806    /// declared override (or `Sync` if none); call
807    /// `TypeInternPool::is_thread_safety_type` for the load-bearing
808    /// query rather than reading this field directly.
809    pub thread_safety: ThreadSafety,
810    /// User-defined destructor function name, if any (e.g., "Data.__drop")
811    pub destructor: Option<String>,
812    /// Whether this is a built-in type (e.g., String) injected by the compiler.
813    ///
814    /// Built-in types behave like regular structs but have runtime implementations
815    /// for their methods rather than generated code.
816    pub is_builtin: bool,
817    /// Whether this struct is public (visible outside its directory)
818    pub is_pub: bool,
819    /// File ID this struct was declared in (for visibility checking)
820    pub file_id: gruel_util::FileId,
821    /// ADR-0085: whether `@mark(c)` was applied — selects C layout
822    /// (declaration-order fields, natural alignment, niche optimisation
823    /// disabled) and makes the type eligible to cross the FFI boundary
824    /// by value.
825    #[serde(default)]
826    pub is_c_layout: bool,
827}
828
829/// A field in a struct definition.
830#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
831pub struct StructField {
832    /// Field name
833    pub name: String,
834    /// Field type
835    pub ty: Type,
836    /// ADR-0073: whether this field is `pub`. For user-defined structs,
837    /// `false` means module-private (same-directory accessible). For
838    /// built-ins, the comparison runs against the synthetic `<builtin>`
839    /// FileId.
840    pub is_pub: bool,
841}
842
843impl StructDef {
844    /// Find a field by name and return its index and definition.
845    pub fn find_field(&self, name: &str) -> Option<(usize, &StructField)> {
846        self.fields.iter().enumerate().find(|(_, f)| f.name == name)
847    }
848
849    /// Get the number of fields in this struct.
850    pub fn field_count(&self) -> usize {
851        self.fields.len()
852    }
853}
854
855/// A single variant in an enum definition.
856#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
857pub struct EnumVariantDef {
858    /// Variant name
859    pub name: String,
860    /// Field types for data variants. Empty for unit variants.
861    /// E.g., `Some(i32)` has `fields = [Type::I32]`.
862    pub fields: Vec<Type>,
863    /// Field names for struct-style variants. Empty for unit and tuple variants.
864    /// When non-empty, `field_names.len() == fields.len()`.
865    pub field_names: Vec<String>,
866}
867
868impl EnumVariantDef {
869    /// Create a unit variant (no associated data).
870    pub fn unit(name: impl Into<String>) -> Self {
871        Self {
872            name: name.into(),
873            fields: Vec::new(),
874            field_names: Vec::new(),
875        }
876    }
877
878    /// Whether this is a data variant (has associated fields).
879    pub fn has_data(&self) -> bool {
880        !self.fields.is_empty()
881    }
882
883    /// Whether this is a struct-style variant (has named fields).
884    pub fn is_struct_variant(&self) -> bool {
885        !self.field_names.is_empty()
886    }
887
888    /// Find a field by name (for struct variants). Returns the field index.
889    pub fn find_field(&self, name: &str) -> Option<usize> {
890        self.field_names.iter().position(|n| n == name)
891    }
892}
893
894/// Definition of an enum type.
895#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
896pub struct EnumDef {
897    /// Enum name
898    pub name: String,
899    /// Variants in declaration order
900    pub variants: Vec<EnumVariantDef>,
901    /// Final ownership posture (ADR-0080). `Copy` makes `is_type_copy`
902    /// return `true` for this enum directly; `Linear` makes `is_type_linear`
903    /// return `true` directly. `Affine` (the default) lets variant-payload
904    /// inference decide the practical posture.
905    pub posture: Posture,
906    /// ADR-0084: final thread-safety classification (mirrors
907    /// `StructDef.thread_safety`).
908    pub thread_safety: ThreadSafety,
909    /// Whether this enum is public (visible outside its directory)
910    pub is_pub: bool,
911    /// File ID this enum was declared in (for visibility checking)
912    pub file_id: gruel_util::FileId,
913    /// User-defined destructor function name, if any (e.g., "Resource.__drop").
914    /// ADR-0053 phase 3b. Mirrors `StructDef.destructor`.
915    pub destructor: Option<String>,
916    /// ADR-0086: whether `@mark(c)` was applied. C-layout enums use
917    /// `c_int` as their discriminant type (regardless of variant count),
918    /// disable niche optimisation, and are eligible to cross the FFI
919    /// boundary by value.
920    #[serde(default)]
921    pub is_c_layout: bool,
922}
923
924impl EnumDef {
925    /// Get the number of variants in this enum.
926    pub fn variant_count(&self) -> usize {
927        self.variants.len()
928    }
929
930    /// Find a variant by name and return its index.
931    pub fn find_variant(&self, name: &str) -> Option<usize> {
932        self.variants.iter().position(|v| v.name == name)
933    }
934
935    /// Whether any variant carries associated data.
936    pub fn has_data_variants(&self) -> bool {
937        self.variants.iter().any(|v| v.has_data())
938    }
939
940    /// Whether all variants are unit variants (no data).
941    pub fn is_unit_only(&self) -> bool {
942        !self.has_data_variants()
943    }
944
945    /// Get the discriminant type for this enum.
946    ///
947    /// ADR-0086: `@mark(c) enum` types use `c_int` as their discriminant
948    /// (matching C's default `enum` representation), regardless of variant
949    /// count. Other enums use the smallest unsigned integer that fits the
950    /// variant index.
951    pub fn discriminant_type(&self) -> Type {
952        if self.is_c_layout {
953            return Type::C_INT;
954        }
955        let count = self.variants.len();
956        if count == 0 {
957            Type::NEVER // Zero-variant enum is uninhabited
958        } else if count <= 256 {
959            Type::U8
960        } else if count <= 65536 {
961            Type::U16
962        } else if count <= 4_294_967_296 {
963            Type::U32
964        } else {
965            Type::U64
966        }
967    }
968}
969
970/// Definition of a module (imported file).
971///
972/// A module contains the public declarations from an imported file.
973/// When code accesses `math.add()`, the module definition is consulted
974/// to find the corresponding function.
975#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
976pub struct ModuleDef {
977    /// The path used in @import (e.g., "math.gruel")
978    pub import_path: String,
979    /// The resolved absolute file path
980    pub file_path: String,
981    /// Public functions in this module: name -> mangled name
982    /// The mangled name includes the module path (e.g., "math::add")
983    pub functions: rustc_hash::FxHashMap<String, String>,
984    /// Public structs in this module
985    pub structs: Vec<String>,
986    /// Public enums in this module
987    pub enums: Vec<String>,
988}
989
990impl ModuleDef {
991    /// Create a new empty module definition.
992    pub fn new(import_path: String, file_path: String) -> Self {
993        Self {
994            import_path,
995            file_path,
996            functions: rustc_hash::FxHashMap::default(),
997            structs: Vec::new(),
998            enums: Vec::new(),
999        }
1000    }
1001
1002    /// Find a function by name in this module.
1003    /// Returns the mangled function name if found.
1004    pub fn find_function(&self, name: &str) -> Option<&str> {
1005        self.functions.get(name).map(|s| s.as_str())
1006    }
1007}
1008
1009impl Type {
1010    /// Get the kind of this type for pattern matching.
1011    ///
1012    /// This method decodes the u32 representation back to a `TypeKind` for pattern matching.
1013    /// Primitive types (0-12) decode directly; composite types decode the tag and ID.
1014    ///
1015    /// # Panics
1016    ///
1017    /// Panics if the Type has an invalid encoding. This should never happen with Types
1018    /// created through the normal API. If you're working with potentially corrupt data,
1019    /// use [`try_kind`](Self::try_kind) instead.
1020    ///
1021    /// # Example
1022    ///
1023    /// ```ignore
1024    /// match ty.kind() {
1025    ///     TypeKind::I32 | TypeKind::I64 => { /* handle integers */ }
1026    ///     TypeKind::Struct(id) => { /* handle struct */ }
1027    ///     _ => { /* other types */ }
1028    /// }
1029    /// ```
1030    #[inline]
1031    pub fn kind(&self) -> TypeKind {
1032        self.try_kind().unwrap_or_else(|| {
1033            panic!(
1034                "invalid Type encoding: raw value {:#010x} (tag={}, id={}). \
1035                 This indicates data corruption or a bug in Type construction. \
1036                 Valid tags are 0-33 (primitives, including ADR-0086 C named types) \
1037                 or 100-111 (composites).",
1038                self.0,
1039                self.0 & 0xFF,
1040                self.0 >> 8
1041            )
1042        })
1043    }
1044
1045    /// Try to get the kind of this type, returning `None` if the encoding is invalid.
1046    ///
1047    /// This is the non-panicking version of [`kind`](Self::kind). Use this when working
1048    /// with potentially corrupt data or for defensive programming.
1049    ///
1050    /// # Example
1051    ///
1052    /// ```ignore
1053    /// if let Some(kind) = ty.try_kind() {
1054    ///     match kind {
1055    ///         TypeKind::I32 => { /* ... */ }
1056    ///         _ => { /* ... */ }
1057    ///     }
1058    /// } else {
1059    ///     eprintln!("corrupt type data");
1060    /// }
1061    /// ```
1062    #[inline]
1063    pub fn try_kind(&self) -> Option<TypeKind> {
1064        let tag = self.0 & 0xFF;
1065        match tag {
1066            0 => Some(TypeKind::I8),
1067            1 => Some(TypeKind::I16),
1068            2 => Some(TypeKind::I32),
1069            3 => Some(TypeKind::I64),
1070            4 => Some(TypeKind::U8),
1071            5 => Some(TypeKind::U16),
1072            6 => Some(TypeKind::U32),
1073            7 => Some(TypeKind::U64),
1074            8 => Some(TypeKind::Isize),
1075            9 => Some(TypeKind::Usize),
1076            10 => Some(TypeKind::F16),
1077            11 => Some(TypeKind::F32),
1078            12 => Some(TypeKind::F64),
1079            13 => Some(TypeKind::Bool),
1080            14 => Some(TypeKind::Unit),
1081            15 => Some(TypeKind::Error),
1082            16 => Some(TypeKind::Never),
1083            17 => Some(TypeKind::ComptimeType),
1084            18 => Some(TypeKind::ComptimeStr),
1085            19 => Some(TypeKind::ComptimeInt),
1086            20 => Some(TypeKind::Char),
1087            21 => Some(TypeKind::CSchar),
1088            22 => Some(TypeKind::CShort),
1089            23 => Some(TypeKind::CInt),
1090            24 => Some(TypeKind::CLong),
1091            25 => Some(TypeKind::CLonglong),
1092            26 => Some(TypeKind::CUchar),
1093            27 => Some(TypeKind::CUshort),
1094            28 => Some(TypeKind::CUint),
1095            29 => Some(TypeKind::CUlong),
1096            30 => Some(TypeKind::CUlonglong),
1097            31 => Some(TypeKind::CFloat),
1098            32 => Some(TypeKind::CDouble),
1099            33 => Some(TypeKind::CVoid),
1100            TAG_STRUCT => Some(TypeKind::Struct(StructId(self.0 >> 8))),
1101            TAG_ENUM => Some(TypeKind::Enum(EnumId(self.0 >> 8))),
1102            TAG_ARRAY => Some(TypeKind::Array(ArrayTypeId(self.0 >> 8))),
1103            TAG_PTR_CONST => Some(TypeKind::PtrConst(PtrConstTypeId(self.0 >> 8))),
1104            TAG_PTR_MUT => Some(TypeKind::PtrMut(PtrMutTypeId(self.0 >> 8))),
1105            TAG_REF => Some(TypeKind::Ref(RefTypeId(self.0 >> 8))),
1106            TAG_MUT_REF => Some(TypeKind::MutRef(MutRefTypeId(self.0 >> 8))),
1107            TAG_SLICE => Some(TypeKind::Slice(SliceTypeId(self.0 >> 8))),
1108            TAG_MUT_SLICE => Some(TypeKind::MutSlice(MutSliceTypeId(self.0 >> 8))),
1109            TAG_VEC => Some(TypeKind::Vec(VecTypeId(self.0 >> 8))),
1110            TAG_MODULE => Some(TypeKind::Module(ModuleId(self.0 >> 8))),
1111            TAG_INTERFACE => Some(TypeKind::Interface(InterfaceId(self.0 >> 8))),
1112            _ => None,
1113        }
1114    }
1115
1116    /// Get a human-readable name for this type.
1117    /// Note: For struct and array types, this returns a placeholder.
1118    /// Use `type_name_with_structs` for proper struct/array names.
1119    pub fn name(&self) -> &'static str {
1120        match self.kind() {
1121            TypeKind::I8 => "i8",
1122            TypeKind::I16 => "i16",
1123            TypeKind::I32 => "i32",
1124            TypeKind::I64 => "i64",
1125            TypeKind::U8 => "u8",
1126            TypeKind::U16 => "u16",
1127            TypeKind::U32 => "u32",
1128            TypeKind::U64 => "u64",
1129            TypeKind::Isize => "isize",
1130            TypeKind::Usize => "usize",
1131            TypeKind::F16 => "f16",
1132            TypeKind::F32 => "f32",
1133            TypeKind::F64 => "f64",
1134            TypeKind::Bool => "bool",
1135            TypeKind::Char => "char",
1136            TypeKind::Unit => "()",
1137            TypeKind::Struct(_) => "<struct>",
1138            TypeKind::Enum(_) => "<enum>",
1139            TypeKind::Array(_) => "<array>",
1140            TypeKind::PtrConst(_) => "<ptr const>",
1141            TypeKind::PtrMut(_) => "<ptr mut>",
1142            TypeKind::Ref(_) => "<ref>",
1143            TypeKind::MutRef(_) => "<mut ref>",
1144            TypeKind::Slice(_) => "<slice>",
1145            TypeKind::MutSlice(_) => "<mut slice>",
1146            TypeKind::Vec(_) => "<vec>",
1147            TypeKind::Module(_) => "<module>",
1148            TypeKind::Interface(_) => "<interface>",
1149            TypeKind::Error => "<error>",
1150            TypeKind::Never => "!",
1151            TypeKind::ComptimeType => "type",
1152            TypeKind::ComptimeStr => "comptime_str",
1153            TypeKind::ComptimeInt => "comptime_int",
1154            TypeKind::CSchar => "c_schar",
1155            TypeKind::CShort => "c_short",
1156            TypeKind::CInt => "c_int",
1157            TypeKind::CLong => "c_long",
1158            TypeKind::CLonglong => "c_longlong",
1159            TypeKind::CUchar => "c_uchar",
1160            TypeKind::CUshort => "c_ushort",
1161            TypeKind::CUint => "c_uint",
1162            TypeKind::CUlong => "c_ulong",
1163            TypeKind::CUlonglong => "c_ulonglong",
1164            TypeKind::CFloat => "c_float",
1165            TypeKind::CDouble => "c_double",
1166            TypeKind::CVoid => "c_void",
1167        }
1168    }
1169
1170    /// Get a human-readable type name, safely handling anonymous structs and missing definitions.
1171    ///
1172    /// Unlike `name()`, this method can access the type pool to get actual struct/enum names
1173    /// instead of returning generic placeholders like `"<struct>"`.
1174    ///
1175    /// This is primarily used for error messages where we want to show meaningful type names
1176    /// even if the type pool lookup fails (returns safe fallback in that case).
1177    ///
1178    /// # Safety
1179    ///
1180    /// This method is safe even if the struct/enum ID is invalid or the pool is None.
1181    /// It will return a fallback string like `"<struct#123>"` in those cases.
1182    pub fn safe_name_with_pool(&self, pool: Option<&crate::intern_pool::TypeInternPool>) -> String {
1183        match self.try_kind() {
1184            Some(TypeKind::Struct(struct_id)) => {
1185                if let Some(pool) = pool {
1186                    let def = pool.struct_def(struct_id);
1187                    // ADR-0048: render tuple-shaped anon structs as tuples.
1188                    if let Some(tuple_name) =
1189                        def.tuple_display_name(|ty| ty.safe_name_with_pool(Some(pool)))
1190                    {
1191                        return tuple_name;
1192                    }
1193                    return def.name.clone();
1194                }
1195                format!("<struct#{}>", struct_id.0)
1196            }
1197            Some(TypeKind::Enum(enum_id)) => {
1198                if let Some(pool) = pool {
1199                    let def = pool.enum_def(enum_id);
1200                    return def.name.clone();
1201                }
1202                format!("<enum#{}>", enum_id.0)
1203            }
1204            Some(_kind) => self.name().to_string(),
1205            None => format!("<invalid type encoding: {:#x}>", self.0),
1206        }
1207    }
1208
1209    /// Check if this type is an integer type.
1210    /// Optimized: checks tag range directly. Native integers occupy tags 0-9
1211    /// (i8..u64, isize/usize); ADR-0086 C named integers occupy tags 21-30
1212    /// (CSchar..CUlonglong).
1213    #[inline]
1214    pub fn is_integer(&self) -> bool {
1215        self.0 <= 9 || (self.0 >= 21 && self.0 <= 30)
1216    }
1217
1218    /// Check if this is an error type.
1219    #[inline]
1220    pub fn is_error(&self) -> bool {
1221        *self == Type::ERROR
1222    }
1223
1224    /// Check if this is the never type.
1225    #[inline]
1226    pub fn is_never(&self) -> bool {
1227        *self == Type::NEVER
1228    }
1229
1230    /// Check if this is the comptime type (the type of types).
1231    #[inline]
1232    pub fn is_comptime_type(&self) -> bool {
1233        *self == Type::COMPTIME_TYPE
1234    }
1235
1236    /// Check if this is the comptime string type.
1237    #[inline]
1238    pub fn is_comptime_str(&self) -> bool {
1239        *self == Type::COMPTIME_STR
1240    }
1241
1242    /// Check if this is the comptime integer type.
1243    #[inline]
1244    pub fn is_comptime_int(&self) -> bool {
1245        *self == Type::COMPTIME_INT
1246    }
1247
1248    /// Check if this is a struct type.
1249    #[inline]
1250    pub fn is_struct(&self) -> bool {
1251        (self.0 & 0xFF) == TAG_STRUCT
1252    }
1253
1254    /// Get the struct ID if this is a struct type.
1255    #[inline]
1256    pub fn as_struct(&self) -> Option<StructId> {
1257        if self.is_struct() {
1258            Some(StructId(self.0 >> 8))
1259        } else {
1260            None
1261        }
1262    }
1263
1264    /// Check if this is an array type.
1265    #[inline]
1266    pub fn is_array(&self) -> bool {
1267        (self.0 & 0xFF) == TAG_ARRAY
1268    }
1269
1270    /// Get the array type ID if this is an array type.
1271    #[inline]
1272    pub fn as_array(&self) -> Option<ArrayTypeId> {
1273        if self.is_array() {
1274            Some(ArrayTypeId(self.0 >> 8))
1275        } else {
1276            None
1277        }
1278    }
1279
1280    /// Check if this is an enum type.
1281    #[inline]
1282    pub fn is_enum(&self) -> bool {
1283        (self.0 & 0xFF) == TAG_ENUM
1284    }
1285
1286    /// Get the enum ID if this is an enum type.
1287    #[inline]
1288    pub fn as_enum(&self) -> Option<EnumId> {
1289        if self.is_enum() {
1290            Some(EnumId(self.0 >> 8))
1291        } else {
1292            None
1293        }
1294    }
1295
1296    /// Check if this is a module type.
1297    #[inline]
1298    pub fn is_module(&self) -> bool {
1299        (self.0 & 0xFF) == TAG_MODULE
1300    }
1301
1302    /// Get the module ID if this is a module type.
1303    #[inline]
1304    pub fn as_module(&self) -> Option<ModuleId> {
1305        if self.is_module() {
1306            Some(ModuleId(self.0 >> 8))
1307        } else {
1308            None
1309        }
1310    }
1311
1312    /// Check if this is a raw const pointer type.
1313    #[inline]
1314    pub fn is_ptr_const(&self) -> bool {
1315        (self.0 & 0xFF) == TAG_PTR_CONST
1316    }
1317
1318    /// Get the pointer type ID if this is a ptr const type.
1319    #[inline]
1320    pub fn as_ptr_const(&self) -> Option<PtrConstTypeId> {
1321        if self.is_ptr_const() {
1322            Some(PtrConstTypeId(self.0 >> 8))
1323        } else {
1324            None
1325        }
1326    }
1327
1328    /// Check if this is a raw mut pointer type.
1329    #[inline]
1330    pub fn is_ptr_mut(&self) -> bool {
1331        (self.0 & 0xFF) == TAG_PTR_MUT
1332    }
1333
1334    /// Get the pointer type ID if this is a ptr mut type.
1335    #[inline]
1336    pub fn as_ptr_mut(&self) -> Option<PtrMutTypeId> {
1337        if self.is_ptr_mut() {
1338            Some(PtrMutTypeId(self.0 >> 8))
1339        } else {
1340            None
1341        }
1342    }
1343
1344    /// Check if this is any raw pointer type (ptr const or ptr mut).
1345    #[inline]
1346    pub fn is_ptr(&self) -> bool {
1347        let tag = self.0 & 0xFF;
1348        tag == TAG_PTR_CONST || tag == TAG_PTR_MUT
1349    }
1350
1351    /// Check if this is an immutable reference type (`Ref(T)`, ADR-0062).
1352    #[inline]
1353    pub fn is_ref(&self) -> bool {
1354        (self.0 & 0xFF) == TAG_REF
1355    }
1356
1357    /// Get the reference type ID if this is a `Ref(T)`.
1358    #[inline]
1359    pub fn as_ref_type(&self) -> Option<RefTypeId> {
1360        if self.is_ref() {
1361            Some(RefTypeId(self.0 >> 8))
1362        } else {
1363            None
1364        }
1365    }
1366
1367    /// Check if this is a mutable reference type (`MutRef(T)`, ADR-0062).
1368    #[inline]
1369    pub fn is_mut_ref(&self) -> bool {
1370        (self.0 & 0xFF) == TAG_MUT_REF
1371    }
1372
1373    /// Get the reference type ID if this is a `MutRef(T)`.
1374    #[inline]
1375    pub fn as_mut_ref_type(&self) -> Option<MutRefTypeId> {
1376        if self.is_mut_ref() {
1377            Some(MutRefTypeId(self.0 >> 8))
1378        } else {
1379            None
1380        }
1381    }
1382
1383    /// Check if this is any reference type (`Ref(T)` or `MutRef(T)`).
1384    #[inline]
1385    pub fn is_any_ref(&self) -> bool {
1386        let tag = self.0 & 0xFF;
1387        tag == TAG_REF || tag == TAG_MUT_REF
1388    }
1389
1390    /// Check if this is an immutable slice type (`Slice(T)`, ADR-0064).
1391    #[inline]
1392    pub fn is_slice(&self) -> bool {
1393        (self.0 & 0xFF) == TAG_SLICE
1394    }
1395
1396    /// Get the slice type ID if this is a `Slice(T)`.
1397    #[inline]
1398    pub fn as_slice_type(&self) -> Option<SliceTypeId> {
1399        if self.is_slice() {
1400            Some(SliceTypeId(self.0 >> 8))
1401        } else {
1402            None
1403        }
1404    }
1405
1406    /// Check if this is a mutable slice type (`MutSlice(T)`, ADR-0064).
1407    #[inline]
1408    pub fn is_mut_slice(&self) -> bool {
1409        (self.0 & 0xFF) == TAG_MUT_SLICE
1410    }
1411
1412    /// Get the slice type ID if this is a `MutSlice(T)`.
1413    #[inline]
1414    pub fn as_mut_slice_type(&self) -> Option<MutSliceTypeId> {
1415        if self.is_mut_slice() {
1416            Some(MutSliceTypeId(self.0 >> 8))
1417        } else {
1418            None
1419        }
1420    }
1421
1422    /// Check if this is any slice type (`Slice(T)` or `MutSlice(T)`).
1423    #[inline]
1424    pub fn is_any_slice(&self) -> bool {
1425        let tag = self.0 & 0xFF;
1426        tag == TAG_SLICE || tag == TAG_MUT_SLICE
1427    }
1428
1429    /// Check if this is a `Vec(T)` type (ADR-0066).
1430    #[inline]
1431    pub fn is_vec(&self) -> bool {
1432        (self.0 & 0xFF) == TAG_VEC
1433    }
1434
1435    /// Get the vec type ID if this is a `Vec(T)`.
1436    #[inline]
1437    pub fn as_vec_type(&self) -> Option<VecTypeId> {
1438        if self.is_vec() {
1439            Some(VecTypeId(self.0 >> 8))
1440        } else {
1441            None
1442        }
1443    }
1444
1445    /// Check if this is a signed integer type.
1446    /// Native signed integers: I8=0, I16=1, I32=2, I64=3, Isize=8.
1447    /// ADR-0086 C named signed integers: CSchar=21..=CLonglong=25.
1448    #[inline]
1449    pub fn is_signed(&self) -> bool {
1450        self.0 <= 3 || self.0 == 8 || (self.0 >= 21 && self.0 <= 25)
1451    }
1452
1453    /// Check if this is a floating-point type.
1454    /// Native float types: F16=10, F32=11, F64=12.
1455    /// ADR-0086 C named float types: CFloat=31, CDouble=32.
1456    #[inline]
1457    pub fn is_float(&self) -> bool {
1458        (self.0 >= 10 && self.0 <= 12) || self.0 == 31 || self.0 == 32
1459    }
1460
1461    /// Check if this is a numeric type (integer or float).
1462    #[inline]
1463    pub fn is_numeric(&self) -> bool {
1464        self.0 <= 12 || (self.0 >= 21 && self.0 <= 32)
1465    }
1466
1467    /// Check if this is a Copy type (can be implicitly duplicated).
1468    ///
1469    /// Copy types are:
1470    /// - All integer types (i8-i64, u8-u64)
1471    /// - Boolean
1472    /// - Unit
1473    /// - Enum types
1474    /// - Never type and Error type (for convenience in error recovery)
1475    ///
1476    /// Non-Copy types (move types) are:
1477    /// - Struct types (unless declared Copy, checked via StructDef.posture)
1478    /// - Array types (unless element type is Copy, checked via Sema.is_type_copy)
1479    ///
1480    /// Note: This method can't check struct's posture or array element
1481    /// types since it doesn't have access to StructDefs or array type information.
1482    /// Use Sema.is_type_copy() for full checking.
1483    pub fn is_copy(&self) -> bool {
1484        let tag = self.0 & 0xFF;
1485        match tag {
1486            // Primitive Copy types (I8..Unit = 0..14, includes integers/floats/bool/unit)
1487            0..=14 => true,
1488            // Error, Never, ComptimeType, ComptimeStr, ComptimeInt are Copy for convenience
1489            15..=19 => true,
1490            // Char is Copy (it's a 32-bit Unicode scalar value).
1491            20 => true,
1492            // ADR-0086 C named arithmetic types (CSchar..CDouble = 21..=32) are all Copy.
1493            21..=32 => true,
1494            // ADR-0086 c_void (tag 33) is an incomplete type — no values exist, so Copy
1495            // is vacuously inapplicable. Returning false matches the spirit of
1496            // "can this type be implicitly duplicated"; pointer-only use is enforced in sema.
1497            33 => false,
1498            // Enum types are Copy (they're small discriminant values)
1499            TAG_ENUM => true,
1500            // Module types are Copy (they're just compile-time namespace references)
1501            TAG_MODULE => true,
1502            // Struct types are move types by default
1503            TAG_STRUCT => false,
1504            // Arrays may be Copy if element type is Copy (need TypeInternPool to check)
1505            TAG_ARRAY => false,
1506            _ => false,
1507        }
1508    }
1509
1510    /// Check if this type is Copy, with access to TypeInternPool for struct checking.
1511    ///
1512    /// This is used during anonymous struct creation to determine if the new struct
1513    /// should be Copy based on its field types.
1514    pub fn is_copy_in_pool(&self, type_pool: &crate::intern_pool::TypeInternPool) -> bool {
1515        if let Some(struct_id) = self.as_struct() {
1516            type_pool.struct_def(struct_id).posture == Posture::Copy
1517        } else {
1518            self.is_copy()
1519        }
1520    }
1521
1522    /// Check if this is a 64-bit type (uses 64-bit operations).
1523    /// Optimized: checks for I64 (3) or U64 (7).
1524    #[inline]
1525    pub fn is_64_bit(&self) -> bool {
1526        self.0 == 3 || self.0 == 7
1527    }
1528
1529    /// Check if this is a pointer-sized type (isize or usize).
1530    /// Checks for Isize (8) or Usize (9).
1531    #[inline]
1532    pub fn is_pointer_sized(&self) -> bool {
1533        self.0 == 8 || self.0 == 9
1534    }
1535
1536    /// Check if this type can coerce to the target type.
1537    ///
1538    /// Coercion rules:
1539    /// - Never can coerce to any type (it represents divergent control flow)
1540    /// - Error can coerce to any type (for error recovery during type checking)
1541    /// - Otherwise, types must be equal
1542    pub fn can_coerce_to(&self, target: &Type) -> bool {
1543        self.is_never() || self.is_error() || self == target
1544    }
1545
1546    /// Check if this is an unsigned integer type.
1547    /// Native unsigned integers: U8=4, U16=5, U32=6, U64=7, Usize=9.
1548    /// ADR-0086 C named unsigned integers: CUchar=26..=CUlonglong=30.
1549    #[inline]
1550    #[must_use]
1551    pub fn is_unsigned(&self) -> bool {
1552        (self.0 >= 4 && self.0 <= 7) || self.0 == 9 || (self.0 >= 26 && self.0 <= 30)
1553    }
1554
1555    /// Check if a u64 value fits within the range of this integer type.
1556    ///
1557    /// For signed types, only the positive range is checked (0 to max positive).
1558    /// Negation is handled separately to allow values like `-128` for i8.
1559    ///
1560    /// Returns `true` if the value fits, `false` otherwise.
1561    /// For non-integer types, returns `false`.
1562    #[must_use]
1563    pub fn literal_fits(&self, value: u64) -> bool {
1564        match self.0 {
1565            0 => value <= i8::MAX as u64,  // I8
1566            1 => value <= i16::MAX as u64, // I16
1567            2 => value <= i32::MAX as u64, // I32
1568            3 => value <= i64::MAX as u64, // I64
1569            4 => value <= u8::MAX as u64,  // U8
1570            5 => value <= u16::MAX as u64, // U16
1571            6 => value <= u32::MAX as u64, // U32
1572            7 => true,                     // U64 - Any u64 value fits
1573            8 => value <= i64::MAX as u64, // Isize - 64-bit signed on current targets
1574            9 => true,                     // Usize - 64-bit unsigned on current targets
1575            // ADR-0086 C named integers — widths match their underlying Gruel type on
1576            // every blessed (LP64) target.
1577            21 => value <= i8::MAX as u64,  // CSchar
1578            22 => value <= i16::MAX as u64, // CShort
1579            23 => value <= i32::MAX as u64, // CInt
1580            24 => value <= i64::MAX as u64, // CLong (LP64)
1581            25 => value <= i64::MAX as u64, // CLonglong
1582            26 => value <= u8::MAX as u64,  // CUchar
1583            27 => value <= u16::MAX as u64, // CUshort
1584            28 => value <= u32::MAX as u64, // CUint
1585            29 => true,                     // CUlong (LP64) - any u64 fits
1586            30 => true,                     // CUlonglong - any u64 fits
1587            _ => false,
1588        }
1589    }
1590
1591    /// Check if a u64 value can be negated to fit within the range of this signed integer type.
1592    ///
1593    /// This is used to allow literals like `2147483648` when negated to `-2147483648` (i32::MIN).
1594    /// Returns `true` if the negated value fits, `false` otherwise.
1595    #[must_use]
1596    pub fn negated_literal_fits(&self, value: u64) -> bool {
1597        match self.0 {
1598            0 => value <= (i8::MIN as i64).unsigned_abs(),  // I8
1599            1 => value <= (i16::MIN as i64).unsigned_abs(), // I16
1600            2 => value <= (i32::MIN as i64).unsigned_abs(), // I32
1601            3 => value <= (i64::MIN).unsigned_abs(),        // I64
1602            8 => value <= (i64::MIN).unsigned_abs(),        // Isize - 64-bit on current targets
1603            // ADR-0086 C named signed integers — same width as their underlying type.
1604            21 => value <= (i8::MIN as i64).unsigned_abs(), // CSchar
1605            22 => value <= (i16::MIN as i64).unsigned_abs(), // CShort
1606            23 => value <= (i32::MIN as i64).unsigned_abs(), // CInt
1607            24 => value <= (i64::MIN).unsigned_abs(),       // CLong (LP64)
1608            25 => value <= (i64::MIN).unsigned_abs(),       // CLonglong
1609            _ => false,
1610        }
1611    }
1612
1613    /// Encode this type as a u32 for storage in extra arrays.
1614    ///
1615    /// Since Type is now a u32 newtype, this simply returns the inner value.
1616    #[inline]
1617    pub fn as_u32(&self) -> u32 {
1618        self.0
1619    }
1620
1621    /// Decode a type from a u32 value.
1622    ///
1623    /// Since Type is now a u32 newtype, this simply wraps the value.
1624    /// Note: This does not validate the encoding - use with values from `as_u32()`.
1625    ///
1626    /// # Safety (not unsafe, but correctness)
1627    ///
1628    /// This method trusts that the input is a valid encoding. For untrusted data,
1629    /// use [`try_from_u32`](Self::try_from_u32) which validates the encoding.
1630    #[inline]
1631    pub fn from_u32(v: u32) -> Self {
1632        Type(v)
1633    }
1634
1635    /// Try to decode a type from a u32 value, returning `None` if invalid.
1636    ///
1637    /// This validates that the encoding represents a valid type before returning.
1638    /// Use this when reading potentially corrupt data (e.g., deserialization,
1639    /// memory-mapped files, or debugging).
1640    ///
1641    /// # Example
1642    ///
1643    /// ```ignore
1644    /// if let Some(ty) = Type::try_from_u32(encoded) {
1645    ///     // Safe to use ty.kind()
1646    /// } else {
1647    ///     // Handle invalid encoding
1648    /// }
1649    /// ```
1650    #[inline]
1651    pub fn try_from_u32(v: u32) -> Option<Self> {
1652        if Self::is_valid_encoding(v) {
1653            Some(Type(v))
1654        } else {
1655            None
1656        }
1657    }
1658
1659    /// Check if a u32 value is a valid Type encoding.
1660    ///
1661    /// Returns `true` if the value represents a valid primitive or composite type.
1662    #[inline]
1663    pub fn is_valid_encoding(v: u32) -> bool {
1664        let tag = v & 0xFF;
1665        match tag {
1666            // Primitive types: I8=0 through ComptimeInt=19
1667            0..=19 => true,
1668            // Composite types with valid tags
1669            TAG_STRUCT | TAG_ENUM | TAG_ARRAY | TAG_PTR_CONST | TAG_PTR_MUT | TAG_MODULE
1670            | TAG_INTERFACE | TAG_REF | TAG_MUT_REF => true,
1671            // Everything else is invalid
1672            _ => false,
1673        }
1674    }
1675
1676    /// Check if this Type has a valid encoding.
1677    ///
1678    /// This is useful for debugging and assertions.
1679    #[inline]
1680    pub fn is_valid(&self) -> bool {
1681        Self::is_valid_encoding(self.0)
1682    }
1683}
1684
1685impl std::fmt::Display for Type {
1686    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1687        write!(f, "{}", self.name())
1688    }
1689}
1690
1691/// Pointer mutability - whether the pointed-to data can be modified.
1692#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1693pub enum PtrMutability {
1694    /// Immutable pointer (`ptr const T`)
1695    Const,
1696    /// Mutable pointer (`ptr mut T`)
1697    Mut,
1698}
1699
1700/// Parse pointer type syntax "ptr const T" or "ptr mut T" and return (pointee_type_str, mutability).
1701///
1702/// Returns `None` if the string doesn't match pointer syntax.
1703pub fn parse_pointer_type_syntax(type_name: &str) -> Option<(String, PtrMutability)> {
1704    let type_name = type_name.trim();
1705    if let Some(rest) = type_name.strip_prefix("ptr const ") {
1706        Some((rest.trim().to_string(), PtrMutability::Const))
1707    } else {
1708        type_name
1709            .strip_prefix("ptr mut ")
1710            .map(|rest| (rest.trim().to_string(), PtrMutability::Mut))
1711    }
1712}
1713
1714/// Parse a type-call syntax like `"Name(arg1, arg2)"` (ADR-0057). Returns
1715/// `(callee_name, [arg_strs])` if `s` matches the pattern, else `None`.
1716///
1717/// Argument splitting is paren/bracket aware so nested type calls like
1718/// `Outer(Inner(i32))` and `Pair([i32; 4], i64)` parse correctly.
1719pub fn parse_type_call_syntax(s: &str) -> Option<(String, Vec<String>)> {
1720    let s = s.trim();
1721    if !s.ends_with(')') {
1722        return None;
1723    }
1724    let open = s.find('(')?;
1725    let callee = s[..open].trim().to_string();
1726    if callee.is_empty() {
1727        return None;
1728    }
1729    // The callee must be an identifier (no whitespace, no special chars).
1730    if !callee.chars().all(|c| c.is_alphanumeric() || c == '_') {
1731        return None;
1732    }
1733    let inner = &s[open + 1..s.len() - 1];
1734    let mut args: Vec<String> = Vec::new();
1735    let mut depth: i32 = 0;
1736    let mut current = String::new();
1737    for ch in inner.chars() {
1738        match ch {
1739            '(' | '[' => {
1740                depth += 1;
1741                current.push(ch);
1742            }
1743            ')' | ']' => {
1744                depth -= 1;
1745                current.push(ch);
1746            }
1747            ',' if depth == 0 => {
1748                args.push(current.trim().to_string());
1749                current.clear();
1750            }
1751            _ => current.push(ch),
1752        }
1753    }
1754    let trimmed = current.trim();
1755    if !trimmed.is_empty() {
1756        args.push(trimmed.to_string());
1757    }
1758    if args.is_empty() {
1759        return None;
1760    }
1761    Some((callee, args))
1762}
1763
1764/// Parse array type syntax "[T; N]" and return (element_type_str, length).
1765///
1766/// This handles nested arrays correctly by tracking bracket depth.
1767/// For example, `[[i32; 3]; 4]` returns `("[i32; 3]", 4)`.
1768pub fn parse_array_type_syntax(type_name: &str) -> Option<(String, u64)> {
1769    let type_name = type_name.trim();
1770    if !type_name.starts_with('[') || !type_name.ends_with(']') {
1771        return None;
1772    }
1773
1774    // Remove the outer brackets
1775    let inner = &type_name[1..type_name.len() - 1];
1776
1777    // Find the semicolon separator - need to handle nested arrays
1778    // We look for the last `;` that's at nesting level 0
1779    let mut bracket_depth = 0;
1780    let mut semi_pos = None;
1781    for (i, ch) in inner.char_indices() {
1782        match ch {
1783            '[' => bracket_depth += 1,
1784            ']' => bracket_depth -= 1,
1785            ';' if bracket_depth == 0 => semi_pos = Some(i),
1786            _ => {}
1787        }
1788    }
1789
1790    let semi_pos = semi_pos?;
1791    let element_type = inner[..semi_pos].trim().to_string();
1792    let length_str = inner[semi_pos + 1..].trim();
1793    let length: u64 = length_str.parse().ok()?;
1794
1795    Some((element_type, length))
1796}
1797
1798/// Parse tuple type syntax "(T0, T1, ..., TN-1)" into a Vec of element-type strings.
1799///
1800/// Returns `None` if the input is not a tuple-shaped type name. Rules:
1801/// - Must start with `(` and end with `)`.
1802/// - The contents are split on commas at nesting depth 0 (respecting parens and brackets).
1803/// - `()` (empty) returns `None` — unit type is handled separately as a primitive.
1804/// - `(T)` (parens around a single type, no trailing comma) is not a tuple, returns `None`.
1805/// - `(T,)` is a 1-tuple (single element).
1806/// - Trailing comma is tolerated in 2+ arities.
1807///
1808/// This handles nesting correctly: `((i32, u8), bool)` returns `["(i32, u8)", "bool"]`.
1809pub fn parse_tuple_type_syntax(type_name: &str) -> Option<Vec<String>> {
1810    let type_name = type_name.trim();
1811    if !type_name.starts_with('(') || !type_name.ends_with(')') {
1812        return None;
1813    }
1814    let inner = type_name[1..type_name.len() - 1].trim();
1815    // Empty inside `()` — that's the unit type, not a tuple.
1816    if inner.is_empty() {
1817        return None;
1818    }
1819
1820    // Split on commas that are at nesting depth 0.
1821    let mut parts: Vec<String> = Vec::new();
1822    let mut depth_paren = 0i32;
1823    let mut depth_bracket = 0i32;
1824    let mut last = 0usize;
1825    for (i, ch) in inner.char_indices() {
1826        match ch {
1827            '(' => depth_paren += 1,
1828            ')' => depth_paren -= 1,
1829            '[' => depth_bracket += 1,
1830            ']' => depth_bracket -= 1,
1831            ',' if depth_paren == 0 && depth_bracket == 0 => {
1832                parts.push(inner[last..i].trim().to_string());
1833                last = i + 1;
1834            }
1835            _ => {}
1836        }
1837    }
1838    let tail = inner[last..].trim();
1839    // If we saw no commas at all, `(T)` is a parenthesised type, not a tuple.
1840    if parts.is_empty() {
1841        return None;
1842    }
1843    // If the tail is non-empty, append it; otherwise we had a trailing comma.
1844    if !tail.is_empty() {
1845        parts.push(tail.to_string());
1846    }
1847    // All parts must be non-empty.
1848    if parts.iter().any(|p| p.is_empty()) {
1849        return None;
1850    }
1851    Some(parts)
1852}
1853
1854#[cfg(test)]
1855mod tests {
1856    use super::*;
1857
1858    #[test]
1859    fn test_parse_tuple_pair() {
1860        assert_eq!(
1861            parse_tuple_type_syntax("(i32, bool)"),
1862            Some(vec!["i32".into(), "bool".into()])
1863        );
1864    }
1865
1866    #[test]
1867    fn test_parse_tuple_singleton() {
1868        assert_eq!(parse_tuple_type_syntax("(i32,)"), Some(vec!["i32".into()]));
1869    }
1870
1871    #[test]
1872    fn test_parse_tuple_triple_with_trailing_comma() {
1873        assert_eq!(
1874            parse_tuple_type_syntax("(i32, u8, bool,)"),
1875            Some(vec!["i32".into(), "u8".into(), "bool".into()])
1876        );
1877    }
1878
1879    #[test]
1880    fn test_parse_tuple_nested() {
1881        assert_eq!(
1882            parse_tuple_type_syntax("((i32, u8), bool)"),
1883            Some(vec!["(i32, u8)".into(), "bool".into()])
1884        );
1885    }
1886
1887    #[test]
1888    fn test_parse_tuple_with_array_element() {
1889        assert_eq!(
1890            parse_tuple_type_syntax("([i32; 3], bool)"),
1891            Some(vec!["[i32; 3]".into(), "bool".into()])
1892        );
1893    }
1894
1895    #[test]
1896    fn test_parse_tuple_unit_is_not_tuple() {
1897        assert_eq!(parse_tuple_type_syntax("()"), None);
1898    }
1899
1900    #[test]
1901    fn test_parse_tuple_single_paren_is_not_tuple() {
1902        // (i32) with no comma — parenthesised type, not a tuple
1903        assert_eq!(parse_tuple_type_syntax("(i32)"), None);
1904    }
1905
1906    #[test]
1907    fn test_parse_tuple_non_tuple() {
1908        assert_eq!(parse_tuple_type_syntax("i32"), None);
1909        assert_eq!(parse_tuple_type_syntax("[i32; 3]"), None);
1910    }
1911
1912    // ========== Type ID tests ==========
1913
1914    #[test]
1915    fn test_struct_id_equality() {
1916        let id1 = StructId(0);
1917        let id2 = StructId(0);
1918        let id3 = StructId(1);
1919        assert_eq!(id1, id2);
1920        assert_ne!(id1, id3);
1921    }
1922
1923    #[test]
1924    fn test_enum_id_equality() {
1925        let id1 = EnumId(0);
1926        let id2 = EnumId(0);
1927        let id3 = EnumId(1);
1928        assert_eq!(id1, id2);
1929        assert_ne!(id1, id3);
1930    }
1931
1932    #[test]
1933    fn test_array_type_id_equality() {
1934        let id1 = ArrayTypeId(0);
1935        let id2 = ArrayTypeId(0);
1936        let id3 = ArrayTypeId(1);
1937        assert_eq!(id1, id2);
1938        assert_ne!(id1, id3);
1939    }
1940
1941    // ========== Type::name() tests ==========
1942
1943    #[test]
1944    fn test_type_name_integers() {
1945        assert_eq!(Type::I8.name(), "i8");
1946        assert_eq!(Type::I16.name(), "i16");
1947        assert_eq!(Type::I32.name(), "i32");
1948        assert_eq!(Type::I64.name(), "i64");
1949        assert_eq!(Type::U8.name(), "u8");
1950        assert_eq!(Type::U16.name(), "u16");
1951        assert_eq!(Type::U32.name(), "u32");
1952        assert_eq!(Type::U64.name(), "u64");
1953    }
1954
1955    #[test]
1956    fn test_type_name_other() {
1957        assert_eq!(Type::BOOL.name(), "bool");
1958        assert_eq!(Type::UNIT.name(), "()");
1959        assert_eq!(Type::ERROR.name(), "<error>");
1960        assert_eq!(Type::NEVER.name(), "!");
1961    }
1962
1963    #[test]
1964    fn test_type_name_composite() {
1965        assert_eq!(Type::new_struct(StructId(0)).name(), "<struct>");
1966        assert_eq!(Type::new_enum(EnumId(0)).name(), "<enum>");
1967        assert_eq!(Type::new_array(ArrayTypeId(0)).name(), "<array>");
1968    }
1969
1970    // ========== Type::is_integer() tests ==========
1971
1972    #[test]
1973    fn test_is_integer_signed() {
1974        assert!(Type::I8.is_integer());
1975        assert!(Type::I16.is_integer());
1976        assert!(Type::I32.is_integer());
1977        assert!(Type::I64.is_integer());
1978    }
1979
1980    #[test]
1981    fn test_is_integer_unsigned() {
1982        assert!(Type::U8.is_integer());
1983        assert!(Type::U16.is_integer());
1984        assert!(Type::U32.is_integer());
1985        assert!(Type::U64.is_integer());
1986    }
1987
1988    #[test]
1989    fn test_is_integer_non_integers() {
1990        assert!(!Type::BOOL.is_integer());
1991        assert!(!Type::UNIT.is_integer());
1992        assert!(!Type::new_struct(StructId(0)).is_integer());
1993        assert!(!Type::new_enum(EnumId(0)).is_integer());
1994        assert!(!Type::new_array(ArrayTypeId(0)).is_integer());
1995        assert!(!Type::ERROR.is_integer());
1996        assert!(!Type::NEVER.is_integer());
1997    }
1998
1999    // ========== Type::is_signed() tests ==========
2000
2001    #[test]
2002    fn test_is_signed() {
2003        assert!(Type::I8.is_signed());
2004        assert!(Type::I16.is_signed());
2005        assert!(Type::I32.is_signed());
2006        assert!(Type::I64.is_signed());
2007
2008        assert!(!Type::U8.is_signed());
2009        assert!(!Type::U16.is_signed());
2010        assert!(!Type::U32.is_signed());
2011        assert!(!Type::U64.is_signed());
2012        assert!(!Type::BOOL.is_signed());
2013    }
2014
2015    // ========== Type::is_unsigned() tests ==========
2016
2017    #[test]
2018    fn test_is_unsigned() {
2019        assert!(Type::U8.is_unsigned());
2020        assert!(Type::U16.is_unsigned());
2021        assert!(Type::U32.is_unsigned());
2022        assert!(Type::U64.is_unsigned());
2023
2024        assert!(!Type::I8.is_unsigned());
2025        assert!(!Type::I16.is_unsigned());
2026        assert!(!Type::I32.is_unsigned());
2027        assert!(!Type::I64.is_unsigned());
2028        assert!(!Type::BOOL.is_unsigned());
2029    }
2030
2031    // ========== Type::is_64_bit() tests ==========
2032
2033    #[test]
2034    fn test_is_64_bit() {
2035        assert!(Type::I64.is_64_bit());
2036        assert!(Type::U64.is_64_bit());
2037
2038        assert!(!Type::I8.is_64_bit());
2039        assert!(!Type::I16.is_64_bit());
2040        assert!(!Type::I32.is_64_bit());
2041        assert!(!Type::U8.is_64_bit());
2042        assert!(!Type::U16.is_64_bit());
2043        assert!(!Type::U32.is_64_bit());
2044        assert!(!Type::BOOL.is_64_bit());
2045    }
2046
2047    // ========== Type::is_error() tests ==========
2048
2049    #[test]
2050    fn test_is_error() {
2051        assert!(Type::ERROR.is_error());
2052        assert!(!Type::I32.is_error());
2053        assert!(!Type::NEVER.is_error());
2054    }
2055
2056    // ========== Type::is_never() tests ==========
2057
2058    #[test]
2059    fn test_is_never() {
2060        assert!(Type::NEVER.is_never());
2061        assert!(!Type::I32.is_never());
2062        assert!(!Type::ERROR.is_never());
2063    }
2064
2065    // ========== Type::is_struct() and as_struct() tests ==========
2066
2067    #[test]
2068    fn test_is_struct() {
2069        assert!(Type::new_struct(StructId(0)).is_struct());
2070        assert!(Type::new_struct(StructId(42)).is_struct());
2071        assert!(!Type::I32.is_struct());
2072        assert!(!Type::new_enum(EnumId(0)).is_struct());
2073    }
2074
2075    #[test]
2076    fn test_as_struct() {
2077        assert_eq!(Type::new_struct(StructId(5)).as_struct(), Some(StructId(5)));
2078        assert_eq!(Type::I32.as_struct(), None);
2079        assert_eq!(Type::new_enum(EnumId(0)).as_struct(), None);
2080    }
2081
2082    // ========== Type::is_enum() and as_enum() tests ==========
2083
2084    #[test]
2085    fn test_is_enum() {
2086        assert!(Type::new_enum(EnumId(0)).is_enum());
2087        assert!(Type::new_enum(EnumId(42)).is_enum());
2088        assert!(!Type::I32.is_enum());
2089        assert!(!Type::new_struct(StructId(0)).is_enum());
2090    }
2091
2092    #[test]
2093    fn test_as_enum() {
2094        assert_eq!(Type::new_enum(EnumId(5)).as_enum(), Some(EnumId(5)));
2095        assert_eq!(Type::I32.as_enum(), None);
2096        assert_eq!(Type::new_struct(StructId(0)).as_enum(), None);
2097    }
2098
2099    // ========== Type::is_array() and as_array() tests ==========
2100
2101    #[test]
2102    fn test_is_array() {
2103        assert!(Type::new_array(ArrayTypeId(0)).is_array());
2104        assert!(Type::new_array(ArrayTypeId(42)).is_array());
2105        assert!(!Type::I32.is_array());
2106        assert!(!Type::new_struct(StructId(0)).is_array());
2107    }
2108
2109    #[test]
2110    fn test_as_array() {
2111        assert_eq!(
2112            Type::new_array(ArrayTypeId(5)).as_array(),
2113            Some(ArrayTypeId(5))
2114        );
2115        assert_eq!(Type::I32.as_array(), None);
2116        assert_eq!(Type::new_struct(StructId(0)).as_array(), None);
2117    }
2118
2119    // ========== Type::is_copy() tests ==========
2120
2121    #[test]
2122    fn test_is_copy_primitives() {
2123        // All integer types are Copy
2124        assert!(Type::I8.is_copy());
2125        assert!(Type::I16.is_copy());
2126        assert!(Type::I32.is_copy());
2127        assert!(Type::I64.is_copy());
2128        assert!(Type::U8.is_copy());
2129        assert!(Type::U16.is_copy());
2130        assert!(Type::U32.is_copy());
2131        assert!(Type::U64.is_copy());
2132
2133        // Bool and Unit are Copy
2134        assert!(Type::BOOL.is_copy());
2135        assert!(Type::UNIT.is_copy());
2136    }
2137
2138    #[test]
2139    fn test_is_copy_special() {
2140        // Enum types are Copy
2141        assert!(Type::new_enum(EnumId(0)).is_copy());
2142
2143        // Never and Error are Copy for convenience
2144        assert!(Type::NEVER.is_copy());
2145        assert!(Type::ERROR.is_copy());
2146    }
2147
2148    #[test]
2149    fn test_is_copy_move_types() {
2150        // Struct and Array are move types (String is a builtin struct now)
2151        assert!(!Type::new_struct(StructId(0)).is_copy());
2152        assert!(!Type::new_array(ArrayTypeId(0)).is_copy());
2153    }
2154
2155    // ========== Type::can_coerce_to() tests ==========
2156
2157    #[test]
2158    fn test_can_coerce_to_same_type() {
2159        assert!(Type::I32.can_coerce_to(&Type::I32));
2160        assert!(Type::BOOL.can_coerce_to(&Type::BOOL));
2161        assert!(Type::new_struct(StructId(0)).can_coerce_to(&Type::new_struct(StructId(0))));
2162    }
2163
2164    #[test]
2165    fn test_can_coerce_to_never_coerces_to_anything() {
2166        assert!(Type::NEVER.can_coerce_to(&Type::I32));
2167        assert!(Type::NEVER.can_coerce_to(&Type::BOOL));
2168        assert!(Type::NEVER.can_coerce_to(&Type::new_struct(StructId(0))));
2169    }
2170
2171    #[test]
2172    fn test_can_coerce_to_error_coerces_to_anything() {
2173        assert!(Type::ERROR.can_coerce_to(&Type::I32));
2174        assert!(Type::ERROR.can_coerce_to(&Type::BOOL));
2175        assert!(Type::ERROR.can_coerce_to(&Type::new_struct(StructId(0))));
2176    }
2177
2178    #[test]
2179    fn test_can_coerce_to_different_types_fail() {
2180        assert!(!Type::I32.can_coerce_to(&Type::BOOL));
2181        assert!(!Type::BOOL.can_coerce_to(&Type::I32));
2182        assert!(!Type::I32.can_coerce_to(&Type::I64));
2183        assert!(!Type::new_struct(StructId(0)).can_coerce_to(&Type::I32));
2184    }
2185
2186    // ========== Type::literal_fits() tests ==========
2187
2188    #[test]
2189    fn test_literal_fits_i8() {
2190        assert!(Type::I8.literal_fits(0));
2191        assert!(Type::I8.literal_fits(127)); // i8::MAX
2192        assert!(!Type::I8.literal_fits(128));
2193    }
2194
2195    #[test]
2196    fn test_literal_fits_i16() {
2197        assert!(Type::I16.literal_fits(0));
2198        assert!(Type::I16.literal_fits(32767)); // i16::MAX
2199        assert!(!Type::I16.literal_fits(32768));
2200    }
2201
2202    #[test]
2203    fn test_literal_fits_i32() {
2204        assert!(Type::I32.literal_fits(0));
2205        assert!(Type::I32.literal_fits(2147483647)); // i32::MAX
2206        assert!(!Type::I32.literal_fits(2147483648));
2207    }
2208
2209    #[test]
2210    fn test_literal_fits_i64() {
2211        assert!(Type::I64.literal_fits(0));
2212        assert!(Type::I64.literal_fits(9223372036854775807)); // i64::MAX
2213        assert!(!Type::I64.literal_fits(9223372036854775808));
2214    }
2215
2216    #[test]
2217    fn test_literal_fits_u8() {
2218        assert!(Type::U8.literal_fits(0));
2219        assert!(Type::U8.literal_fits(255)); // u8::MAX
2220        assert!(!Type::U8.literal_fits(256));
2221    }
2222
2223    #[test]
2224    fn test_literal_fits_u16() {
2225        assert!(Type::U16.literal_fits(0));
2226        assert!(Type::U16.literal_fits(65535)); // u16::MAX
2227        assert!(!Type::U16.literal_fits(65536));
2228    }
2229
2230    #[test]
2231    fn test_literal_fits_u32() {
2232        assert!(Type::U32.literal_fits(0));
2233        assert!(Type::U32.literal_fits(4294967295)); // u32::MAX
2234        assert!(!Type::U32.literal_fits(4294967296));
2235    }
2236
2237    #[test]
2238    fn test_literal_fits_u64() {
2239        assert!(Type::U64.literal_fits(0));
2240        assert!(Type::U64.literal_fits(u64::MAX)); // Any u64 fits
2241    }
2242
2243    #[test]
2244    fn test_literal_fits_non_integer() {
2245        assert!(!Type::BOOL.literal_fits(0));
2246        assert!(!Type::new_struct(StructId(0)).literal_fits(0));
2247        assert!(!Type::UNIT.literal_fits(0));
2248    }
2249
2250    // ========== Type::negated_literal_fits() tests ==========
2251
2252    #[test]
2253    fn test_negated_literal_fits_i8() {
2254        assert!(Type::I8.negated_literal_fits(128)); // -128 = i8::MIN
2255        assert!(!Type::I8.negated_literal_fits(129));
2256    }
2257
2258    #[test]
2259    fn test_negated_literal_fits_i16() {
2260        assert!(Type::I16.negated_literal_fits(32768)); // -32768 = i16::MIN
2261        assert!(!Type::I16.negated_literal_fits(32769));
2262    }
2263
2264    #[test]
2265    fn test_negated_literal_fits_i32() {
2266        assert!(Type::I32.negated_literal_fits(2147483648)); // -2147483648 = i32::MIN
2267        assert!(!Type::I32.negated_literal_fits(2147483649));
2268    }
2269
2270    #[test]
2271    fn test_negated_literal_fits_i64() {
2272        assert!(Type::I64.negated_literal_fits(9223372036854775808)); // i64::MIN abs
2273        assert!(!Type::I64.negated_literal_fits(9223372036854775809));
2274    }
2275
2276    #[test]
2277    fn test_negated_literal_fits_unsigned() {
2278        // Unsigned types don't support negated literals
2279        assert!(!Type::U8.negated_literal_fits(1));
2280        assert!(!Type::U16.negated_literal_fits(1));
2281        assert!(!Type::U32.negated_literal_fits(1));
2282        assert!(!Type::U64.negated_literal_fits(1));
2283    }
2284
2285    #[test]
2286    fn test_negated_literal_fits_non_integer() {
2287        assert!(!Type::BOOL.negated_literal_fits(1));
2288        assert!(!Type::new_struct(StructId(0)).negated_literal_fits(1));
2289    }
2290
2291    // ========== Type Display tests ==========
2292
2293    #[test]
2294    fn test_type_display() {
2295        assert_eq!(format!("{}", Type::I32), "i32");
2296        assert_eq!(format!("{}", Type::BOOL), "bool");
2297        assert_eq!(format!("{}", Type::NEVER), "!");
2298    }
2299
2300    // ========== Type Default tests ==========
2301
2302    #[test]
2303    fn test_type_default() {
2304        assert_eq!(Type::default(), Type::UNIT);
2305    }
2306
2307    // ========== StructDef tests ==========
2308
2309    #[test]
2310    fn test_struct_def_find_field() {
2311        let def = StructDef {
2312            name: "Point".to_string(),
2313            fields: vec![
2314                StructField {
2315                    name: "x".to_string(),
2316                    ty: Type::I32,
2317
2318                    is_pub: true,
2319                },
2320                StructField {
2321                    name: "y".to_string(),
2322                    ty: Type::I32,
2323
2324                    is_pub: true,
2325                },
2326            ],
2327            posture: Posture::Affine,
2328            is_clone: false,
2329            thread_safety: ThreadSafety::Sync,
2330            destructor: None,
2331            is_builtin: false,
2332            is_pub: false,
2333            file_id: gruel_util::FileId::DEFAULT,
2334            is_c_layout: false,
2335        };
2336
2337        let (idx, field) = def.find_field("x").unwrap();
2338        assert_eq!(idx, 0);
2339        assert_eq!(field.name, "x");
2340        assert_eq!(field.ty, Type::I32);
2341
2342        let (idx, field) = def.find_field("y").unwrap();
2343        assert_eq!(idx, 1);
2344        assert_eq!(field.name, "y");
2345
2346        assert!(def.find_field("z").is_none());
2347    }
2348
2349    #[test]
2350    fn test_struct_def_field_count() {
2351        let empty = StructDef {
2352            name: "Empty".to_string(),
2353            fields: vec![],
2354            posture: Posture::Affine,
2355            is_clone: false,
2356            thread_safety: ThreadSafety::Sync,
2357            destructor: None,
2358            is_builtin: false,
2359            is_pub: false,
2360            file_id: gruel_util::FileId::DEFAULT,
2361            is_c_layout: false,
2362        };
2363        assert_eq!(empty.field_count(), 0);
2364
2365        let with_fields = StructDef {
2366            name: "Data".to_string(),
2367            fields: vec![
2368                StructField {
2369                    name: "a".to_string(),
2370                    ty: Type::I32,
2371
2372                    is_pub: true,
2373                },
2374                StructField {
2375                    name: "b".to_string(),
2376                    ty: Type::BOOL,
2377
2378                    is_pub: true,
2379                },
2380                StructField {
2381                    name: "c".to_string(),
2382                    ty: Type::I64,
2383
2384                    is_pub: true,
2385                },
2386            ],
2387            posture: Posture::Affine,
2388            is_clone: false,
2389            thread_safety: ThreadSafety::Sync,
2390            destructor: None,
2391            is_builtin: false,
2392            is_pub: false,
2393            file_id: gruel_util::FileId::DEFAULT,
2394            is_c_layout: false,
2395        };
2396        assert_eq!(with_fields.field_count(), 3);
2397    }
2398
2399    // ========== EnumDef tests ==========
2400
2401    #[test]
2402    fn test_enum_def_variant_count() {
2403        let empty = EnumDef {
2404            name: "Empty".to_string(),
2405            variants: vec![],
2406            posture: Posture::Affine,
2407            thread_safety: ThreadSafety::Sync,
2408            is_pub: false,
2409            file_id: gruel_util::FileId::DEFAULT,
2410            destructor: None,
2411            is_c_layout: false,
2412        };
2413        assert_eq!(empty.variant_count(), 0);
2414
2415        let color = EnumDef {
2416            name: "Color".to_string(),
2417            variants: vec![
2418                EnumVariantDef::unit("Red"),
2419                EnumVariantDef::unit("Green"),
2420                EnumVariantDef::unit("Blue"),
2421            ],
2422            posture: Posture::Affine,
2423            thread_safety: ThreadSafety::Sync,
2424            is_pub: false,
2425            file_id: gruel_util::FileId::DEFAULT,
2426            destructor: None,
2427            is_c_layout: false,
2428        };
2429        assert_eq!(color.variant_count(), 3);
2430    }
2431
2432    #[test]
2433    fn test_enum_def_find_variant() {
2434        let color = EnumDef {
2435            name: "Color".to_string(),
2436            variants: vec![
2437                EnumVariantDef::unit("Red"),
2438                EnumVariantDef::unit("Green"),
2439                EnumVariantDef::unit("Blue"),
2440            ],
2441            posture: Posture::Affine,
2442            thread_safety: ThreadSafety::Sync,
2443            is_pub: false,
2444            file_id: gruel_util::FileId::DEFAULT,
2445            destructor: None,
2446            is_c_layout: false,
2447        };
2448
2449        assert_eq!(color.find_variant("Red"), Some(0));
2450        assert_eq!(color.find_variant("Green"), Some(1));
2451        assert_eq!(color.find_variant("Blue"), Some(2));
2452        assert_eq!(color.find_variant("Yellow"), None);
2453    }
2454
2455    #[test]
2456    fn test_enum_def_discriminant_type_empty() {
2457        let empty = EnumDef {
2458            name: "Empty".to_string(),
2459            variants: vec![],
2460            posture: Posture::Affine,
2461            thread_safety: ThreadSafety::Sync,
2462            is_pub: false,
2463            file_id: gruel_util::FileId::DEFAULT,
2464            destructor: None,
2465            is_c_layout: false,
2466        };
2467        assert_eq!(empty.discriminant_type(), Type::NEVER);
2468    }
2469
2470    #[test]
2471    fn test_enum_def_discriminant_type_small() {
2472        // 1-256 variants -> U8
2473        let small = EnumDef {
2474            name: "Small".to_string(),
2475            variants: vec![EnumVariantDef::unit("A")],
2476            posture: Posture::Affine,
2477            thread_safety: ThreadSafety::Sync,
2478            is_pub: false,
2479            file_id: gruel_util::FileId::DEFAULT,
2480            destructor: None,
2481            is_c_layout: false,
2482        };
2483        assert_eq!(small.discriminant_type(), Type::U8);
2484
2485        let max_u8 = EnumDef {
2486            name: "MaxU8".to_string(),
2487            variants: (0..256)
2488                .map(|i| EnumVariantDef::unit(format!("V{}", i)))
2489                .collect(),
2490            posture: Posture::Affine,
2491            thread_safety: ThreadSafety::Sync,
2492            is_pub: false,
2493            file_id: gruel_util::FileId::DEFAULT,
2494            destructor: None,
2495            is_c_layout: false,
2496        };
2497        assert_eq!(max_u8.discriminant_type(), Type::U8);
2498    }
2499
2500    #[test]
2501    fn test_enum_def_discriminant_type_medium() {
2502        // 257-65536 variants -> U16
2503        let medium = EnumDef {
2504            name: "Medium".to_string(),
2505            variants: (0..257)
2506                .map(|i| EnumVariantDef::unit(format!("V{}", i)))
2507                .collect(),
2508            posture: Posture::Affine,
2509            thread_safety: ThreadSafety::Sync,
2510            is_pub: false,
2511            file_id: gruel_util::FileId::DEFAULT,
2512            destructor: None,
2513            is_c_layout: false,
2514        };
2515        assert_eq!(medium.discriminant_type(), Type::U16);
2516    }
2517
2518    // ========== Type::COMPTIME_TYPE tests ==========
2519
2520    #[test]
2521    fn test_comptime_type_name() {
2522        assert_eq!(Type::COMPTIME_TYPE.name(), "type");
2523    }
2524
2525    #[test]
2526    fn test_comptime_type_is_copy() {
2527        assert!(Type::COMPTIME_TYPE.is_copy());
2528    }
2529
2530    #[test]
2531    fn test_comptime_type_is_comptime_type() {
2532        assert!(Type::COMPTIME_TYPE.is_comptime_type());
2533        assert!(!Type::I32.is_comptime_type());
2534        assert!(!Type::BOOL.is_comptime_type());
2535    }
2536
2537    #[test]
2538    fn test_comptime_type_not_integer() {
2539        assert!(!Type::COMPTIME_TYPE.is_integer());
2540    }
2541
2542    #[test]
2543    fn test_comptime_type_not_signed() {
2544        assert!(!Type::COMPTIME_TYPE.is_signed());
2545    }
2546
2547    #[test]
2548    fn test_comptime_type_not_64_bit() {
2549        assert!(!Type::COMPTIME_TYPE.is_64_bit());
2550    }
2551
2552    #[test]
2553    fn test_comptime_type_can_coerce_to_itself() {
2554        assert!(Type::COMPTIME_TYPE.can_coerce_to(&Type::COMPTIME_TYPE));
2555    }
2556
2557    #[test]
2558    fn test_comptime_type_cannot_coerce_to_runtime_types() {
2559        assert!(!Type::COMPTIME_TYPE.can_coerce_to(&Type::I32));
2560        assert!(!Type::COMPTIME_TYPE.can_coerce_to(&Type::BOOL));
2561    }
2562
2563    // ========== Type encoding validation tests ==========
2564
2565    #[test]
2566    fn test_is_valid_encoding_primitives() {
2567        // All primitive types (0-19) are valid
2568        for i in 0..=19u32 {
2569            assert!(
2570                Type::is_valid_encoding(i),
2571                "primitive tag {} should be valid",
2572                i
2573            );
2574        }
2575    }
2576
2577    #[test]
2578    fn test_is_valid_encoding_composites() {
2579        // Composite types with valid tags
2580        assert!(Type::is_valid_encoding(100)); // TAG_STRUCT
2581        assert!(Type::is_valid_encoding(101)); // TAG_ENUM
2582        assert!(Type::is_valid_encoding(102)); // TAG_ARRAY
2583        assert!(Type::is_valid_encoding(103)); // TAG_MODULE
2584        assert!(Type::is_valid_encoding(104)); // TAG_PTR_CONST
2585        assert!(Type::is_valid_encoding(105)); // TAG_PTR_MUT
2586
2587        // With IDs in the high bits
2588        assert!(Type::is_valid_encoding(100 | (42 << 8))); // Struct with ID 42
2589        assert!(Type::is_valid_encoding(101 | (100 << 8))); // Enum with ID 100
2590    }
2591
2592    #[test]
2593    fn test_is_valid_encoding_invalid() {
2594        // Tags between primitives and composites are invalid (20-99)
2595        for tag in 20..100u32 {
2596            assert!(
2597                !Type::is_valid_encoding(tag),
2598                "tag {} should be invalid",
2599                tag
2600            );
2601        }
2602
2603        // Tags above composites are invalid (109+)
2604        for tag in 109..=255u32 {
2605            assert!(
2606                !Type::is_valid_encoding(tag),
2607                "tag {} should be invalid",
2608                tag
2609            );
2610        }
2611    }
2612
2613    #[test]
2614    fn test_try_from_u32_valid() {
2615        // Valid primitives
2616        assert!(Type::try_from_u32(0).is_some()); // I8
2617        assert!(Type::try_from_u32(2).is_some()); // I32
2618        assert!(Type::try_from_u32(10).is_some()); // F16
2619
2620        // Valid composites
2621        assert!(Type::try_from_u32(100).is_some()); // Struct(0)
2622        assert!(Type::try_from_u32(100 | (42 << 8)).is_some()); // Struct(42)
2623    }
2624
2625    #[test]
2626    fn test_try_from_u32_invalid() {
2627        // Invalid tags
2628        assert!(Type::try_from_u32(50).is_none());
2629        assert!(Type::try_from_u32(99).is_none());
2630        assert!(Type::try_from_u32(109).is_none());
2631        assert!(Type::try_from_u32(255).is_none());
2632    }
2633
2634    #[test]
2635    fn test_try_kind_valid() {
2636        assert_eq!(Type::I32.try_kind(), Some(TypeKind::I32));
2637        assert_eq!(Type::BOOL.try_kind(), Some(TypeKind::Bool));
2638        assert_eq!(
2639            Type::new_struct(StructId(42)).try_kind(),
2640            Some(TypeKind::Struct(StructId(42)))
2641        );
2642    }
2643
2644    #[test]
2645    fn test_try_kind_invalid() {
2646        // Create an invalid Type by directly constructing with invalid encoding
2647        let invalid = Type::from_u32(50); // Tag 50 is invalid
2648        assert!(invalid.try_kind().is_none());
2649
2650        let invalid2 = Type::from_u32(200); // Tag 200 is invalid
2651        assert!(invalid2.try_kind().is_none());
2652    }
2653
2654    #[test]
2655    fn test_is_valid_method() {
2656        assert!(Type::I32.is_valid());
2657        assert!(Type::new_struct(StructId(0)).is_valid());
2658
2659        // Invalid types
2660        let invalid = Type::from_u32(50);
2661        assert!(!invalid.is_valid());
2662    }
2663
2664    #[test]
2665    #[should_panic(expected = "invalid Type encoding")]
2666    fn test_kind_panics_on_invalid() {
2667        let invalid = Type::from_u32(50);
2668        let _ = invalid.kind(); // Should panic
2669    }
2670
2671    #[test]
2672    fn test_roundtrip_encoding() {
2673        // Test that as_u32 and from_u32 are inverses for valid types
2674        let types = [
2675            Type::I8,
2676            Type::I16,
2677            Type::I32,
2678            Type::I64,
2679            Type::U8,
2680            Type::U16,
2681            Type::U32,
2682            Type::U64,
2683            Type::BOOL,
2684            Type::UNIT,
2685            Type::ERROR,
2686            Type::NEVER,
2687            Type::COMPTIME_TYPE,
2688            Type::COMPTIME_STR,
2689            Type::new_struct(StructId(0)),
2690            Type::new_struct(StructId(1000)),
2691            Type::new_enum(EnumId(5)),
2692            Type::new_array(ArrayTypeId(10)),
2693            Type::new_ptr_const(PtrConstTypeId(20)),
2694            Type::new_ptr_mut(PtrMutTypeId(30)),
2695            Type::new_module(ModuleId(40)),
2696        ];
2697
2698        for ty in types {
2699            let encoded = ty.as_u32();
2700            let decoded = Type::from_u32(encoded);
2701            assert_eq!(ty, decoded, "roundtrip failed for {:?}", ty);
2702            assert!(
2703                decoded.is_valid(),
2704                "{:?} should be valid after roundtrip",
2705                ty
2706            );
2707        }
2708    }
2709}