gruel_air/
types.rs

1//! Type system for Gruel.
2//!
3//! Currently very minimal - just i32. Will be extended as the language grows.
4
5/// A unique identifier for a struct definition.
6///
7/// As of Phase 3 (ADR-0024), the inner value is a pool index into `TypeInternPool`,
8/// not a vector index into a separate struct definitions array.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct StructId(pub u32);
11
12impl StructId {
13    /// Create a StructId from a pool index.
14    ///
15    /// This is the primary way to create StructIds during Phase 3+.
16    /// The pool index is the raw index into `TypeInternPool.types`.
17    #[inline]
18    pub fn from_pool_index(pool_index: u32) -> Self {
19        StructId(pool_index)
20    }
21
22    /// Get the pool index for this struct.
23    ///
24    /// This is the index into `TypeInternPool.types`.
25    #[inline]
26    pub fn pool_index(self) -> u32 {
27        self.0
28    }
29}
30
31/// A unique identifier for an enum definition.
32///
33/// As of Phase 3 (ADR-0024), the inner value is a pool index into `TypeInternPool`,
34/// not a vector index into a separate enum definitions array.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub struct EnumId(pub u32);
37
38impl EnumId {
39    /// Create an EnumId from a pool index.
40    ///
41    /// This is the primary way to create EnumIds during Phase 3+.
42    /// The pool index is the raw index into `TypeInternPool.types`.
43    #[inline]
44    pub fn from_pool_index(pool_index: u32) -> Self {
45        EnumId(pool_index)
46    }
47
48    /// Get the pool index for this enum.
49    ///
50    /// This is the index into `TypeInternPool.types`.
51    #[inline]
52    pub fn pool_index(self) -> u32 {
53        self.0
54    }
55}
56
57/// A unique identifier for an array type.
58/// This is needed because Type is Copy, so we can't use Box<Type> for the element type.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub struct ArrayTypeId(pub u32);
61
62impl ArrayTypeId {
63    /// Create an ArrayTypeId from a pool index.
64    ///
65    /// This is used during Phase 2B to create ArrayTypeIds from pool indices.
66    /// The pool index is the raw index into `TypeInternPool.types`.
67    #[inline]
68    pub fn from_pool_index(pool_index: u32) -> Self {
69        ArrayTypeId(pool_index)
70    }
71
72    /// Get the pool index for this array type.
73    ///
74    /// Returns the raw index into the TypeInternPool.
75    #[inline]
76    pub fn pool_index(self) -> u32 {
77        self.0
78    }
79}
80
81/// A unique identifier for a `ptr const T` type.
82/// This is needed because Type is Copy, so we can't use Box<Type> for the pointee type.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub struct PtrConstTypeId(pub u32);
85
86impl PtrConstTypeId {
87    /// Create a PtrConstTypeId from a pool index.
88    #[inline]
89    pub fn from_pool_index(pool_index: u32) -> Self {
90        PtrConstTypeId(pool_index)
91    }
92
93    /// Get the pool index for this pointer type.
94    #[inline]
95    pub fn pool_index(self) -> u32 {
96        self.0
97    }
98}
99
100/// A unique identifier for a `ptr mut T` type.
101/// This is needed because Type is Copy, so we can't use Box<Type> for the pointee type.
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub struct PtrMutTypeId(pub u32);
104
105impl PtrMutTypeId {
106    /// Create a PtrMutTypeId from a pool index.
107    #[inline]
108    pub fn from_pool_index(pool_index: u32) -> Self {
109        PtrMutTypeId(pool_index)
110    }
111
112    /// Get the pool index for this pointer type.
113    #[inline]
114    pub fn pool_index(self) -> u32 {
115        self.0
116    }
117}
118
119/// A unique identifier for a module (imported file).
120///
121/// Modules are created by `@import("path.gruel")` and represent the public
122/// declarations of an imported file.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124pub struct ModuleId(pub u32);
125
126impl ModuleId {
127    /// Create a ModuleId from an index.
128    #[inline]
129    pub fn new(index: u32) -> Self {
130        ModuleId(index)
131    }
132
133    /// Get the index for this module.
134    #[inline]
135    pub fn index(self) -> u32 {
136        self.0
137    }
138}
139
140/// The kind of a type - used for pattern matching.
141///
142/// This enum mirrors the structure of the `Type` enum but is designed for
143/// pattern matching. During the migration to `Type(InternedType)`, code that
144/// pattern matches on types will use `ty.kind()` to get a `TypeKind`.
145///
146/// This separation allows incremental migration: all pattern matches can be
147/// updated to use `.kind()` while `Type` is still an enum, then `Type` can be
148/// replaced with `Type(InternedType)` without breaking existing code.
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum TypeKind {
151    /// 8-bit signed integer
152    I8,
153    /// 16-bit signed integer
154    I16,
155    /// 32-bit signed integer
156    I32,
157    /// 64-bit signed integer
158    I64,
159    /// 8-bit unsigned integer
160    U8,
161    /// 16-bit unsigned integer
162    U16,
163    /// 32-bit unsigned integer
164    U32,
165    /// 64-bit unsigned integer
166    U64,
167    /// Pointer-sized signed integer
168    Isize,
169    /// Pointer-sized unsigned integer
170    Usize,
171    /// IEEE 754 binary16 (half-precision float)
172    F16,
173    /// IEEE 754 binary32 (single-precision float)
174    F32,
175    /// IEEE 754 binary64 (double-precision float)
176    F64,
177    /// Boolean
178    Bool,
179    /// The unit type (for functions that don't return a value)
180    Unit,
181    /// User-defined struct type
182    Struct(StructId),
183    /// User-defined enum type
184    Enum(EnumId),
185    /// Fixed-size array type: [T; N]
186    Array(ArrayTypeId),
187    /// Raw pointer to immutable data: ptr const T
188    PtrConst(PtrConstTypeId),
189    /// Raw pointer to mutable data: ptr mut T
190    PtrMut(PtrMutTypeId),
191    /// A module type (from @import)
192    Module(ModuleId),
193    /// An error type (used during type checking to continue after errors)
194    Error,
195    /// The never type - represents computations that don't return
196    Never,
197    /// The comptime type - the type of types themselves
198    ComptimeType,
199    /// The comptime string type - compile-time only string values
200    ComptimeStr,
201    /// The comptime integer type - compile-time only integer values
202    ComptimeInt,
203}
204
205/// A type in the Gruel type system.
206///
207/// After Phase 4.1 of ADR-0024, `Type` is a newtype wrapping a u32 index.
208/// This enables O(1) type equality via u32 comparison.
209///
210/// # Encoding
211///
212/// The u32 value uses a tag-based encoding:
213/// - Primitives (0-18): I8=0, I16=1, I32=2, I64=3, U8=4, U16=5, U32=6, U64=7,
214///   Isize=8, Usize=9, F16=10, F32=11, F64=12,
215///   Bool=13, Unit=14, Error=15, Never=16, ComptimeType=17, ComptimeStr=18, ComptimeInt=19
216/// - Composites: low byte is tag (TAG_STRUCT, TAG_ENUM, TAG_ARRAY, TAG_MODULE),
217///   high 24 bits are the ID
218///
219/// # Usage
220///
221/// Use the associated constants for primitive types:
222/// ```ignore
223/// let ty = Type::I32;
224/// ```
225///
226/// Use constructor methods for composite types:
227/// ```ignore
228/// let ty = Type::new_struct(struct_id);
229/// ```
230///
231/// Use `kind()` for pattern matching:
232/// ```ignore
233/// match ty.kind() {
234///     TypeKind::I32 => { /* ... */ }
235///     TypeKind::Struct(id) => { /* ... */ }
236///     _ => { /* ... */ }
237/// }
238/// ```
239#[derive(Clone, Copy, PartialEq, Eq, Hash)]
240pub struct Type(u32);
241
242impl Default for Type {
243    fn default() -> Self {
244        Type::UNIT
245    }
246}
247
248impl std::fmt::Debug for Type {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        // Provide a readable debug format
251        match self.kind() {
252            TypeKind::I8 => write!(f, "Type::I8"),
253            TypeKind::I16 => write!(f, "Type::I16"),
254            TypeKind::I32 => write!(f, "Type::I32"),
255            TypeKind::I64 => write!(f, "Type::I64"),
256            TypeKind::U8 => write!(f, "Type::U8"),
257            TypeKind::U16 => write!(f, "Type::U16"),
258            TypeKind::U32 => write!(f, "Type::U32"),
259            TypeKind::U64 => write!(f, "Type::U64"),
260            TypeKind::Isize => write!(f, "Type::ISIZE"),
261            TypeKind::Usize => write!(f, "Type::USIZE"),
262            TypeKind::F16 => write!(f, "Type::F16"),
263            TypeKind::F32 => write!(f, "Type::F32"),
264            TypeKind::F64 => write!(f, "Type::F64"),
265            TypeKind::Bool => write!(f, "Type::BOOL"),
266            TypeKind::Unit => write!(f, "Type::UNIT"),
267            TypeKind::Error => write!(f, "Type::ERROR"),
268            TypeKind::Never => write!(f, "Type::NEVER"),
269            TypeKind::ComptimeType => write!(f, "Type::COMPTIME_TYPE"),
270            TypeKind::ComptimeStr => write!(f, "Type::COMPTIME_STR"),
271            TypeKind::ComptimeInt => write!(f, "Type::COMPTIME_INT"),
272            TypeKind::Struct(id) => write!(f, "Type::new_struct(StructId({}))", id.0),
273            TypeKind::Enum(id) => write!(f, "Type::new_enum(EnumId({}))", id.0),
274            TypeKind::Array(id) => write!(f, "Type::new_array(ArrayTypeId({}))", id.0),
275            TypeKind::PtrConst(id) => write!(f, "Type::new_ptr_const(PtrConstTypeId({}))", id.0),
276            TypeKind::PtrMut(id) => write!(f, "Type::new_ptr_mut(PtrMutTypeId({}))", id.0),
277            TypeKind::Module(id) => write!(f, "Type::new_module(ModuleId({}))", id.0),
278        }
279    }
280}
281
282// Composite type tag constants
283// These are used in the low byte of the u32 encoding to identify composite types.
284// The high 24 bits contain the ID (StructId, EnumId, ArrayTypeId, ModuleId, or pointer type IDs).
285const TAG_STRUCT: u32 = 100;
286const TAG_ENUM: u32 = 101;
287const TAG_ARRAY: u32 = 102;
288const TAG_MODULE: u32 = 103;
289const TAG_PTR_CONST: u32 = 104;
290const TAG_PTR_MUT: u32 = 105;
291
292// Primitive type constants
293impl Type {
294    /// 8-bit signed integer
295    pub const I8: Type = Type(0);
296    /// 16-bit signed integer
297    pub const I16: Type = Type(1);
298    /// 32-bit signed integer
299    pub const I32: Type = Type(2);
300    /// 64-bit signed integer
301    pub const I64: Type = Type(3);
302    /// 8-bit unsigned integer
303    pub const U8: Type = Type(4);
304    /// 16-bit unsigned integer
305    pub const U16: Type = Type(5);
306    /// 32-bit unsigned integer
307    pub const U32: Type = Type(6);
308    /// 64-bit unsigned integer
309    pub const U64: Type = Type(7);
310    /// Pointer-sized signed integer
311    pub const ISIZE: Type = Type(8);
312    /// Pointer-sized unsigned integer
313    pub const USIZE: Type = Type(9);
314    /// IEEE 754 binary16 (half-precision float)
315    pub const F16: Type = Type(10);
316    /// IEEE 754 binary32 (single-precision float)
317    pub const F32: Type = Type(11);
318    /// IEEE 754 binary64 (double-precision float)
319    pub const F64: Type = Type(12);
320    /// Boolean
321    pub const BOOL: Type = Type(13);
322    /// The unit type (for functions that don't return a value)
323    pub const UNIT: Type = Type(14);
324    /// An error type (used during type checking to continue after errors)
325    pub const ERROR: Type = Type(15);
326    /// The never type - represents computations that don't return
327    pub const NEVER: Type = Type(16);
328    /// The comptime type - the type of types themselves
329    pub const COMPTIME_TYPE: Type = Type(17);
330    /// The comptime string type - compile-time only string values
331    pub const COMPTIME_STR: Type = Type(18);
332    /// The comptime integer type - compile-time only integer values
333    pub const COMPTIME_INT: Type = Type(19);
334}
335
336// Composite type constructors
337impl Type {
338    /// Create a struct type from a StructId.
339    #[inline]
340    pub const fn new_struct(id: StructId) -> Type {
341        Type(TAG_STRUCT | (id.0 << 8))
342    }
343
344    /// Create an enum type from an EnumId.
345    #[inline]
346    pub const fn new_enum(id: EnumId) -> Type {
347        Type(TAG_ENUM | (id.0 << 8))
348    }
349
350    /// Create an array type from an ArrayTypeId.
351    #[inline]
352    pub const fn new_array(id: ArrayTypeId) -> Type {
353        Type(TAG_ARRAY | (id.0 << 8))
354    }
355
356    /// Create a raw const pointer type from a PtrConstTypeId.
357    #[inline]
358    pub const fn new_ptr_const(id: PtrConstTypeId) -> Type {
359        Type(TAG_PTR_CONST | (id.0 << 8))
360    }
361
362    /// Create a raw mut pointer type from a PtrMutTypeId.
363    #[inline]
364    pub const fn new_ptr_mut(id: PtrMutTypeId) -> Type {
365        Type(TAG_PTR_MUT | (id.0 << 8))
366    }
367
368    /// Create a module type from a ModuleId.
369    #[inline]
370    pub const fn new_module(id: ModuleId) -> Type {
371        Type(TAG_MODULE | (id.0 << 8))
372    }
373}
374
375/// Definition of a struct type.
376#[derive(Debug, Clone)]
377pub struct StructDef {
378    /// Struct name
379    pub name: String,
380    /// Fields in declaration order
381    pub fields: Vec<StructField>,
382    /// Whether this struct is marked with @copy (can be implicitly duplicated)
383    pub is_copy: bool,
384    /// Whether this struct is marked with @handle (can be explicitly duplicated via .handle())
385    pub is_handle: bool,
386    /// Whether this struct is a linear type (must be consumed, cannot be dropped)
387    pub is_linear: bool,
388    /// User-defined destructor function name, if any (e.g., "Data.__drop")
389    pub destructor: Option<String>,
390    /// Whether this is a built-in type (e.g., String) injected by the compiler.
391    ///
392    /// Built-in types behave like regular structs but have runtime implementations
393    /// for their methods rather than generated code.
394    pub is_builtin: bool,
395    /// Whether this struct is public (visible outside its directory)
396    pub is_pub: bool,
397    /// File ID this struct was declared in (for visibility checking)
398    pub file_id: gruel_span::FileId,
399}
400
401/// A field in a struct definition.
402#[derive(Debug, Clone)]
403pub struct StructField {
404    /// Field name
405    pub name: String,
406    /// Field type
407    pub ty: Type,
408}
409
410impl StructDef {
411    /// Find a field by name and return its index and definition.
412    pub fn find_field(&self, name: &str) -> Option<(usize, &StructField)> {
413        self.fields.iter().enumerate().find(|(_, f)| f.name == name)
414    }
415
416    /// Get the number of fields in this struct.
417    pub fn field_count(&self) -> usize {
418        self.fields.len()
419    }
420}
421
422/// A single variant in an enum definition.
423#[derive(Debug, Clone)]
424pub struct EnumVariantDef {
425    /// Variant name
426    pub name: String,
427    /// Field types for data variants. Empty for unit variants.
428    /// E.g., `Some(i32)` has `fields = [Type::I32]`.
429    pub fields: Vec<Type>,
430    /// Field names for struct-style variants. Empty for unit and tuple variants.
431    /// When non-empty, `field_names.len() == fields.len()`.
432    pub field_names: Vec<String>,
433}
434
435impl EnumVariantDef {
436    /// Create a unit variant (no associated data).
437    pub fn unit(name: impl Into<String>) -> Self {
438        Self {
439            name: name.into(),
440            fields: Vec::new(),
441            field_names: Vec::new(),
442        }
443    }
444
445    /// Whether this is a data variant (has associated fields).
446    pub fn has_data(&self) -> bool {
447        !self.fields.is_empty()
448    }
449
450    /// Whether this is a struct-style variant (has named fields).
451    pub fn is_struct_variant(&self) -> bool {
452        !self.field_names.is_empty()
453    }
454
455    /// Find a field by name (for struct variants). Returns the field index.
456    pub fn find_field(&self, name: &str) -> Option<usize> {
457        self.field_names.iter().position(|n| n == name)
458    }
459}
460
461/// Definition of an enum type.
462#[derive(Debug, Clone)]
463pub struct EnumDef {
464    /// Enum name
465    pub name: String,
466    /// Variants in declaration order
467    pub variants: Vec<EnumVariantDef>,
468    /// Whether this enum is public (visible outside its directory)
469    pub is_pub: bool,
470    /// File ID this enum was declared in (for visibility checking)
471    pub file_id: gruel_span::FileId,
472}
473
474impl EnumDef {
475    /// Get the number of variants in this enum.
476    pub fn variant_count(&self) -> usize {
477        self.variants.len()
478    }
479
480    /// Find a variant by name and return its index.
481    pub fn find_variant(&self, name: &str) -> Option<usize> {
482        self.variants.iter().position(|v| v.name == name)
483    }
484
485    /// Whether any variant carries associated data.
486    pub fn has_data_variants(&self) -> bool {
487        self.variants.iter().any(|v| v.has_data())
488    }
489
490    /// Whether all variants are unit variants (no data).
491    pub fn is_unit_only(&self) -> bool {
492        !self.has_data_variants()
493    }
494
495    /// Get the discriminant type for this enum.
496    /// Returns the smallest unsigned integer type that can hold all variant indices.
497    pub fn discriminant_type(&self) -> Type {
498        let count = self.variants.len();
499        if count == 0 {
500            Type::NEVER // Zero-variant enum is uninhabited
501        } else if count <= 256 {
502            Type::U8
503        } else if count <= 65536 {
504            Type::U16
505        } else if count <= 4_294_967_296 {
506            Type::U32
507        } else {
508            Type::U64
509        }
510    }
511}
512
513/// Definition of a module (imported file).
514///
515/// A module contains the public declarations from an imported file.
516/// When code accesses `math.add()`, the module definition is consulted
517/// to find the corresponding function.
518#[derive(Debug, Clone)]
519pub struct ModuleDef {
520    /// The path used in @import (e.g., "math.gruel")
521    pub import_path: String,
522    /// The resolved absolute file path
523    pub file_path: String,
524    /// Public functions in this module: name -> mangled name
525    /// The mangled name includes the module path (e.g., "math::add")
526    pub functions: std::collections::HashMap<String, String>,
527    /// Public structs in this module
528    pub structs: Vec<String>,
529    /// Public enums in this module
530    pub enums: Vec<String>,
531}
532
533impl ModuleDef {
534    /// Create a new empty module definition.
535    pub fn new(import_path: String, file_path: String) -> Self {
536        Self {
537            import_path,
538            file_path,
539            functions: std::collections::HashMap::new(),
540            structs: Vec::new(),
541            enums: Vec::new(),
542        }
543    }
544
545    /// Find a function by name in this module.
546    /// Returns the mangled function name if found.
547    pub fn find_function(&self, name: &str) -> Option<&str> {
548        self.functions.get(name).map(|s| s.as_str())
549    }
550}
551
552impl Type {
553    /// Get the kind of this type for pattern matching.
554    ///
555    /// This method decodes the u32 representation back to a `TypeKind` for pattern matching.
556    /// Primitive types (0-12) decode directly; composite types decode the tag and ID.
557    ///
558    /// # Panics
559    ///
560    /// Panics if the Type has an invalid encoding. This should never happen with Types
561    /// created through the normal API. If you're working with potentially corrupt data,
562    /// use [`try_kind`](Self::try_kind) instead.
563    ///
564    /// # Example
565    ///
566    /// ```ignore
567    /// match ty.kind() {
568    ///     TypeKind::I32 | TypeKind::I64 => { /* handle integers */ }
569    ///     TypeKind::Struct(id) => { /* handle struct */ }
570    ///     _ => { /* other types */ }
571    /// }
572    /// ```
573    #[inline]
574    pub fn kind(&self) -> TypeKind {
575        self.try_kind().unwrap_or_else(|| {
576            panic!(
577                "invalid Type encoding: raw value {:#010x} (tag={}, id={}). \
578                 This indicates data corruption or a bug in Type construction. \
579                 Valid tags are 0-18 (primitives) or 100-105 (composites).",
580                self.0,
581                self.0 & 0xFF,
582                self.0 >> 8
583            )
584        })
585    }
586
587    /// Try to get the kind of this type, returning `None` if the encoding is invalid.
588    ///
589    /// This is the non-panicking version of [`kind`](Self::kind). Use this when working
590    /// with potentially corrupt data or for defensive programming.
591    ///
592    /// # Example
593    ///
594    /// ```ignore
595    /// if let Some(kind) = ty.try_kind() {
596    ///     match kind {
597    ///         TypeKind::I32 => { /* ... */ }
598    ///         _ => { /* ... */ }
599    ///     }
600    /// } else {
601    ///     eprintln!("corrupt type data");
602    /// }
603    /// ```
604    #[inline]
605    pub fn try_kind(&self) -> Option<TypeKind> {
606        let tag = self.0 & 0xFF;
607        match tag {
608            0 => Some(TypeKind::I8),
609            1 => Some(TypeKind::I16),
610            2 => Some(TypeKind::I32),
611            3 => Some(TypeKind::I64),
612            4 => Some(TypeKind::U8),
613            5 => Some(TypeKind::U16),
614            6 => Some(TypeKind::U32),
615            7 => Some(TypeKind::U64),
616            8 => Some(TypeKind::Isize),
617            9 => Some(TypeKind::Usize),
618            10 => Some(TypeKind::F16),
619            11 => Some(TypeKind::F32),
620            12 => Some(TypeKind::F64),
621            13 => Some(TypeKind::Bool),
622            14 => Some(TypeKind::Unit),
623            15 => Some(TypeKind::Error),
624            16 => Some(TypeKind::Never),
625            17 => Some(TypeKind::ComptimeType),
626            18 => Some(TypeKind::ComptimeStr),
627            19 => Some(TypeKind::ComptimeInt),
628            TAG_STRUCT => Some(TypeKind::Struct(StructId(self.0 >> 8))),
629            TAG_ENUM => Some(TypeKind::Enum(EnumId(self.0 >> 8))),
630            TAG_ARRAY => Some(TypeKind::Array(ArrayTypeId(self.0 >> 8))),
631            TAG_PTR_CONST => Some(TypeKind::PtrConst(PtrConstTypeId(self.0 >> 8))),
632            TAG_PTR_MUT => Some(TypeKind::PtrMut(PtrMutTypeId(self.0 >> 8))),
633            TAG_MODULE => Some(TypeKind::Module(ModuleId(self.0 >> 8))),
634            _ => None,
635        }
636    }
637
638    /// Get a human-readable name for this type.
639    /// Note: For struct and array types, this returns a placeholder.
640    /// Use `type_name_with_structs` for proper struct/array names.
641    pub fn name(&self) -> &'static str {
642        match self.kind() {
643            TypeKind::I8 => "i8",
644            TypeKind::I16 => "i16",
645            TypeKind::I32 => "i32",
646            TypeKind::I64 => "i64",
647            TypeKind::U8 => "u8",
648            TypeKind::U16 => "u16",
649            TypeKind::U32 => "u32",
650            TypeKind::U64 => "u64",
651            TypeKind::Isize => "isize",
652            TypeKind::Usize => "usize",
653            TypeKind::F16 => "f16",
654            TypeKind::F32 => "f32",
655            TypeKind::F64 => "f64",
656            TypeKind::Bool => "bool",
657            TypeKind::Unit => "()",
658            TypeKind::Struct(_) => "<struct>",
659            TypeKind::Enum(_) => "<enum>",
660            TypeKind::Array(_) => "<array>",
661            TypeKind::PtrConst(_) => "<ptr const>",
662            TypeKind::PtrMut(_) => "<ptr mut>",
663            TypeKind::Module(_) => "<module>",
664            TypeKind::Error => "<error>",
665            TypeKind::Never => "!",
666            TypeKind::ComptimeType => "type",
667            TypeKind::ComptimeStr => "comptime_str",
668            TypeKind::ComptimeInt => "comptime_int",
669        }
670    }
671
672    /// Get a human-readable type name, safely handling anonymous structs and missing definitions.
673    ///
674    /// Unlike `name()`, this method can access the type pool to get actual struct/enum names
675    /// instead of returning generic placeholders like `"<struct>"`.
676    ///
677    /// This is primarily used for error messages where we want to show meaningful type names
678    /// even if the type pool lookup fails (returns safe fallback in that case).
679    ///
680    /// # Safety
681    ///
682    /// This method is safe even if the struct/enum ID is invalid or the pool is None.
683    /// It will return a fallback string like `"<struct#123>"` in those cases.
684    pub fn safe_name_with_pool(&self, pool: Option<&crate::intern_pool::TypeInternPool>) -> String {
685        match self.try_kind() {
686            Some(TypeKind::Struct(struct_id)) => {
687                if let Some(pool) = pool {
688                    let def = pool.struct_def(struct_id);
689                    return def.name.clone();
690                }
691                format!("<struct#{}>", struct_id.0)
692            }
693            Some(TypeKind::Enum(enum_id)) => {
694                if let Some(pool) = pool {
695                    let def = pool.enum_def(enum_id);
696                    return def.name.clone();
697                }
698                format!("<enum#{}>", enum_id.0)
699            }
700            Some(_kind) => self.name().to_string(),
701            None => format!("<invalid type encoding: {:#x}>", self.0),
702        }
703    }
704
705    /// Check if this type is an integer type.
706    /// Optimized: checks tag range directly (0-9 are integer types: i8..u64, isize/usize).
707    #[inline]
708    pub fn is_integer(&self) -> bool {
709        self.0 <= 9
710    }
711
712    /// Check if this is an error type.
713    #[inline]
714    pub fn is_error(&self) -> bool {
715        *self == Type::ERROR
716    }
717
718    /// Check if this is the never type.
719    #[inline]
720    pub fn is_never(&self) -> bool {
721        *self == Type::NEVER
722    }
723
724    /// Check if this is the comptime type (the type of types).
725    #[inline]
726    pub fn is_comptime_type(&self) -> bool {
727        *self == Type::COMPTIME_TYPE
728    }
729
730    /// Check if this is the comptime string type.
731    #[inline]
732    pub fn is_comptime_str(&self) -> bool {
733        *self == Type::COMPTIME_STR
734    }
735
736    /// Check if this is the comptime integer type.
737    #[inline]
738    pub fn is_comptime_int(&self) -> bool {
739        *self == Type::COMPTIME_INT
740    }
741
742    /// Check if this is a struct type.
743    #[inline]
744    pub fn is_struct(&self) -> bool {
745        (self.0 & 0xFF) == TAG_STRUCT
746    }
747
748    /// Get the struct ID if this is a struct type.
749    #[inline]
750    pub fn as_struct(&self) -> Option<StructId> {
751        if self.is_struct() {
752            Some(StructId(self.0 >> 8))
753        } else {
754            None
755        }
756    }
757
758    /// Check if this is an array type.
759    #[inline]
760    pub fn is_array(&self) -> bool {
761        (self.0 & 0xFF) == TAG_ARRAY
762    }
763
764    /// Get the array type ID if this is an array type.
765    #[inline]
766    pub fn as_array(&self) -> Option<ArrayTypeId> {
767        if self.is_array() {
768            Some(ArrayTypeId(self.0 >> 8))
769        } else {
770            None
771        }
772    }
773
774    /// Check if this is an enum type.
775    #[inline]
776    pub fn is_enum(&self) -> bool {
777        (self.0 & 0xFF) == TAG_ENUM
778    }
779
780    /// Get the enum ID if this is an enum type.
781    #[inline]
782    pub fn as_enum(&self) -> Option<EnumId> {
783        if self.is_enum() {
784            Some(EnumId(self.0 >> 8))
785        } else {
786            None
787        }
788    }
789
790    /// Check if this is a module type.
791    #[inline]
792    pub fn is_module(&self) -> bool {
793        (self.0 & 0xFF) == TAG_MODULE
794    }
795
796    /// Get the module ID if this is a module type.
797    #[inline]
798    pub fn as_module(&self) -> Option<ModuleId> {
799        if self.is_module() {
800            Some(ModuleId(self.0 >> 8))
801        } else {
802            None
803        }
804    }
805
806    /// Check if this is a raw const pointer type.
807    #[inline]
808    pub fn is_ptr_const(&self) -> bool {
809        (self.0 & 0xFF) == TAG_PTR_CONST
810    }
811
812    /// Get the pointer type ID if this is a ptr const type.
813    #[inline]
814    pub fn as_ptr_const(&self) -> Option<PtrConstTypeId> {
815        if self.is_ptr_const() {
816            Some(PtrConstTypeId(self.0 >> 8))
817        } else {
818            None
819        }
820    }
821
822    /// Check if this is a raw mut pointer type.
823    #[inline]
824    pub fn is_ptr_mut(&self) -> bool {
825        (self.0 & 0xFF) == TAG_PTR_MUT
826    }
827
828    /// Get the pointer type ID if this is a ptr mut type.
829    #[inline]
830    pub fn as_ptr_mut(&self) -> Option<PtrMutTypeId> {
831        if self.is_ptr_mut() {
832            Some(PtrMutTypeId(self.0 >> 8))
833        } else {
834            None
835        }
836    }
837
838    /// Check if this is any raw pointer type (ptr const or ptr mut).
839    #[inline]
840    pub fn is_ptr(&self) -> bool {
841        let tag = self.0 & 0xFF;
842        tag == TAG_PTR_CONST || tag == TAG_PTR_MUT
843    }
844
845    /// Check if this is a signed integer type.
846    /// Signed integers: I8=0, I16=1, I32=2, I64=3, Isize=8.
847    #[inline]
848    pub fn is_signed(&self) -> bool {
849        self.0 <= 3 || self.0 == 8
850    }
851
852    /// Check if this is a floating-point type.
853    /// Float types: F16=10, F32=11, F64=12.
854    #[inline]
855    pub fn is_float(&self) -> bool {
856        self.0 >= 10 && self.0 <= 12
857    }
858
859    /// Check if this is a numeric type (integer or float).
860    #[inline]
861    pub fn is_numeric(&self) -> bool {
862        self.0 <= 12
863    }
864
865    /// Check if this is a Copy type (can be implicitly duplicated).
866    ///
867    /// Copy types are:
868    /// - All integer types (i8-i64, u8-u64)
869    /// - Boolean
870    /// - Unit
871    /// - Enum types
872    /// - Never type and Error type (for convenience in error recovery)
873    ///
874    /// Non-Copy types (move types) are:
875    /// - Struct types (unless marked @copy, checked via StructDef.is_copy)
876    /// - Array types (unless element type is Copy, checked via Sema.is_type_copy)
877    ///
878    /// Note: This method can't check struct's is_copy attribute or array element
879    /// types since it doesn't have access to StructDefs or array type information.
880    /// Use Sema.is_type_copy() for full checking.
881    pub fn is_copy(&self) -> bool {
882        let tag = self.0 & 0xFF;
883        match tag {
884            // Primitive Copy types (I8..Unit = 0..14, includes integers/floats/bool/unit)
885            0..=14 => true,
886            // Error, Never, ComptimeType, ComptimeStr, ComptimeInt are Copy for convenience
887            15..=19 => true,
888            // Enum types are Copy (they're small discriminant values)
889            TAG_ENUM => true,
890            // Module types are Copy (they're just compile-time namespace references)
891            TAG_MODULE => true,
892            // Struct types are move types by default
893            TAG_STRUCT => false,
894            // Arrays may be Copy if element type is Copy (need TypeInternPool to check)
895            TAG_ARRAY => false,
896            _ => false,
897        }
898    }
899
900    /// Check if this type is Copy, with access to TypeInternPool for struct checking.
901    ///
902    /// This is used during anonymous struct creation to determine if the new struct
903    /// should be Copy based on its field types.
904    pub fn is_copy_in_pool(&self, type_pool: &crate::intern_pool::TypeInternPool) -> bool {
905        if let Some(struct_id) = self.as_struct() {
906            type_pool.struct_def(struct_id).is_copy
907        } else {
908            self.is_copy()
909        }
910    }
911
912    /// Check if this is a 64-bit type (uses 64-bit operations).
913    /// Optimized: checks for I64 (3) or U64 (7).
914    #[inline]
915    pub fn is_64_bit(&self) -> bool {
916        self.0 == 3 || self.0 == 7
917    }
918
919    /// Check if this is a pointer-sized type (isize or usize).
920    /// Checks for Isize (8) or Usize (9).
921    #[inline]
922    pub fn is_pointer_sized(&self) -> bool {
923        self.0 == 8 || self.0 == 9
924    }
925
926    /// Check if this type can coerce to the target type.
927    ///
928    /// Coercion rules:
929    /// - Never can coerce to any type (it represents divergent control flow)
930    /// - Error can coerce to any type (for error recovery during type checking)
931    /// - Otherwise, types must be equal
932    pub fn can_coerce_to(&self, target: &Type) -> bool {
933        self.is_never() || self.is_error() || self == target
934    }
935
936    /// Check if this is an unsigned integer type.
937    /// Unsigned integers: U8=4, U16=5, U32=6, U64=7, Usize=9.
938    #[inline]
939    #[must_use]
940    pub fn is_unsigned(&self) -> bool {
941        (self.0 >= 4 && self.0 <= 7) || self.0 == 9
942    }
943
944    /// Check if a u64 value fits within the range of this integer type.
945    ///
946    /// For signed types, only the positive range is checked (0 to max positive).
947    /// Negation is handled separately to allow values like `-128` for i8.
948    ///
949    /// Returns `true` if the value fits, `false` otherwise.
950    /// For non-integer types, returns `false`.
951    #[must_use]
952    pub fn literal_fits(&self, value: u64) -> bool {
953        match self.0 {
954            0 => value <= i8::MAX as u64,  // I8
955            1 => value <= i16::MAX as u64, // I16
956            2 => value <= i32::MAX as u64, // I32
957            3 => value <= i64::MAX as u64, // I64
958            4 => value <= u8::MAX as u64,  // U8
959            5 => value <= u16::MAX as u64, // U16
960            6 => value <= u32::MAX as u64, // U32
961            7 => true,                     // U64 - Any u64 value fits
962            8 => value <= i64::MAX as u64, // Isize - 64-bit signed on current targets
963            9 => true,                     // Usize - 64-bit unsigned on current targets
964            _ => false,
965        }
966    }
967
968    /// Check if a u64 value can be negated to fit within the range of this signed integer type.
969    ///
970    /// This is used to allow literals like `2147483648` when negated to `-2147483648` (i32::MIN).
971    /// Returns `true` if the negated value fits, `false` otherwise.
972    #[must_use]
973    pub fn negated_literal_fits(&self, value: u64) -> bool {
974        match self.0 {
975            0 => value <= (i8::MIN as i64).unsigned_abs(),  // I8
976            1 => value <= (i16::MIN as i64).unsigned_abs(), // I16
977            2 => value <= (i32::MIN as i64).unsigned_abs(), // I32
978            3 => value <= (i64::MIN).unsigned_abs(),        // I64
979            8 => value <= (i64::MIN).unsigned_abs(),        // Isize - 64-bit on current targets
980            _ => false,
981        }
982    }
983
984    /// Encode this type as a u32 for storage in extra arrays.
985    ///
986    /// Since Type is now a u32 newtype, this simply returns the inner value.
987    #[inline]
988    pub fn as_u32(&self) -> u32 {
989        self.0
990    }
991
992    /// Decode a type from a u32 value.
993    ///
994    /// Since Type is now a u32 newtype, this simply wraps the value.
995    /// Note: This does not validate the encoding - use with values from `as_u32()`.
996    ///
997    /// # Safety (not unsafe, but correctness)
998    ///
999    /// This method trusts that the input is a valid encoding. For untrusted data,
1000    /// use [`try_from_u32`](Self::try_from_u32) which validates the encoding.
1001    #[inline]
1002    pub fn from_u32(v: u32) -> Self {
1003        Type(v)
1004    }
1005
1006    /// Try to decode a type from a u32 value, returning `None` if invalid.
1007    ///
1008    /// This validates that the encoding represents a valid type before returning.
1009    /// Use this when reading potentially corrupt data (e.g., deserialization,
1010    /// memory-mapped files, or debugging).
1011    ///
1012    /// # Example
1013    ///
1014    /// ```ignore
1015    /// if let Some(ty) = Type::try_from_u32(encoded) {
1016    ///     // Safe to use ty.kind()
1017    /// } else {
1018    ///     // Handle invalid encoding
1019    /// }
1020    /// ```
1021    #[inline]
1022    pub fn try_from_u32(v: u32) -> Option<Self> {
1023        if Self::is_valid_encoding(v) {
1024            Some(Type(v))
1025        } else {
1026            None
1027        }
1028    }
1029
1030    /// Check if a u32 value is a valid Type encoding.
1031    ///
1032    /// Returns `true` if the value represents a valid primitive or composite type.
1033    #[inline]
1034    pub fn is_valid_encoding(v: u32) -> bool {
1035        let tag = v & 0xFF;
1036        match tag {
1037            // Primitive types: I8=0 through ComptimeInt=19
1038            0..=19 => true,
1039            // Composite types with valid tags
1040            TAG_STRUCT | TAG_ENUM | TAG_ARRAY | TAG_PTR_CONST | TAG_PTR_MUT | TAG_MODULE => true,
1041            // Everything else is invalid
1042            _ => false,
1043        }
1044    }
1045
1046    /// Check if this Type has a valid encoding.
1047    ///
1048    /// This is useful for debugging and assertions.
1049    #[inline]
1050    pub fn is_valid(&self) -> bool {
1051        Self::is_valid_encoding(self.0)
1052    }
1053}
1054
1055impl std::fmt::Display for Type {
1056    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1057        write!(f, "{}", self.name())
1058    }
1059}
1060
1061/// Pointer mutability - whether the pointed-to data can be modified.
1062#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1063pub enum PtrMutability {
1064    /// Immutable pointer (`ptr const T`)
1065    Const,
1066    /// Mutable pointer (`ptr mut T`)
1067    Mut,
1068}
1069
1070/// Parse pointer type syntax "ptr const T" or "ptr mut T" and return (pointee_type_str, mutability).
1071///
1072/// Returns `None` if the string doesn't match pointer syntax.
1073pub fn parse_pointer_type_syntax(type_name: &str) -> Option<(String, PtrMutability)> {
1074    let type_name = type_name.trim();
1075    if let Some(rest) = type_name.strip_prefix("ptr const ") {
1076        Some((rest.trim().to_string(), PtrMutability::Const))
1077    } else {
1078        type_name
1079            .strip_prefix("ptr mut ")
1080            .map(|rest| (rest.trim().to_string(), PtrMutability::Mut))
1081    }
1082}
1083
1084/// Parse array type syntax "[T; N]" and return (element_type_str, length).
1085///
1086/// This handles nested arrays correctly by tracking bracket depth.
1087/// For example, `[[i32; 3]; 4]` returns `("[i32; 3]", 4)`.
1088pub fn parse_array_type_syntax(type_name: &str) -> Option<(String, u64)> {
1089    let type_name = type_name.trim();
1090    if !type_name.starts_with('[') || !type_name.ends_with(']') {
1091        return None;
1092    }
1093
1094    // Remove the outer brackets
1095    let inner = &type_name[1..type_name.len() - 1];
1096
1097    // Find the semicolon separator - need to handle nested arrays
1098    // We look for the last `;` that's at nesting level 0
1099    let mut bracket_depth = 0;
1100    let mut semi_pos = None;
1101    for (i, ch) in inner.char_indices() {
1102        match ch {
1103            '[' => bracket_depth += 1,
1104            ']' => bracket_depth -= 1,
1105            ';' if bracket_depth == 0 => semi_pos = Some(i),
1106            _ => {}
1107        }
1108    }
1109
1110    let semi_pos = semi_pos?;
1111    let element_type = inner[..semi_pos].trim().to_string();
1112    let length_str = inner[semi_pos + 1..].trim();
1113    let length: u64 = length_str.parse().ok()?;
1114
1115    Some((element_type, length))
1116}
1117
1118#[cfg(test)]
1119mod tests {
1120    use super::*;
1121
1122    // ========== Type ID tests ==========
1123
1124    #[test]
1125    fn test_struct_id_equality() {
1126        let id1 = StructId(0);
1127        let id2 = StructId(0);
1128        let id3 = StructId(1);
1129        assert_eq!(id1, id2);
1130        assert_ne!(id1, id3);
1131    }
1132
1133    #[test]
1134    fn test_enum_id_equality() {
1135        let id1 = EnumId(0);
1136        let id2 = EnumId(0);
1137        let id3 = EnumId(1);
1138        assert_eq!(id1, id2);
1139        assert_ne!(id1, id3);
1140    }
1141
1142    #[test]
1143    fn test_array_type_id_equality() {
1144        let id1 = ArrayTypeId(0);
1145        let id2 = ArrayTypeId(0);
1146        let id3 = ArrayTypeId(1);
1147        assert_eq!(id1, id2);
1148        assert_ne!(id1, id3);
1149    }
1150
1151    // ========== Type::name() tests ==========
1152
1153    #[test]
1154    fn test_type_name_integers() {
1155        assert_eq!(Type::I8.name(), "i8");
1156        assert_eq!(Type::I16.name(), "i16");
1157        assert_eq!(Type::I32.name(), "i32");
1158        assert_eq!(Type::I64.name(), "i64");
1159        assert_eq!(Type::U8.name(), "u8");
1160        assert_eq!(Type::U16.name(), "u16");
1161        assert_eq!(Type::U32.name(), "u32");
1162        assert_eq!(Type::U64.name(), "u64");
1163    }
1164
1165    #[test]
1166    fn test_type_name_other() {
1167        assert_eq!(Type::BOOL.name(), "bool");
1168        assert_eq!(Type::UNIT.name(), "()");
1169        assert_eq!(Type::ERROR.name(), "<error>");
1170        assert_eq!(Type::NEVER.name(), "!");
1171    }
1172
1173    #[test]
1174    fn test_type_name_composite() {
1175        assert_eq!(Type::new_struct(StructId(0)).name(), "<struct>");
1176        assert_eq!(Type::new_enum(EnumId(0)).name(), "<enum>");
1177        assert_eq!(Type::new_array(ArrayTypeId(0)).name(), "<array>");
1178    }
1179
1180    // ========== Type::is_integer() tests ==========
1181
1182    #[test]
1183    fn test_is_integer_signed() {
1184        assert!(Type::I8.is_integer());
1185        assert!(Type::I16.is_integer());
1186        assert!(Type::I32.is_integer());
1187        assert!(Type::I64.is_integer());
1188    }
1189
1190    #[test]
1191    fn test_is_integer_unsigned() {
1192        assert!(Type::U8.is_integer());
1193        assert!(Type::U16.is_integer());
1194        assert!(Type::U32.is_integer());
1195        assert!(Type::U64.is_integer());
1196    }
1197
1198    #[test]
1199    fn test_is_integer_non_integers() {
1200        assert!(!Type::BOOL.is_integer());
1201        assert!(!Type::UNIT.is_integer());
1202        assert!(!Type::new_struct(StructId(0)).is_integer());
1203        assert!(!Type::new_enum(EnumId(0)).is_integer());
1204        assert!(!Type::new_array(ArrayTypeId(0)).is_integer());
1205        assert!(!Type::ERROR.is_integer());
1206        assert!(!Type::NEVER.is_integer());
1207    }
1208
1209    // ========== Type::is_signed() tests ==========
1210
1211    #[test]
1212    fn test_is_signed() {
1213        assert!(Type::I8.is_signed());
1214        assert!(Type::I16.is_signed());
1215        assert!(Type::I32.is_signed());
1216        assert!(Type::I64.is_signed());
1217
1218        assert!(!Type::U8.is_signed());
1219        assert!(!Type::U16.is_signed());
1220        assert!(!Type::U32.is_signed());
1221        assert!(!Type::U64.is_signed());
1222        assert!(!Type::BOOL.is_signed());
1223    }
1224
1225    // ========== Type::is_unsigned() tests ==========
1226
1227    #[test]
1228    fn test_is_unsigned() {
1229        assert!(Type::U8.is_unsigned());
1230        assert!(Type::U16.is_unsigned());
1231        assert!(Type::U32.is_unsigned());
1232        assert!(Type::U64.is_unsigned());
1233
1234        assert!(!Type::I8.is_unsigned());
1235        assert!(!Type::I16.is_unsigned());
1236        assert!(!Type::I32.is_unsigned());
1237        assert!(!Type::I64.is_unsigned());
1238        assert!(!Type::BOOL.is_unsigned());
1239    }
1240
1241    // ========== Type::is_64_bit() tests ==========
1242
1243    #[test]
1244    fn test_is_64_bit() {
1245        assert!(Type::I64.is_64_bit());
1246        assert!(Type::U64.is_64_bit());
1247
1248        assert!(!Type::I8.is_64_bit());
1249        assert!(!Type::I16.is_64_bit());
1250        assert!(!Type::I32.is_64_bit());
1251        assert!(!Type::U8.is_64_bit());
1252        assert!(!Type::U16.is_64_bit());
1253        assert!(!Type::U32.is_64_bit());
1254        assert!(!Type::BOOL.is_64_bit());
1255    }
1256
1257    // ========== Type::is_error() tests ==========
1258
1259    #[test]
1260    fn test_is_error() {
1261        assert!(Type::ERROR.is_error());
1262        assert!(!Type::I32.is_error());
1263        assert!(!Type::NEVER.is_error());
1264    }
1265
1266    // ========== Type::is_never() tests ==========
1267
1268    #[test]
1269    fn test_is_never() {
1270        assert!(Type::NEVER.is_never());
1271        assert!(!Type::I32.is_never());
1272        assert!(!Type::ERROR.is_never());
1273    }
1274
1275    // ========== Type::is_struct() and as_struct() tests ==========
1276
1277    #[test]
1278    fn test_is_struct() {
1279        assert!(Type::new_struct(StructId(0)).is_struct());
1280        assert!(Type::new_struct(StructId(42)).is_struct());
1281        assert!(!Type::I32.is_struct());
1282        assert!(!Type::new_enum(EnumId(0)).is_struct());
1283    }
1284
1285    #[test]
1286    fn test_as_struct() {
1287        assert_eq!(Type::new_struct(StructId(5)).as_struct(), Some(StructId(5)));
1288        assert_eq!(Type::I32.as_struct(), None);
1289        assert_eq!(Type::new_enum(EnumId(0)).as_struct(), None);
1290    }
1291
1292    // ========== Type::is_enum() and as_enum() tests ==========
1293
1294    #[test]
1295    fn test_is_enum() {
1296        assert!(Type::new_enum(EnumId(0)).is_enum());
1297        assert!(Type::new_enum(EnumId(42)).is_enum());
1298        assert!(!Type::I32.is_enum());
1299        assert!(!Type::new_struct(StructId(0)).is_enum());
1300    }
1301
1302    #[test]
1303    fn test_as_enum() {
1304        assert_eq!(Type::new_enum(EnumId(5)).as_enum(), Some(EnumId(5)));
1305        assert_eq!(Type::I32.as_enum(), None);
1306        assert_eq!(Type::new_struct(StructId(0)).as_enum(), None);
1307    }
1308
1309    // ========== Type::is_array() and as_array() tests ==========
1310
1311    #[test]
1312    fn test_is_array() {
1313        assert!(Type::new_array(ArrayTypeId(0)).is_array());
1314        assert!(Type::new_array(ArrayTypeId(42)).is_array());
1315        assert!(!Type::I32.is_array());
1316        assert!(!Type::new_struct(StructId(0)).is_array());
1317    }
1318
1319    #[test]
1320    fn test_as_array() {
1321        assert_eq!(
1322            Type::new_array(ArrayTypeId(5)).as_array(),
1323            Some(ArrayTypeId(5))
1324        );
1325        assert_eq!(Type::I32.as_array(), None);
1326        assert_eq!(Type::new_struct(StructId(0)).as_array(), None);
1327    }
1328
1329    // ========== Type::is_copy() tests ==========
1330
1331    #[test]
1332    fn test_is_copy_primitives() {
1333        // All integer types are Copy
1334        assert!(Type::I8.is_copy());
1335        assert!(Type::I16.is_copy());
1336        assert!(Type::I32.is_copy());
1337        assert!(Type::I64.is_copy());
1338        assert!(Type::U8.is_copy());
1339        assert!(Type::U16.is_copy());
1340        assert!(Type::U32.is_copy());
1341        assert!(Type::U64.is_copy());
1342
1343        // Bool and Unit are Copy
1344        assert!(Type::BOOL.is_copy());
1345        assert!(Type::UNIT.is_copy());
1346    }
1347
1348    #[test]
1349    fn test_is_copy_special() {
1350        // Enum types are Copy
1351        assert!(Type::new_enum(EnumId(0)).is_copy());
1352
1353        // Never and Error are Copy for convenience
1354        assert!(Type::NEVER.is_copy());
1355        assert!(Type::ERROR.is_copy());
1356    }
1357
1358    #[test]
1359    fn test_is_copy_move_types() {
1360        // Struct and Array are move types (String is a builtin struct now)
1361        assert!(!Type::new_struct(StructId(0)).is_copy());
1362        assert!(!Type::new_array(ArrayTypeId(0)).is_copy());
1363    }
1364
1365    // ========== Type::can_coerce_to() tests ==========
1366
1367    #[test]
1368    fn test_can_coerce_to_same_type() {
1369        assert!(Type::I32.can_coerce_to(&Type::I32));
1370        assert!(Type::BOOL.can_coerce_to(&Type::BOOL));
1371        assert!(Type::new_struct(StructId(0)).can_coerce_to(&Type::new_struct(StructId(0))));
1372    }
1373
1374    #[test]
1375    fn test_can_coerce_to_never_coerces_to_anything() {
1376        assert!(Type::NEVER.can_coerce_to(&Type::I32));
1377        assert!(Type::NEVER.can_coerce_to(&Type::BOOL));
1378        assert!(Type::NEVER.can_coerce_to(&Type::new_struct(StructId(0))));
1379    }
1380
1381    #[test]
1382    fn test_can_coerce_to_error_coerces_to_anything() {
1383        assert!(Type::ERROR.can_coerce_to(&Type::I32));
1384        assert!(Type::ERROR.can_coerce_to(&Type::BOOL));
1385        assert!(Type::ERROR.can_coerce_to(&Type::new_struct(StructId(0))));
1386    }
1387
1388    #[test]
1389    fn test_can_coerce_to_different_types_fail() {
1390        assert!(!Type::I32.can_coerce_to(&Type::BOOL));
1391        assert!(!Type::BOOL.can_coerce_to(&Type::I32));
1392        assert!(!Type::I32.can_coerce_to(&Type::I64));
1393        assert!(!Type::new_struct(StructId(0)).can_coerce_to(&Type::I32));
1394    }
1395
1396    // ========== Type::literal_fits() tests ==========
1397
1398    #[test]
1399    fn test_literal_fits_i8() {
1400        assert!(Type::I8.literal_fits(0));
1401        assert!(Type::I8.literal_fits(127)); // i8::MAX
1402        assert!(!Type::I8.literal_fits(128));
1403    }
1404
1405    #[test]
1406    fn test_literal_fits_i16() {
1407        assert!(Type::I16.literal_fits(0));
1408        assert!(Type::I16.literal_fits(32767)); // i16::MAX
1409        assert!(!Type::I16.literal_fits(32768));
1410    }
1411
1412    #[test]
1413    fn test_literal_fits_i32() {
1414        assert!(Type::I32.literal_fits(0));
1415        assert!(Type::I32.literal_fits(2147483647)); // i32::MAX
1416        assert!(!Type::I32.literal_fits(2147483648));
1417    }
1418
1419    #[test]
1420    fn test_literal_fits_i64() {
1421        assert!(Type::I64.literal_fits(0));
1422        assert!(Type::I64.literal_fits(9223372036854775807)); // i64::MAX
1423        assert!(!Type::I64.literal_fits(9223372036854775808));
1424    }
1425
1426    #[test]
1427    fn test_literal_fits_u8() {
1428        assert!(Type::U8.literal_fits(0));
1429        assert!(Type::U8.literal_fits(255)); // u8::MAX
1430        assert!(!Type::U8.literal_fits(256));
1431    }
1432
1433    #[test]
1434    fn test_literal_fits_u16() {
1435        assert!(Type::U16.literal_fits(0));
1436        assert!(Type::U16.literal_fits(65535)); // u16::MAX
1437        assert!(!Type::U16.literal_fits(65536));
1438    }
1439
1440    #[test]
1441    fn test_literal_fits_u32() {
1442        assert!(Type::U32.literal_fits(0));
1443        assert!(Type::U32.literal_fits(4294967295)); // u32::MAX
1444        assert!(!Type::U32.literal_fits(4294967296));
1445    }
1446
1447    #[test]
1448    fn test_literal_fits_u64() {
1449        assert!(Type::U64.literal_fits(0));
1450        assert!(Type::U64.literal_fits(u64::MAX)); // Any u64 fits
1451    }
1452
1453    #[test]
1454    fn test_literal_fits_non_integer() {
1455        assert!(!Type::BOOL.literal_fits(0));
1456        assert!(!Type::new_struct(StructId(0)).literal_fits(0));
1457        assert!(!Type::UNIT.literal_fits(0));
1458    }
1459
1460    // ========== Type::negated_literal_fits() tests ==========
1461
1462    #[test]
1463    fn test_negated_literal_fits_i8() {
1464        assert!(Type::I8.negated_literal_fits(128)); // -128 = i8::MIN
1465        assert!(!Type::I8.negated_literal_fits(129));
1466    }
1467
1468    #[test]
1469    fn test_negated_literal_fits_i16() {
1470        assert!(Type::I16.negated_literal_fits(32768)); // -32768 = i16::MIN
1471        assert!(!Type::I16.negated_literal_fits(32769));
1472    }
1473
1474    #[test]
1475    fn test_negated_literal_fits_i32() {
1476        assert!(Type::I32.negated_literal_fits(2147483648)); // -2147483648 = i32::MIN
1477        assert!(!Type::I32.negated_literal_fits(2147483649));
1478    }
1479
1480    #[test]
1481    fn test_negated_literal_fits_i64() {
1482        assert!(Type::I64.negated_literal_fits(9223372036854775808)); // i64::MIN abs
1483        assert!(!Type::I64.negated_literal_fits(9223372036854775809));
1484    }
1485
1486    #[test]
1487    fn test_negated_literal_fits_unsigned() {
1488        // Unsigned types don't support negated literals
1489        assert!(!Type::U8.negated_literal_fits(1));
1490        assert!(!Type::U16.negated_literal_fits(1));
1491        assert!(!Type::U32.negated_literal_fits(1));
1492        assert!(!Type::U64.negated_literal_fits(1));
1493    }
1494
1495    #[test]
1496    fn test_negated_literal_fits_non_integer() {
1497        assert!(!Type::BOOL.negated_literal_fits(1));
1498        assert!(!Type::new_struct(StructId(0)).negated_literal_fits(1));
1499    }
1500
1501    // ========== Type Display tests ==========
1502
1503    #[test]
1504    fn test_type_display() {
1505        assert_eq!(format!("{}", Type::I32), "i32");
1506        assert_eq!(format!("{}", Type::BOOL), "bool");
1507        assert_eq!(format!("{}", Type::NEVER), "!");
1508    }
1509
1510    // ========== Type Default tests ==========
1511
1512    #[test]
1513    fn test_type_default() {
1514        assert_eq!(Type::default(), Type::UNIT);
1515    }
1516
1517    // ========== StructDef tests ==========
1518
1519    #[test]
1520    fn test_struct_def_find_field() {
1521        let def = StructDef {
1522            name: "Point".to_string(),
1523            fields: vec![
1524                StructField {
1525                    name: "x".to_string(),
1526                    ty: Type::I32,
1527                },
1528                StructField {
1529                    name: "y".to_string(),
1530                    ty: Type::I32,
1531                },
1532            ],
1533            is_copy: false,
1534            is_handle: false,
1535            is_linear: false,
1536            destructor: None,
1537            is_builtin: false,
1538            is_pub: false,
1539            file_id: gruel_span::FileId::DEFAULT,
1540        };
1541
1542        let (idx, field) = def.find_field("x").unwrap();
1543        assert_eq!(idx, 0);
1544        assert_eq!(field.name, "x");
1545        assert_eq!(field.ty, Type::I32);
1546
1547        let (idx, field) = def.find_field("y").unwrap();
1548        assert_eq!(idx, 1);
1549        assert_eq!(field.name, "y");
1550
1551        assert!(def.find_field("z").is_none());
1552    }
1553
1554    #[test]
1555    fn test_struct_def_field_count() {
1556        let empty = StructDef {
1557            name: "Empty".to_string(),
1558            fields: vec![],
1559            is_copy: false,
1560            is_handle: false,
1561            is_linear: false,
1562            destructor: None,
1563            is_builtin: false,
1564            is_pub: false,
1565            file_id: gruel_span::FileId::DEFAULT,
1566        };
1567        assert_eq!(empty.field_count(), 0);
1568
1569        let with_fields = StructDef {
1570            name: "Data".to_string(),
1571            fields: vec![
1572                StructField {
1573                    name: "a".to_string(),
1574                    ty: Type::I32,
1575                },
1576                StructField {
1577                    name: "b".to_string(),
1578                    ty: Type::BOOL,
1579                },
1580                StructField {
1581                    name: "c".to_string(),
1582                    ty: Type::I64,
1583                },
1584            ],
1585            is_copy: false,
1586            is_handle: false,
1587            is_linear: false,
1588            destructor: None,
1589            is_builtin: false,
1590            is_pub: false,
1591            file_id: gruel_span::FileId::DEFAULT,
1592        };
1593        assert_eq!(with_fields.field_count(), 3);
1594    }
1595
1596    // ========== EnumDef tests ==========
1597
1598    #[test]
1599    fn test_enum_def_variant_count() {
1600        let empty = EnumDef {
1601            name: "Empty".to_string(),
1602            variants: vec![],
1603            is_pub: false,
1604            file_id: gruel_span::FileId::DEFAULT,
1605        };
1606        assert_eq!(empty.variant_count(), 0);
1607
1608        let color = EnumDef {
1609            name: "Color".to_string(),
1610            variants: vec![
1611                EnumVariantDef::unit("Red"),
1612                EnumVariantDef::unit("Green"),
1613                EnumVariantDef::unit("Blue"),
1614            ],
1615            is_pub: false,
1616            file_id: gruel_span::FileId::DEFAULT,
1617        };
1618        assert_eq!(color.variant_count(), 3);
1619    }
1620
1621    #[test]
1622    fn test_enum_def_find_variant() {
1623        let color = EnumDef {
1624            name: "Color".to_string(),
1625            variants: vec![
1626                EnumVariantDef::unit("Red"),
1627                EnumVariantDef::unit("Green"),
1628                EnumVariantDef::unit("Blue"),
1629            ],
1630            is_pub: false,
1631            file_id: gruel_span::FileId::DEFAULT,
1632        };
1633
1634        assert_eq!(color.find_variant("Red"), Some(0));
1635        assert_eq!(color.find_variant("Green"), Some(1));
1636        assert_eq!(color.find_variant("Blue"), Some(2));
1637        assert_eq!(color.find_variant("Yellow"), None);
1638    }
1639
1640    #[test]
1641    fn test_enum_def_discriminant_type_empty() {
1642        let empty = EnumDef {
1643            name: "Empty".to_string(),
1644            variants: vec![],
1645            is_pub: false,
1646            file_id: gruel_span::FileId::DEFAULT,
1647        };
1648        assert_eq!(empty.discriminant_type(), Type::NEVER);
1649    }
1650
1651    #[test]
1652    fn test_enum_def_discriminant_type_small() {
1653        // 1-256 variants -> U8
1654        let small = EnumDef {
1655            name: "Small".to_string(),
1656            variants: vec![EnumVariantDef::unit("A")],
1657            is_pub: false,
1658            file_id: gruel_span::FileId::DEFAULT,
1659        };
1660        assert_eq!(small.discriminant_type(), Type::U8);
1661
1662        let max_u8 = EnumDef {
1663            name: "MaxU8".to_string(),
1664            variants: (0..256)
1665                .map(|i| EnumVariantDef::unit(format!("V{}", i)))
1666                .collect(),
1667            is_pub: false,
1668            file_id: gruel_span::FileId::DEFAULT,
1669        };
1670        assert_eq!(max_u8.discriminant_type(), Type::U8);
1671    }
1672
1673    #[test]
1674    fn test_enum_def_discriminant_type_medium() {
1675        // 257-65536 variants -> U16
1676        let medium = EnumDef {
1677            name: "Medium".to_string(),
1678            variants: (0..257)
1679                .map(|i| EnumVariantDef::unit(format!("V{}", i)))
1680                .collect(),
1681            is_pub: false,
1682            file_id: gruel_span::FileId::DEFAULT,
1683        };
1684        assert_eq!(medium.discriminant_type(), Type::U16);
1685    }
1686
1687    // ========== Type::COMPTIME_TYPE tests ==========
1688
1689    #[test]
1690    fn test_comptime_type_name() {
1691        assert_eq!(Type::COMPTIME_TYPE.name(), "type");
1692    }
1693
1694    #[test]
1695    fn test_comptime_type_is_copy() {
1696        assert!(Type::COMPTIME_TYPE.is_copy());
1697    }
1698
1699    #[test]
1700    fn test_comptime_type_is_comptime_type() {
1701        assert!(Type::COMPTIME_TYPE.is_comptime_type());
1702        assert!(!Type::I32.is_comptime_type());
1703        assert!(!Type::BOOL.is_comptime_type());
1704    }
1705
1706    #[test]
1707    fn test_comptime_type_not_integer() {
1708        assert!(!Type::COMPTIME_TYPE.is_integer());
1709    }
1710
1711    #[test]
1712    fn test_comptime_type_not_signed() {
1713        assert!(!Type::COMPTIME_TYPE.is_signed());
1714    }
1715
1716    #[test]
1717    fn test_comptime_type_not_64_bit() {
1718        assert!(!Type::COMPTIME_TYPE.is_64_bit());
1719    }
1720
1721    #[test]
1722    fn test_comptime_type_can_coerce_to_itself() {
1723        assert!(Type::COMPTIME_TYPE.can_coerce_to(&Type::COMPTIME_TYPE));
1724    }
1725
1726    #[test]
1727    fn test_comptime_type_cannot_coerce_to_runtime_types() {
1728        assert!(!Type::COMPTIME_TYPE.can_coerce_to(&Type::I32));
1729        assert!(!Type::COMPTIME_TYPE.can_coerce_to(&Type::BOOL));
1730    }
1731
1732    // ========== Type encoding validation tests ==========
1733
1734    #[test]
1735    fn test_is_valid_encoding_primitives() {
1736        // All primitive types (0-19) are valid
1737        for i in 0..=19u32 {
1738            assert!(
1739                Type::is_valid_encoding(i),
1740                "primitive tag {} should be valid",
1741                i
1742            );
1743        }
1744    }
1745
1746    #[test]
1747    fn test_is_valid_encoding_composites() {
1748        // Composite types with valid tags
1749        assert!(Type::is_valid_encoding(100)); // TAG_STRUCT
1750        assert!(Type::is_valid_encoding(101)); // TAG_ENUM
1751        assert!(Type::is_valid_encoding(102)); // TAG_ARRAY
1752        assert!(Type::is_valid_encoding(103)); // TAG_MODULE
1753        assert!(Type::is_valid_encoding(104)); // TAG_PTR_CONST
1754        assert!(Type::is_valid_encoding(105)); // TAG_PTR_MUT
1755
1756        // With IDs in the high bits
1757        assert!(Type::is_valid_encoding(100 | (42 << 8))); // Struct with ID 42
1758        assert!(Type::is_valid_encoding(101 | (100 << 8))); // Enum with ID 100
1759    }
1760
1761    #[test]
1762    fn test_is_valid_encoding_invalid() {
1763        // Tags between primitives and composites are invalid (20-99)
1764        for tag in 20..100u32 {
1765            assert!(
1766                !Type::is_valid_encoding(tag),
1767                "tag {} should be invalid",
1768                tag
1769            );
1770        }
1771
1772        // Tags above composites are invalid (106+)
1773        for tag in 106..=255u32 {
1774            assert!(
1775                !Type::is_valid_encoding(tag),
1776                "tag {} should be invalid",
1777                tag
1778            );
1779        }
1780    }
1781
1782    #[test]
1783    fn test_try_from_u32_valid() {
1784        // Valid primitives
1785        assert!(Type::try_from_u32(0).is_some()); // I8
1786        assert!(Type::try_from_u32(2).is_some()); // I32
1787        assert!(Type::try_from_u32(10).is_some()); // F16
1788
1789        // Valid composites
1790        assert!(Type::try_from_u32(100).is_some()); // Struct(0)
1791        assert!(Type::try_from_u32(100 | (42 << 8)).is_some()); // Struct(42)
1792    }
1793
1794    #[test]
1795    fn test_try_from_u32_invalid() {
1796        // Invalid tags
1797        assert!(Type::try_from_u32(50).is_none());
1798        assert!(Type::try_from_u32(99).is_none());
1799        assert!(Type::try_from_u32(106).is_none());
1800        assert!(Type::try_from_u32(255).is_none());
1801    }
1802
1803    #[test]
1804    fn test_try_kind_valid() {
1805        assert_eq!(Type::I32.try_kind(), Some(TypeKind::I32));
1806        assert_eq!(Type::BOOL.try_kind(), Some(TypeKind::Bool));
1807        assert_eq!(
1808            Type::new_struct(StructId(42)).try_kind(),
1809            Some(TypeKind::Struct(StructId(42)))
1810        );
1811    }
1812
1813    #[test]
1814    fn test_try_kind_invalid() {
1815        // Create an invalid Type by directly constructing with invalid encoding
1816        let invalid = Type::from_u32(50); // Tag 50 is invalid
1817        assert!(invalid.try_kind().is_none());
1818
1819        let invalid2 = Type::from_u32(200); // Tag 200 is invalid
1820        assert!(invalid2.try_kind().is_none());
1821    }
1822
1823    #[test]
1824    fn test_is_valid_method() {
1825        assert!(Type::I32.is_valid());
1826        assert!(Type::new_struct(StructId(0)).is_valid());
1827
1828        // Invalid types
1829        let invalid = Type::from_u32(50);
1830        assert!(!invalid.is_valid());
1831    }
1832
1833    #[test]
1834    #[should_panic(expected = "invalid Type encoding")]
1835    fn test_kind_panics_on_invalid() {
1836        let invalid = Type::from_u32(50);
1837        let _ = invalid.kind(); // Should panic
1838    }
1839
1840    #[test]
1841    fn test_roundtrip_encoding() {
1842        // Test that as_u32 and from_u32 are inverses for valid types
1843        let types = [
1844            Type::I8,
1845            Type::I16,
1846            Type::I32,
1847            Type::I64,
1848            Type::U8,
1849            Type::U16,
1850            Type::U32,
1851            Type::U64,
1852            Type::BOOL,
1853            Type::UNIT,
1854            Type::ERROR,
1855            Type::NEVER,
1856            Type::COMPTIME_TYPE,
1857            Type::COMPTIME_STR,
1858            Type::new_struct(StructId(0)),
1859            Type::new_struct(StructId(1000)),
1860            Type::new_enum(EnumId(5)),
1861            Type::new_array(ArrayTypeId(10)),
1862            Type::new_ptr_const(PtrConstTypeId(20)),
1863            Type::new_ptr_mut(PtrMutTypeId(30)),
1864            Type::new_module(ModuleId(40)),
1865        ];
1866
1867        for ty in types {
1868            let encoded = ty.as_u32();
1869            let decoded = Type::from_u32(encoded);
1870            assert_eq!(ty, decoded, "roundtrip failed for {:?}", ty);
1871            assert!(
1872                decoded.is_valid(),
1873                "{:?} should be valid after roundtrip",
1874                ty
1875            );
1876        }
1877    }
1878}