gruel_air/
intern_pool.rs

1//! Type intern pool for efficient type representation.
2//!
3//! This module implements a unified type interning system inspired by Zig's `InternPool`.
4//! All types become 32-bit indices into a canonical pool, enabling:
5//!
6//! - O(1) type equality (u32 comparison)
7//! - Efficient memory usage
8//! - Clean parallel compilation (no per-function type merging)
9//! - Foundation for future generics
10//!
11//! # Architecture
12//!
13//! The `TypeInternPool` serves as a canonical repository for all composite types:
14//! - **Structs and enums** are nominal types (same name = same type)
15//! - **Arrays** are structural types (same element type + length = same type)
16//!
17//! Primitive types (i8-i64, u8-u64, bool, unit, never, error) are encoded directly
18//! in the `InternedType` index using reserved indices 0-15, requiring no pool lookup.
19//!
20//! # Migration Strategy (ADR-0024)
21//!
22//! This module is part of Phase 1 of the Type Intern Pool migration:
23//! - Phase 1: Introduce pool alongside existing system (this module)
24//! - Phase 2: Migrate array types to the pool
25//! - Phase 3: Migrate struct/enum IDs to pool indices
26//! - Phase 4: Unify Type representation to `InternedType(u32)`
27//!
28//! During Phase 1, the pool coexists with the existing `Type` enum, `StructId`,
29//! `EnumId`, and `ArrayTypeId`. The pool is populated during declaration collection
30//! but not yet used for type operations.
31//!
32//! # Thread Safety
33//!
34//! The pool uses `RwLock` for thread-safe access during parallel compilation:
35//! - Read lock for lookups (common case)
36//! - Write lock for insertions (rare, during declaration gathering)
37
38use std::collections::HashMap;
39use std::sync::{PoisonError, RwLock};
40
41use lasso::Spur;
42
43use crate::types::{
44    ArrayTypeId, EnumDef, EnumId, PtrConstTypeId, PtrMutTypeId, StructDef, StructId, Type, TypeKind,
45};
46
47/// Interned type index - 32 bits, Copy, cheap comparison.
48///
49/// Reserved indices 0-15 are primitives (no lookup needed).
50/// Index 16+ are composite types stored in the pool.
51///
52/// # Primitive Encoding
53///
54/// The following indices are reserved for primitive types:
55/// - 0: i8
56/// - 1: i16
57/// - 2: i32
58/// - 3: i64
59/// - 4: u8
60/// - 5: u16
61/// - 6: u32
62/// - 7: u64
63/// - 8: isize
64/// - 9: usize
65/// - 10: f16
66/// - 11: f32
67/// - 12: f64
68/// - 13: bool
69/// - 14: unit
70/// - 15: never
71/// - 16: error
72/// - 17-18: reserved for future primitives
73#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
74pub struct InternedType(u32);
75
76impl InternedType {
77    // Reserved indices for primitives
78    pub const I8: InternedType = InternedType(0);
79    pub const I16: InternedType = InternedType(1);
80    pub const I32: InternedType = InternedType(2);
81    pub const I64: InternedType = InternedType(3);
82    pub const U8: InternedType = InternedType(4);
83    pub const U16: InternedType = InternedType(5);
84    pub const U32: InternedType = InternedType(6);
85    pub const U64: InternedType = InternedType(7);
86    pub const ISIZE: InternedType = InternedType(8);
87    pub const USIZE: InternedType = InternedType(9);
88    pub const F16: InternedType = InternedType(10);
89    pub const F32: InternedType = InternedType(11);
90    pub const F64: InternedType = InternedType(12);
91    pub const BOOL: InternedType = InternedType(13);
92    pub const UNIT: InternedType = InternedType(14);
93    pub const NEVER: InternedType = InternedType(15);
94    pub const ERROR: InternedType = InternedType(16);
95
96    const PRIMITIVE_COUNT: u32 = 19;
97
98    /// Check if this is a primitive type (no pool lookup needed).
99    #[inline]
100    pub fn is_primitive(self) -> bool {
101        self.0 < Self::PRIMITIVE_COUNT
102    }
103
104    /// Get the raw index value.
105    #[inline]
106    pub fn index(self) -> u32 {
107        self.0
108    }
109
110    /// Create an InternedType from a raw index.
111    ///
112    /// # Safety
113    ///
114    /// The caller must ensure the index is valid (either a primitive index 0-15,
115    /// or a composite index that exists in the pool).
116    #[inline]
117    pub fn from_raw(index: u32) -> Self {
118        InternedType(index)
119    }
120
121    /// Create an InternedType for a composite type from its pool index.
122    ///
123    /// The pool index is offset by `PRIMITIVE_COUNT` to produce the final index.
124    #[inline]
125    fn from_pool_index(pool_index: u32) -> Self {
126        InternedType(pool_index + Self::PRIMITIVE_COUNT)
127    }
128
129    /// Get the pool index for a composite type.
130    ///
131    /// Returns `None` for primitive types.
132    #[inline]
133    pub fn pool_index(self) -> Option<u32> {
134        if self.is_primitive() {
135            None
136        } else {
137            Some(self.0 - Self::PRIMITIVE_COUNT)
138        }
139    }
140}
141
142impl std::fmt::Debug for InternedType {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        if self.is_primitive() {
145            let name = match self.0 {
146                0 => "i8",
147                1 => "i16",
148                2 => "i32",
149                3 => "i64",
150                4 => "u8",
151                5 => "u16",
152                6 => "u32",
153                7 => "u64",
154                8 => "bool",
155                9 => "()",
156                10 => "!",
157                11 => "<error>",
158                _ => "<reserved>",
159            };
160            write!(f, "InternedType({name})")
161        } else {
162            write!(f, "InternedType(pool:{})", self.0 - Self::PRIMITIVE_COUNT)
163        }
164    }
165}
166
167/// Type data stored in the intern pool.
168///
169/// This is NOT Copy - it lives in the pool. You work with `InternedType` indices.
170///
171/// # Type Categories
172///
173/// - **Struct** and **Enum** are nominal types: identity comes from the name
174/// - **Array**, **PtrConst**, and **PtrMut** are structural types: identity comes from element/pointee type
175#[derive(Debug, Clone)]
176pub enum TypeData {
177    /// User-defined struct (nominal type).
178    ///
179    /// Two structs with the same fields but different names are different types.
180    Struct(StructData),
181
182    /// User-defined enum (nominal type).
183    ///
184    /// Two enums with the same variants but different names are different types.
185    Enum(EnumData),
186
187    /// Fixed-size array (structural type).
188    ///
189    /// Arrays with the same element type and length are the same type,
190    /// regardless of where they were defined.
191    Array { element: InternedType, len: u64 },
192
193    /// Raw const pointer (structural type).
194    ///
195    /// `ptr const T` - pointer to immutable data.
196    PtrConst { pointee: InternedType },
197
198    /// Raw mut pointer (structural type).
199    ///
200    /// `ptr mut T` - pointer to mutable data.
201    PtrMut { pointee: InternedType },
202}
203
204/// Data for a struct type in the intern pool.
205///
206/// During Phase 1, this mirrors the existing `StructDef` to verify correctness.
207/// In later phases, `StructDef` will be replaced by this.
208#[derive(Debug, Clone)]
209pub struct StructData {
210    /// The name symbol (interned string).
211    pub name: Spur,
212    /// Reference to the full struct definition.
213    /// During Phase 1, we keep a clone of the StructDef for verification.
214    /// In later phases, the pool will be the canonical source.
215    pub def: StructDef,
216}
217
218/// Data for an enum type in the intern pool.
219///
220/// During Phase 1, this mirrors the existing `EnumDef` to verify correctness.
221/// In later phases, `EnumDef` will be replaced by this.
222#[derive(Debug, Clone)]
223pub struct EnumData {
224    /// The name symbol (interned string).
225    pub name: Spur,
226    /// Reference to the full enum definition.
227    /// During Phase 1, we keep a clone of the EnumDef for verification.
228    /// In later phases, the pool will be the canonical source.
229    pub def: EnumDef,
230}
231
232/// Thread-safe intern pool for all composite types.
233///
234/// The pool is designed to be built during declaration gathering (sequential)
235/// and then queried during function body analysis (potentially parallel).
236///
237/// # Thread Safety
238///
239/// Uses `RwLock` for interior mutability:
240/// - Read lock for lookups (most common)
241/// - Write lock for insertions (only during declaration gathering)
242///
243/// # Usage
244///
245/// ```ignore
246/// let pool = TypeInternPool::new();
247///
248/// // Register nominal types (structs/enums)
249/// let (struct_type, is_new) = pool.register_struct(name_spur, struct_def);
250///
251/// // Intern structural types (arrays)
252/// let array_type = pool.intern_array(element_type, 10);
253///
254/// // Look up type data
255/// if let Some(data) = pool.try_get(some_type) {
256///     match data {
257///         TypeData::Struct(s) => println!("struct {}", s.def.name),
258///         TypeData::Enum(e) => println!("enum {}", e.def.name),
259///         TypeData::Array { element, len } => println!("array of {:?}; {}", element, len),
260///     }
261/// }
262/// ```
263#[derive(Debug)]
264pub struct TypeInternPool {
265    inner: RwLock<TypeInternPoolInner>,
266}
267
268#[derive(Debug)]
269struct TypeInternPoolInner {
270    /// All composite type data, indexed by (InternedType.0 - PRIMITIVE_COUNT).
271    types: Vec<TypeData>,
272
273    /// Structural type deduplication: (element, len) -> InternedType for arrays.
274    array_map: HashMap<(InternedType, u64), InternedType>,
275
276    /// Structural type deduplication: pointee -> InternedType for ptr const.
277    ptr_const_map: HashMap<InternedType, InternedType>,
278
279    /// Structural type deduplication: pointee -> InternedType for ptr mut.
280    ptr_mut_map: HashMap<InternedType, InternedType>,
281
282    /// Nominal type lookup: name -> InternedType for structs.
283    struct_by_name: HashMap<Spur, InternedType>,
284
285    /// Nominal type lookup: name -> InternedType for enums.
286    enum_by_name: HashMap<Spur, InternedType>,
287}
288
289impl TypeInternPool {
290    /// Create a new empty pool.
291    pub fn new() -> Self {
292        Self {
293            inner: RwLock::new(TypeInternPoolInner {
294                types: Vec::new(),
295                array_map: HashMap::new(),
296                ptr_const_map: HashMap::new(),
297                ptr_mut_map: HashMap::new(),
298                struct_by_name: HashMap::new(),
299                enum_by_name: HashMap::new(),
300            }),
301        }
302    }
303
304    /// Register a new struct (nominal - no deduplication).
305    ///
306    /// Returns the `StructId` (containing the pool index) and whether it was newly inserted.
307    /// If a struct with this name already exists, returns the existing StructId.
308    pub fn register_struct(&self, name: Spur, def: StructDef) -> (StructId, bool) {
309        // Fast path: check with read lock
310        {
311            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
312            if let Some(&existing) = inner.struct_by_name.get(&name) {
313                // Convert InternedType back to StructId via pool_index
314                let pool_index = existing.pool_index().expect("struct must have pool index");
315                return (StructId::from_pool_index(pool_index), false);
316            }
317        }
318
319        // Slow path: acquire write lock
320        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
321
322        // Double-check after acquiring write lock
323        if let Some(&existing) = inner.struct_by_name.get(&name) {
324            let pool_index = existing.pool_index().expect("struct must have pool index");
325            return (StructId::from_pool_index(pool_index), false);
326        }
327
328        // Create new struct type
329        let pool_index = inner.types.len() as u32;
330        let interned = InternedType::from_pool_index(pool_index);
331
332        inner.types.push(TypeData::Struct(StructData { name, def }));
333        inner.struct_by_name.insert(name, interned);
334
335        (StructId::from_pool_index(pool_index), true)
336    }
337
338    /// Reserve a struct ID without registering the full definition yet.
339    ///
340    /// This is used for anonymous structs where we need to know the ID before
341    /// we can construct the name (which includes the ID). Call `complete_struct_registration`
342    /// with the reserved ID to finish registration.
343    ///
344    /// # Returns
345    ///
346    /// Returns the reserved `StructId`. The caller MUST call `complete_struct_registration`
347    /// with this ID before any other pool operations that might read this entry.
348    ///
349    /// # Example
350    ///
351    /// ```ignore
352    /// let struct_id = pool.reserve_struct_id();
353    /// let name = format!("__anon_struct_{}", struct_id.0);
354    /// let name_spur = interner.get_or_intern(&name);
355    /// let def = StructDef { name: name.clone(), ... };
356    /// pool.complete_struct_registration(struct_id, name_spur, def);
357    /// ```
358    pub fn reserve_struct_id(&self) -> StructId {
359        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
360
361        // Reserve a slot by pushing a placeholder
362        // We use a placeholder Struct with empty data that will be overwritten
363        let pool_index = inner.types.len() as u32;
364
365        // Push a placeholder - this reserves the index
366        // The placeholder will be replaced by complete_struct_registration
367        inner.types.push(TypeData::Struct(StructData {
368            name: Spur::default(),
369            def: StructDef {
370                name: String::new(),
371                fields: vec![],
372                is_copy: false,
373                is_handle: false,
374                is_linear: false,
375                destructor: None,
376                is_builtin: false,
377                is_pub: false,
378                file_id: gruel_span::FileId::DEFAULT,
379            },
380        }));
381
382        StructId::from_pool_index(pool_index)
383    }
384
385    /// Complete the registration of a previously reserved struct ID.
386    ///
387    /// This must be called after `reserve_struct_id` to fill in the actual struct data.
388    /// The struct will be registered with the provided name for lookup purposes.
389    ///
390    /// # Panics
391    ///
392    /// Panics if:
393    /// - The struct_id wasn't created by `reserve_struct_id`
394    /// - The slot at struct_id doesn't contain a placeholder struct
395    /// - A struct with the given name already exists
396    pub fn complete_struct_registration(&self, struct_id: StructId, name: Spur, def: StructDef) {
397        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
398        let pool_index = struct_id.0 as usize;
399
400        // Verify this is a valid reserved slot
401        assert!(
402            pool_index < inner.types.len(),
403            "Invalid reserved struct ID: index {} out of bounds (len {})",
404            pool_index,
405            inner.types.len()
406        );
407
408        // Check that a struct with this name doesn't already exist
409        assert!(
410            !inner.struct_by_name.contains_key(&name),
411            "Struct with this name already exists"
412        );
413
414        // Update the placeholder with actual data
415        inner.types[pool_index] = TypeData::Struct(StructData { name, def });
416
417        // Register in the name lookup
418        let interned = InternedType::from_pool_index(pool_index as u32);
419        inner.struct_by_name.insert(name, interned);
420    }
421
422    /// Register a new enum (nominal - no deduplication).
423    ///
424    /// Returns the `EnumId` (containing the pool index) and whether it was newly inserted.
425    /// If an enum with this name already exists, returns the existing EnumId.
426    pub fn register_enum(&self, name: Spur, def: EnumDef) -> (EnumId, bool) {
427        // Fast path: check with read lock
428        {
429            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
430            if let Some(&existing) = inner.enum_by_name.get(&name) {
431                let pool_index = existing.pool_index().expect("enum must have pool index");
432                return (EnumId::from_pool_index(pool_index), false);
433            }
434        }
435
436        // Slow path: acquire write lock
437        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
438
439        // Double-check after acquiring write lock
440        if let Some(&existing) = inner.enum_by_name.get(&name) {
441            let pool_index = existing.pool_index().expect("enum must have pool index");
442            return (EnumId::from_pool_index(pool_index), false);
443        }
444
445        // Create new enum type
446        let pool_index = inner.types.len() as u32;
447        let interned = InternedType::from_pool_index(pool_index);
448
449        inner.types.push(TypeData::Enum(EnumData { name, def }));
450        inner.enum_by_name.insert(name, interned);
451
452        (EnumId::from_pool_index(pool_index), true)
453    }
454
455    /// Intern an array type (structural - deduplicates).
456    ///
457    /// Returns the canonical `InternedType` for arrays with this element type and length.
458    /// If an identical array type already exists, returns the existing type.
459    pub fn intern_array(&self, element: InternedType, len: u64) -> InternedType {
460        let key = (element, len);
461
462        // Fast path: check with read lock
463        {
464            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
465            if let Some(&existing) = inner.array_map.get(&key) {
466                return existing;
467            }
468        }
469
470        // Slow path: acquire write lock
471        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
472
473        // Double-check after acquiring write lock
474        if let Some(&existing) = inner.array_map.get(&key) {
475            return existing;
476        }
477
478        // Create new array type
479        let pool_index = inner.types.len() as u32;
480        let interned = InternedType::from_pool_index(pool_index);
481
482        inner.types.push(TypeData::Array { element, len });
483        inner.array_map.insert(key, interned);
484
485        interned
486    }
487
488    /// Intern a ptr const type (structural - deduplicates).
489    ///
490    /// Returns the canonical `InternedType` for pointers to this pointee type.
491    /// If an identical pointer type already exists, returns the existing type.
492    pub fn intern_ptr_const(&self, pointee: InternedType) -> InternedType {
493        // Fast path: check with read lock
494        {
495            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
496            if let Some(&existing) = inner.ptr_const_map.get(&pointee) {
497                return existing;
498            }
499        }
500
501        // Slow path: acquire write lock
502        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
503
504        // Double-check after acquiring write lock
505        if let Some(&existing) = inner.ptr_const_map.get(&pointee) {
506            return existing;
507        }
508
509        // Create new pointer type
510        let pool_index = inner.types.len() as u32;
511        let interned = InternedType::from_pool_index(pool_index);
512
513        inner.types.push(TypeData::PtrConst { pointee });
514        inner.ptr_const_map.insert(pointee, interned);
515
516        interned
517    }
518
519    /// Intern a ptr mut type (structural - deduplicates).
520    ///
521    /// Returns the canonical `InternedType` for mutable pointers to this pointee type.
522    /// If an identical pointer type already exists, returns the existing type.
523    pub fn intern_ptr_mut(&self, pointee: InternedType) -> InternedType {
524        // Fast path: check with read lock
525        {
526            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
527            if let Some(&existing) = inner.ptr_mut_map.get(&pointee) {
528                return existing;
529            }
530        }
531
532        // Slow path: acquire write lock
533        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
534
535        // Double-check after acquiring write lock
536        if let Some(&existing) = inner.ptr_mut_map.get(&pointee) {
537            return existing;
538        }
539
540        // Create new pointer type
541        let pool_index = inner.types.len() as u32;
542        let interned = InternedType::from_pool_index(pool_index);
543
544        inner.types.push(TypeData::PtrMut { pointee });
545        inner.ptr_mut_map.insert(pointee, interned);
546
547        interned
548    }
549
550    /// Look up a struct by name.
551    ///
552    pub fn get_struct_by_name(&self, name: Spur) -> Option<InternedType> {
553        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
554        inner.struct_by_name.get(&name).copied()
555    }
556
557    /// Look up an enum by name.
558    pub fn get_enum_by_name(&self, name: Spur) -> Option<InternedType> {
559        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
560        inner.enum_by_name.get(&name).copied()
561    }
562
563    /// Look up an array type by element and length.
564    pub fn get_array(&self, element: InternedType, len: u64) -> Option<InternedType> {
565        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
566        inner.array_map.get(&(element, len)).copied()
567    }
568
569    /// Get type data for a composite type.
570    ///
571    /// Returns `None` for primitive types (use `InternedType::is_primitive()` first).
572    ///
573    /// # Panics
574    ///
575    /// Panics if the index is invalid.
576    pub fn get(&self, ty: InternedType) -> Option<TypeData> {
577        if ty.is_primitive() {
578            return None;
579        }
580
581        let pool_index = ty.pool_index().expect("non-primitive must have pool index");
582        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
583        Some(inner.types[pool_index as usize].clone())
584    }
585
586    /// Check if this is a struct type.
587    pub fn is_struct(&self, ty: InternedType) -> bool {
588        if ty.is_primitive() {
589            return false;
590        }
591        matches!(self.get(ty), Some(TypeData::Struct(_)))
592    }
593
594    /// Check if this is an enum type.
595    pub fn is_enum(&self, ty: InternedType) -> bool {
596        if ty.is_primitive() {
597            return false;
598        }
599        matches!(self.get(ty), Some(TypeData::Enum(_)))
600    }
601
602    /// Check if this is an array type.
603    pub fn is_array(&self, ty: InternedType) -> bool {
604        if ty.is_primitive() {
605            return false;
606        }
607        matches!(self.get(ty), Some(TypeData::Array { .. }))
608    }
609
610    /// Get the struct definition if this is a struct type.
611    pub fn get_struct_def(&self, ty: InternedType) -> Option<StructDef> {
612        match self.get(ty)? {
613            TypeData::Struct(data) => Some(data.def),
614            _ => None,
615        }
616    }
617
618    /// Get the enum definition if this is an enum type.
619    pub fn get_enum_def(&self, ty: InternedType) -> Option<EnumDef> {
620        match self.get(ty)? {
621            TypeData::Enum(data) => Some(data.def),
622            _ => None,
623        }
624    }
625
626    /// Get array info (element type, length) if this is an array type.
627    pub fn get_array_info(&self, ty: InternedType) -> Option<(InternedType, u64)> {
628        match self.get(ty)? {
629            TypeData::Array { element, len } => Some((element, len)),
630            _ => None,
631        }
632    }
633
634    // ========================================================================
635    // Phase 3 helpers: Direct StructId/EnumId access
636    // ========================================================================
637    //
638    // These methods allow accessing struct and enum definitions directly via
639    // StructId/EnumId, which now store pool indices instead of vector indices.
640
641    /// Get a struct definition by StructId.
642    ///
643    /// The StructId contains a pool index. This method looks up the struct
644    /// in the pool and returns a clone of its definition.
645    ///
646    /// # Panics
647    ///
648    /// Panics if the StructId doesn't correspond to a struct in the pool.
649    pub fn struct_def(&self, struct_id: StructId) -> StructDef {
650        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
651        let pool_index = struct_id.0 as usize;
652        match &inner.types[pool_index] {
653            TypeData::Struct(data) => data.def.clone(),
654            other => panic!(
655                "Expected struct at pool index {}, got {:?}",
656                pool_index, other
657            ),
658        }
659    }
660
661    /// Get an enum definition by EnumId.
662    ///
663    /// The EnumId contains a pool index. This method looks up the enum
664    /// in the pool and returns a clone of its definition.
665    ///
666    /// # Panics
667    ///
668    /// Panics if the EnumId doesn't correspond to an enum in the pool.
669    pub fn enum_def(&self, enum_id: EnumId) -> EnumDef {
670        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
671        let pool_index = enum_id.0 as usize;
672        match &inner.types[pool_index] {
673            TypeData::Enum(data) => data.def.clone(),
674            other => panic!(
675                "Expected enum at pool index {}, got {:?}",
676                pool_index, other
677            ),
678        }
679    }
680
681    /// Update a struct definition in the pool.
682    ///
683    /// This is used during semantic analysis when struct fields are resolved
684    /// after the struct is initially registered.
685    ///
686    /// # Panics
687    ///
688    /// Panics if the StructId doesn't correspond to a struct in the pool.
689    pub fn update_struct_def(&self, struct_id: StructId, new_def: StructDef) {
690        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
691        let pool_index = struct_id.0 as usize;
692        match &mut inner.types[pool_index] {
693            TypeData::Struct(data) => data.def = new_def,
694            other => panic!(
695                "Expected struct at pool index {}, got {:?}",
696                pool_index, other
697            ),
698        }
699    }
700
701    /// Update an enum definition in the pool.
702    ///
703    /// This is used during semantic analysis when enum variants are resolved
704    /// after the enum is initially registered.
705    ///
706    /// # Panics
707    ///
708    /// Panics if the EnumId doesn't correspond to an enum in the pool.
709    pub fn update_enum_def(&self, enum_id: EnumId, new_def: EnumDef) {
710        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
711        let pool_index = enum_id.0 as usize;
712        match &mut inner.types[pool_index] {
713            TypeData::Enum(data) => data.def = new_def,
714            other => panic!(
715                "Expected enum at pool index {}, got {:?}",
716                pool_index, other
717            ),
718        }
719    }
720
721    /// Convert a StructId to an InternedType.
722    ///
723    /// Since StructId now contains a pool index, we just add the primitive offset.
724    #[inline]
725    pub fn struct_id_to_interned(&self, struct_id: StructId) -> InternedType {
726        InternedType::from_pool_index(struct_id.0)
727    }
728
729    /// Convert an EnumId to an InternedType.
730    ///
731    /// Since EnumId now contains a pool index, we just add the primitive offset.
732    #[inline]
733    pub fn enum_id_to_interned(&self, enum_id: EnumId) -> InternedType {
734        InternedType::from_pool_index(enum_id.0)
735    }
736
737    /// Get an array type definition by ArrayTypeId.
738    ///
739    /// The ArrayTypeId contains a pool index. This method looks up the array
740    /// in the pool and returns its element type and length as a tuple.
741    ///
742    /// # Returns
743    ///
744    /// Returns `(element_type, length)` where `element_type` is the array's element type
745    /// and `length` is the array's fixed size.
746    ///
747    /// # Panics
748    ///
749    /// Panics if the ArrayTypeId doesn't correspond to an array in the pool.
750    pub fn array_def(&self, array_id: ArrayTypeId) -> (Type, u64) {
751        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
752        let pool_index = array_id.0 as usize;
753        match &inner.types[pool_index] {
754            TypeData::Array { element, len } => {
755                // Convert InternedType back to Type
756                let element_type = Self::interned_to_type_recursive(*element, &inner);
757                (element_type, *len)
758            }
759            other => panic!(
760                "Expected array at pool index {}, got {:?}",
761                pool_index, other
762            ),
763        }
764    }
765
766    /// Intern an array type from a Type element.
767    ///
768    /// This is a helper method that converts the Type to InternedType
769    /// and then interns the array.
770    ///
771    /// # Panics
772    ///
773    /// Panics if the element type contains a struct/enum that isn't in the pool.
774    pub fn intern_array_from_type(&self, element_type: Type, len: u64) -> ArrayTypeId {
775        let element_interned = Self::type_to_interned_recursive(element_type);
776        let array_interned = self.intern_array(element_interned, len);
777        ArrayTypeId::from_pool_index(
778            array_interned
779                .pool_index()
780                .expect("array must have pool index"),
781        )
782    }
783
784    /// Look up an array type by Type element and length.
785    ///
786    /// Returns None if no such array exists in the pool.
787    pub fn get_array_by_type(&self, element_type: Type, len: u64) -> Option<ArrayTypeId> {
788        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
789        let element_interned = Self::type_to_interned_recursive(element_type);
790        let array_interned = inner.array_map.get(&(element_interned, len))?;
791        Some(ArrayTypeId::from_pool_index(
792            array_interned
793                .pool_index()
794                .expect("array must have pool index"),
795        ))
796    }
797
798    /// Intern a ptr const type from a Type pointee.
799    ///
800    /// # Panics
801    ///
802    /// Panics if the pointee type contains a struct/enum that isn't in the pool.
803    pub fn intern_ptr_const_from_type(&self, pointee_type: Type) -> PtrConstTypeId {
804        let pointee_interned = Self::type_to_interned_recursive(pointee_type);
805        let ptr_interned = self.intern_ptr_const(pointee_interned);
806        PtrConstTypeId::from_pool_index(
807            ptr_interned
808                .pool_index()
809                .expect("ptr const must have pool index"),
810        )
811    }
812
813    /// Intern a ptr mut type from a Type pointee.
814    ///
815    /// # Panics
816    ///
817    /// Panics if the pointee type contains a struct/enum that isn't in the pool.
818    pub fn intern_ptr_mut_from_type(&self, pointee_type: Type) -> PtrMutTypeId {
819        let pointee_interned = Self::type_to_interned_recursive(pointee_type);
820        let ptr_interned = self.intern_ptr_mut(pointee_interned);
821        PtrMutTypeId::from_pool_index(
822            ptr_interned
823                .pool_index()
824                .expect("ptr mut must have pool index"),
825        )
826    }
827
828    /// Get ptr const pointee type if this is a ptr const type.
829    pub fn ptr_const_def(&self, ptr_id: PtrConstTypeId) -> Type {
830        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
831        let pool_index = ptr_id.0 as usize;
832        match &inner.types[pool_index] {
833            TypeData::PtrConst { pointee } => Self::interned_to_type_recursive(*pointee, &inner),
834            other => panic!(
835                "Expected ptr const at pool index {}, got {:?}",
836                pool_index, other
837            ),
838        }
839    }
840
841    /// Get ptr mut pointee type if this is a ptr mut type.
842    pub fn ptr_mut_def(&self, ptr_id: PtrMutTypeId) -> Type {
843        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
844        let pool_index = ptr_id.0 as usize;
845        match &inner.types[pool_index] {
846            TypeData::PtrMut { pointee } => Self::interned_to_type_recursive(*pointee, &inner),
847            other => panic!(
848                "Expected ptr mut at pool index {}, got {:?}",
849                pool_index, other
850            ),
851        }
852    }
853
854    /// Convert InternedType to Type recursively (handles composite types).
855    ///
856    /// This is used to convert array element types from InternedType back to Type.
857    fn interned_to_type_recursive(ty: InternedType, inner: &TypeInternPoolInner) -> Type {
858        if ty.is_primitive() {
859            return match ty.0 {
860                0 => Type::I8,
861                1 => Type::I16,
862                2 => Type::I32,
863                3 => Type::I64,
864                4 => Type::U8,
865                5 => Type::U16,
866                6 => Type::U32,
867                7 => Type::U64,
868                8 => Type::ISIZE,
869                9 => Type::USIZE,
870                10 => Type::F16,
871                11 => Type::F32,
872                12 => Type::F64,
873                13 => Type::BOOL,
874                14 => Type::UNIT,
875                15 => Type::NEVER,
876                16 => Type::ERROR,
877                _ => panic!("Unknown primitive index: {}", ty.0),
878            };
879        }
880
881        let pool_index = ty.pool_index().expect("non-primitive must have pool index");
882        match &inner.types[pool_index as usize] {
883            TypeData::Struct(_) => Type::new_struct(StructId::from_pool_index(pool_index)),
884            TypeData::Enum(_) => Type::new_enum(EnumId::from_pool_index(pool_index)),
885            TypeData::Array { .. } => Type::new_array(ArrayTypeId::from_pool_index(pool_index)),
886            TypeData::PtrConst { .. } => {
887                Type::new_ptr_const(PtrConstTypeId::from_pool_index(pool_index))
888            }
889            TypeData::PtrMut { .. } => Type::new_ptr_mut(PtrMutTypeId::from_pool_index(pool_index)),
890        }
891    }
892
893    /// Convert Type to InternedType recursively (handles composite types).
894    ///
895    /// This is used during Phase 2B migration to convert Type to InternedType
896    /// for array interning.
897    fn type_to_interned_recursive(ty: Type) -> InternedType {
898        match ty.kind() {
899            TypeKind::I8 => InternedType::I8,
900            TypeKind::I16 => InternedType::I16,
901            TypeKind::I32 => InternedType::I32,
902            TypeKind::I64 => InternedType::I64,
903            TypeKind::U8 => InternedType::U8,
904            TypeKind::U16 => InternedType::U16,
905            TypeKind::U32 => InternedType::U32,
906            TypeKind::U64 => InternedType::U64,
907            TypeKind::Isize => InternedType::ISIZE,
908            TypeKind::Usize => InternedType::USIZE,
909            TypeKind::F16 => InternedType::F16,
910            TypeKind::F32 => InternedType::F32,
911            TypeKind::F64 => InternedType::F64,
912            TypeKind::Bool => InternedType::BOOL,
913            TypeKind::Unit => InternedType::UNIT,
914            TypeKind::Never => InternedType::NEVER,
915            TypeKind::Error => InternedType::ERROR,
916            TypeKind::Struct(id) => InternedType::from_pool_index(id.pool_index()),
917            TypeKind::Enum(id) => InternedType::from_pool_index(id.pool_index()),
918            TypeKind::Array(id) => InternedType::from_pool_index(id.pool_index()),
919            TypeKind::PtrConst(id) => InternedType::from_pool_index(id.pool_index()),
920            TypeKind::PtrMut(id) => InternedType::from_pool_index(id.pool_index()),
921            TypeKind::Module(_) => panic!("Cannot intern module types"),
922            TypeKind::ComptimeType => panic!("Cannot intern comptime types"),
923            TypeKind::ComptimeStr => panic!("Cannot intern comptime_str types"),
924            TypeKind::ComptimeInt => panic!("Cannot intern comptime_int types"),
925        }
926    }
927
928    /// Convert an ArrayTypeId to an InternedType.
929    ///
930    /// Since ArrayTypeId now contains a pool index, we just add the primitive offset.
931    #[inline]
932    pub fn array_id_to_interned(&self, array_id: ArrayTypeId) -> InternedType {
933        InternedType::from_pool_index(array_id.0)
934    }
935
936    /// Get all struct IDs registered in the pool.
937    ///
938    /// Returns a vector of all StructId values, useful for iterating over all
939    /// structs (e.g., for drop glue synthesis).
940    pub fn all_struct_ids(&self) -> Vec<StructId> {
941        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
942        inner
943            .struct_by_name
944            .values()
945            .map(|interned| {
946                let pool_index = interned.pool_index().expect("struct must have pool index");
947                StructId::from_pool_index(pool_index)
948            })
949            .collect()
950    }
951
952    /// Get all enum IDs registered in the pool.
953    ///
954    /// Returns a vector of all EnumId values, useful for iterating over all
955    /// enums.
956    pub fn all_enum_ids(&self) -> Vec<EnumId> {
957        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
958        inner
959            .enum_by_name
960            .values()
961            .map(|interned| {
962                let pool_index = interned.pool_index().expect("enum must have pool index");
963                EnumId::from_pool_index(pool_index)
964            })
965            .collect()
966    }
967
968    /// Get all array IDs registered in the pool.
969    ///
970    /// Returns a vector of all ArrayTypeId values, useful for iterating over all
971    /// arrays (e.g., for drop glue synthesis).
972    pub fn all_array_ids(&self) -> Vec<ArrayTypeId> {
973        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
974        inner
975            .types
976            .iter()
977            .enumerate()
978            .filter_map(|(idx, data)| match data {
979                TypeData::Array { .. } => Some(ArrayTypeId(idx as u32)),
980                _ => None,
981            })
982            .collect()
983    }
984
985    /// Get the number of composite types in the pool.
986    pub fn len(&self) -> usize {
987        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
988        inner.types.len()
989    }
990
991    /// Check if the pool is empty (no composite types).
992    pub fn is_empty(&self) -> bool {
993        self.len() == 0
994    }
995
996    /// Get statistics about the pool contents.
997    pub fn stats(&self) -> TypeInternPoolStats {
998        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
999        let mut struct_count = 0;
1000        let mut enum_count = 0;
1001        let mut array_count = 0;
1002
1003        for data in &inner.types {
1004            match data {
1005                TypeData::Struct(_) => struct_count += 1,
1006                TypeData::Enum(_) => enum_count += 1,
1007                TypeData::Array { .. } => array_count += 1,
1008                TypeData::PtrConst { .. } | TypeData::PtrMut { .. } => {
1009                    // Pointer types are not counted separately in stats
1010                }
1011            }
1012        }
1013
1014        TypeInternPoolStats {
1015            struct_count,
1016            enum_count,
1017            array_count,
1018            total: inner.types.len(),
1019        }
1020    }
1021
1022    // ========================================================================
1023    // Conversion helpers for migration (Phase 1)
1024    // ========================================================================
1025
1026    /// Convert an old-style `Type` to an `InternedType`.
1027    ///
1028    /// This is a temporary helper for Phase 1 migration. It converts the
1029    /// existing `Type` enum to the new interned representation.
1030    ///
1031    /// # Note
1032    ///
1033    /// For struct/enum types, the corresponding type must already be registered
1034    /// in the pool. For array types, this returns an error since array interning
1035    /// requires the pool to already have the element type interned.
1036    pub fn type_to_interned(&self, ty: Type) -> Option<InternedType> {
1037        match ty.kind() {
1038            TypeKind::I8 => Some(InternedType::I8),
1039            TypeKind::I16 => Some(InternedType::I16),
1040            TypeKind::I32 => Some(InternedType::I32),
1041            TypeKind::I64 => Some(InternedType::I64),
1042            TypeKind::U8 => Some(InternedType::U8),
1043            TypeKind::U16 => Some(InternedType::U16),
1044            TypeKind::U32 => Some(InternedType::U32),
1045            TypeKind::U64 => Some(InternedType::U64),
1046            TypeKind::Isize => Some(InternedType::ISIZE),
1047            TypeKind::Usize => Some(InternedType::USIZE),
1048            TypeKind::F16 => Some(InternedType::F16),
1049            TypeKind::F32 => Some(InternedType::F32),
1050            TypeKind::F64 => Some(InternedType::F64),
1051            TypeKind::Bool => Some(InternedType::BOOL),
1052            TypeKind::Unit => Some(InternedType::UNIT),
1053            TypeKind::Never => Some(InternedType::NEVER),
1054            TypeKind::Error => Some(InternedType::ERROR),
1055            // Struct, enum, array, pointer, and module require pool lookup by ID - we need the name
1056            // to find the interned type. This conversion is not straightforward
1057            // without additional context. Return None to indicate we can't convert.
1058            TypeKind::Struct(_)
1059            | TypeKind::Enum(_)
1060            | TypeKind::Array(_)
1061            | TypeKind::PtrConst(_)
1062            | TypeKind::PtrMut(_)
1063            | TypeKind::Module(_) => None,
1064            // Comptime-only types cannot be interned for runtime
1065            TypeKind::ComptimeType | TypeKind::ComptimeStr | TypeKind::ComptimeInt => None,
1066        }
1067    }
1068
1069    /// Convert an `InternedType` back to the old-style `Type`.
1070    ///
1071    /// This is a temporary helper for Phase 1 migration to verify correctness.
1072    /// Returns `None` for composite types since we need the old IDs.
1073    pub fn interned_to_type(&self, ty: InternedType) -> Option<Type> {
1074        if !ty.is_primitive() {
1075            return None;
1076        }
1077
1078        Some(match ty.0 {
1079            0 => Type::I8,
1080            1 => Type::I16,
1081            2 => Type::I32,
1082            3 => Type::I64,
1083            4 => Type::U8,
1084            5 => Type::U16,
1085            6 => Type::U32,
1086            7 => Type::U64,
1087            8 => Type::ISIZE,
1088            9 => Type::USIZE,
1089            10 => Type::F16,
1090            11 => Type::F32,
1091            12 => Type::F64,
1092            13 => Type::BOOL,
1093            14 => Type::UNIT,
1094            15 => Type::NEVER,
1095            16 => Type::ERROR,
1096            _ => return None,
1097        })
1098    }
1099
1100    /// Return the number of ABI slots that `ty` occupies when passed as a function argument.
1101    ///
1102    /// - Scalars (integers, bool, enum, pointer) → 1
1103    /// - Struct → sum of field slot counts (flattened)
1104    /// - Array → element slot count × length
1105    /// - Zero-sized types (unit, never, comptime-only, module) → 0
1106    pub fn abi_slot_count(&self, ty: Type) -> u32 {
1107        match ty.kind() {
1108            TypeKind::Struct(id) => {
1109                let def = self.struct_def(id);
1110                def.fields.iter().map(|f| self.abi_slot_count(f.ty)).sum()
1111            }
1112            TypeKind::Array(id) => {
1113                let (elem, len) = self.array_def(id);
1114                self.abi_slot_count(elem) * len as u32
1115            }
1116            TypeKind::Unit
1117            | TypeKind::Never
1118            | TypeKind::ComptimeType
1119            | TypeKind::ComptimeStr
1120            | TypeKind::ComptimeInt
1121            | TypeKind::Module(_) => 0,
1122            _ => 1,
1123        }
1124    }
1125
1126    /// Return the human-readable name of `ty`, suitable for error messages.
1127    ///
1128    /// Examples: `"i32"`, `"bool"`, `"MyStruct"`, `"[i32; 4]"`, `"ptr const i32"`.
1129    pub fn format_type_name(&self, ty: Type) -> String {
1130        match ty.kind() {
1131            TypeKind::I8 => "i8".to_string(),
1132            TypeKind::I16 => "i16".to_string(),
1133            TypeKind::I32 => "i32".to_string(),
1134            TypeKind::I64 => "i64".to_string(),
1135            TypeKind::U8 => "u8".to_string(),
1136            TypeKind::U16 => "u16".to_string(),
1137            TypeKind::U32 => "u32".to_string(),
1138            TypeKind::U64 => "u64".to_string(),
1139            TypeKind::Isize => "isize".to_string(),
1140            TypeKind::Usize => "usize".to_string(),
1141            TypeKind::F16 => "f16".to_string(),
1142            TypeKind::F32 => "f32".to_string(),
1143            TypeKind::F64 => "f64".to_string(),
1144            TypeKind::Bool => "bool".to_string(),
1145            TypeKind::Unit => "()".to_string(),
1146            TypeKind::Never => "!".to_string(),
1147            TypeKind::Error => "<error>".to_string(),
1148            TypeKind::ComptimeType => "type".to_string(),
1149            TypeKind::ComptimeStr => "comptime_str".to_string(),
1150            TypeKind::ComptimeInt => "comptime_int".to_string(),
1151            TypeKind::Struct(id) => self.struct_def(id).name.clone(),
1152            TypeKind::Enum(id) => self.enum_def(id).name.clone(),
1153            TypeKind::Array(id) => {
1154                let (elem, len) = self.array_def(id);
1155                format!("[{}; {}]", self.format_type_name(elem), len)
1156            }
1157            TypeKind::PtrConst(id) => {
1158                let pointee = self.ptr_const_def(id);
1159                format!("ptr const {}", self.format_type_name(pointee))
1160            }
1161            TypeKind::PtrMut(id) => {
1162                let pointee = self.ptr_mut_def(id);
1163                format!("ptr mut {}", self.format_type_name(pointee))
1164            }
1165            TypeKind::Module(_) => "<module>".to_string(),
1166        }
1167    }
1168}
1169
1170impl Default for TypeInternPool {
1171    fn default() -> Self {
1172        Self::new()
1173    }
1174}
1175
1176impl Clone for TypeInternPool {
1177    /// Clone the pool by copying all type data into a new pool.
1178    ///
1179    /// This is used when building `SemaContext` from `Sema`, as the context
1180    /// needs its own copy of the pool for thread-safe sharing.
1181    fn clone(&self) -> Self {
1182        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1183        Self {
1184            inner: RwLock::new(TypeInternPoolInner {
1185                types: inner.types.clone(),
1186                array_map: inner.array_map.clone(),
1187                ptr_const_map: inner.ptr_const_map.clone(),
1188                ptr_mut_map: inner.ptr_mut_map.clone(),
1189                struct_by_name: inner.struct_by_name.clone(),
1190                enum_by_name: inner.enum_by_name.clone(),
1191            }),
1192        }
1193    }
1194}
1195
1196/// Statistics about the intern pool contents.
1197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1198pub struct TypeInternPoolStats {
1199    pub struct_count: usize,
1200    pub enum_count: usize,
1201    pub array_count: usize,
1202    pub total: usize,
1203}
1204
1205#[cfg(test)]
1206mod tests {
1207    use super::*;
1208    use crate::types::EnumVariantDef;
1209    use lasso::ThreadedRodeo;
1210
1211    // ========================================================================
1212    // InternedType tests
1213    // ========================================================================
1214
1215    #[test]
1216    fn test_interned_type_primitives() {
1217        assert!(InternedType::I8.is_primitive());
1218        assert!(InternedType::I16.is_primitive());
1219        assert!(InternedType::I32.is_primitive());
1220        assert!(InternedType::I64.is_primitive());
1221        assert!(InternedType::U8.is_primitive());
1222        assert!(InternedType::U16.is_primitive());
1223        assert!(InternedType::U32.is_primitive());
1224        assert!(InternedType::U64.is_primitive());
1225        assert!(InternedType::BOOL.is_primitive());
1226        assert!(InternedType::UNIT.is_primitive());
1227        assert!(InternedType::NEVER.is_primitive());
1228        assert!(InternedType::ERROR.is_primitive());
1229    }
1230
1231    #[test]
1232    fn test_interned_type_indices() {
1233        assert_eq!(InternedType::I8.index(), 0);
1234        assert_eq!(InternedType::I16.index(), 1);
1235        assert_eq!(InternedType::I32.index(), 2);
1236        assert_eq!(InternedType::I64.index(), 3);
1237        assert_eq!(InternedType::U8.index(), 4);
1238        assert_eq!(InternedType::ISIZE.index(), 8);
1239        assert_eq!(InternedType::USIZE.index(), 9);
1240        assert_eq!(InternedType::F16.index(), 10);
1241        assert_eq!(InternedType::F32.index(), 11);
1242        assert_eq!(InternedType::F64.index(), 12);
1243        assert_eq!(InternedType::BOOL.index(), 13);
1244        assert_eq!(InternedType::UNIT.index(), 14);
1245    }
1246
1247    #[test]
1248    fn test_interned_type_pool_index() {
1249        // Primitives don't have pool indices
1250        assert_eq!(InternedType::I32.pool_index(), None);
1251        assert_eq!(InternedType::BOOL.pool_index(), None);
1252
1253        // Composite types have pool indices
1254        let composite = InternedType::from_pool_index(0);
1255        assert_eq!(composite.pool_index(), Some(0));
1256        assert!(!composite.is_primitive());
1257
1258        let composite2 = InternedType::from_pool_index(42);
1259        assert_eq!(composite2.pool_index(), Some(42));
1260    }
1261
1262    #[test]
1263    fn test_interned_type_equality() {
1264        assert_eq!(InternedType::I32, InternedType::I32);
1265        assert_ne!(InternedType::I32, InternedType::I64);
1266        assert_ne!(InternedType::I32, InternedType::from_pool_index(0));
1267    }
1268
1269    #[test]
1270    fn test_interned_type_debug() {
1271        let i32_str = format!("{:?}", InternedType::I32);
1272        assert!(i32_str.contains("i32"));
1273
1274        let composite_str = format!("{:?}", InternedType::from_pool_index(5));
1275        assert!(composite_str.contains("pool:5"));
1276    }
1277
1278    // ========================================================================
1279    // TypeInternPool tests
1280    // ========================================================================
1281
1282    #[test]
1283    fn test_pool_new() {
1284        let pool = TypeInternPool::new();
1285        assert!(pool.is_empty());
1286        assert_eq!(pool.len(), 0);
1287    }
1288
1289    #[test]
1290    fn test_pool_register_struct() {
1291        let pool = TypeInternPool::new();
1292        let interner = ThreadedRodeo::default();
1293        let name = interner.get_or_intern("Point");
1294
1295        let def = StructDef {
1296            name: "Point".to_string(),
1297            fields: vec![],
1298            is_copy: false,
1299            is_handle: false,
1300            is_linear: false,
1301            destructor: None,
1302            is_builtin: false,
1303            is_pub: false,
1304            file_id: gruel_span::FileId::DEFAULT,
1305        };
1306
1307        let (struct_id, is_new) = pool.register_struct(name, def.clone());
1308        assert!(is_new);
1309        assert_eq!(struct_id.pool_index(), 0); // First entry in pool
1310        assert_eq!(pool.len(), 1);
1311
1312        // Registering the same name returns the existing type
1313        let (struct_id2, is_new2) = pool.register_struct(name, def);
1314        assert!(!is_new2);
1315        assert_eq!(struct_id, struct_id2);
1316        assert_eq!(pool.len(), 1); // No new type added
1317    }
1318
1319    #[test]
1320    fn test_pool_register_enum() {
1321        let pool = TypeInternPool::new();
1322        let interner = ThreadedRodeo::default();
1323        let name = interner.get_or_intern("Color");
1324
1325        let def = EnumDef {
1326            name: "Color".to_string(),
1327            variants: vec![
1328                EnumVariantDef::unit("Red"),
1329                EnumVariantDef::unit("Green"),
1330                EnumVariantDef::unit("Blue"),
1331            ],
1332            is_pub: false,
1333            file_id: gruel_span::FileId::DEFAULT,
1334        };
1335
1336        let (enum_id, is_new) = pool.register_enum(name, def.clone());
1337        assert!(is_new);
1338        assert_eq!(enum_id.pool_index(), 0); // First entry in pool
1339        assert_eq!(pool.len(), 1);
1340
1341        // Registering the same name returns the existing type
1342        let (enum_id2, is_new2) = pool.register_enum(name, def);
1343        assert!(!is_new2);
1344        assert_eq!(enum_id, enum_id2);
1345    }
1346
1347    #[test]
1348    fn test_pool_intern_array() {
1349        let pool = TypeInternPool::new();
1350
1351        // Intern [i32; 5]
1352        let arr1 = pool.intern_array(InternedType::I32, 5);
1353        assert!(!arr1.is_primitive());
1354        assert_eq!(pool.len(), 1);
1355
1356        // Interning the same array returns the same type
1357        let arr2 = pool.intern_array(InternedType::I32, 5);
1358        assert_eq!(arr1, arr2);
1359        assert_eq!(pool.len(), 1);
1360
1361        // Different length is a different type
1362        let arr3 = pool.intern_array(InternedType::I32, 10);
1363        assert_ne!(arr1, arr3);
1364        assert_eq!(pool.len(), 2);
1365
1366        // Different element type is a different type
1367        let arr4 = pool.intern_array(InternedType::I64, 5);
1368        assert_ne!(arr1, arr4);
1369        assert_eq!(pool.len(), 3);
1370    }
1371
1372    #[test]
1373    fn test_pool_get_struct_by_name() {
1374        let pool = TypeInternPool::new();
1375        let interner = ThreadedRodeo::default();
1376        let name = interner.get_or_intern("Point");
1377
1378        assert!(pool.get_struct_by_name(name).is_none());
1379
1380        let def = StructDef {
1381            name: "Point".to_string(),
1382            fields: vec![],
1383            is_copy: false,
1384            is_handle: false,
1385            is_linear: false,
1386            destructor: None,
1387            is_builtin: false,
1388            is_pub: false,
1389            file_id: gruel_span::FileId::DEFAULT,
1390        };
1391
1392        let (struct_id, _) = pool.register_struct(name, def);
1393        // get_struct_by_name returns InternedType, convert StructId for comparison
1394        let expected = pool.struct_id_to_interned(struct_id);
1395        assert_eq!(pool.get_struct_by_name(name), Some(expected));
1396    }
1397
1398    #[test]
1399    fn test_pool_get_enum_by_name() {
1400        let pool = TypeInternPool::new();
1401        let interner = ThreadedRodeo::default();
1402        let name = interner.get_or_intern("Status");
1403
1404        assert!(pool.get_enum_by_name(name).is_none());
1405
1406        let def = EnumDef {
1407            name: "Status".to_string(),
1408            variants: vec![
1409                EnumVariantDef::unit("Active"),
1410                EnumVariantDef::unit("Inactive"),
1411            ],
1412            is_pub: false,
1413            file_id: gruel_span::FileId::DEFAULT,
1414        };
1415
1416        let (enum_id, _) = pool.register_enum(name, def);
1417        // get_enum_by_name returns InternedType, convert EnumId for comparison
1418        let expected = pool.enum_id_to_interned(enum_id);
1419        assert_eq!(pool.get_enum_by_name(name), Some(expected));
1420    }
1421
1422    #[test]
1423    fn test_pool_get_array() {
1424        let pool = TypeInternPool::new();
1425
1426        assert!(pool.get_array(InternedType::I32, 5).is_none());
1427
1428        let arr = pool.intern_array(InternedType::I32, 5);
1429        assert_eq!(pool.get_array(InternedType::I32, 5), Some(arr));
1430        assert!(pool.get_array(InternedType::I32, 10).is_none());
1431    }
1432
1433    #[test]
1434    fn test_pool_get_type_data() {
1435        let pool = TypeInternPool::new();
1436        let interner = ThreadedRodeo::default();
1437
1438        // Primitive types return None
1439        assert!(pool.get(InternedType::I32).is_none());
1440
1441        // Register a struct
1442        let struct_name = interner.get_or_intern("Point");
1443        let struct_def = StructDef {
1444            name: "Point".to_string(),
1445            fields: vec![],
1446            is_copy: false,
1447            is_handle: false,
1448            is_linear: false,
1449            destructor: None,
1450            is_builtin: false,
1451            is_pub: false,
1452            file_id: gruel_span::FileId::DEFAULT,
1453        };
1454        let (struct_id, _) = pool.register_struct(struct_name, struct_def);
1455        let struct_ty = pool.struct_id_to_interned(struct_id);
1456
1457        // Get struct data
1458        let data = pool.get(struct_ty).expect("should get struct data");
1459        assert!(matches!(data, TypeData::Struct(_)));
1460
1461        // Intern an array
1462        let arr_ty = pool.intern_array(InternedType::I32, 10);
1463        let arr_data = pool.get(arr_ty).expect("should get array data");
1464        match arr_data {
1465            TypeData::Array { element, len } => {
1466                assert_eq!(element, InternedType::I32);
1467                assert_eq!(len, 10);
1468            }
1469            _ => panic!("expected array data"),
1470        }
1471    }
1472
1473    #[test]
1474    fn test_pool_type_checks() {
1475        let pool = TypeInternPool::new();
1476        let interner = ThreadedRodeo::default();
1477
1478        let struct_name = interner.get_or_intern("Point");
1479        let struct_def = StructDef {
1480            name: "Point".to_string(),
1481            fields: vec![],
1482            is_copy: false,
1483            is_handle: false,
1484            is_linear: false,
1485            destructor: None,
1486            is_builtin: false,
1487            is_pub: false,
1488            file_id: gruel_span::FileId::DEFAULT,
1489        };
1490        let (struct_id, _) = pool.register_struct(struct_name, struct_def);
1491        let struct_ty = pool.struct_id_to_interned(struct_id);
1492
1493        let enum_name = interner.get_or_intern("Color");
1494        let enum_def = EnumDef {
1495            name: "Color".to_string(),
1496            variants: vec![EnumVariantDef::unit("Red")],
1497            is_pub: false,
1498            file_id: gruel_span::FileId::DEFAULT,
1499        };
1500        let (enum_id, _) = pool.register_enum(enum_name, enum_def);
1501        let enum_ty = pool.enum_id_to_interned(enum_id);
1502
1503        let array_ty = pool.intern_array(InternedType::I32, 5);
1504
1505        // Check is_struct
1506        assert!(pool.is_struct(struct_ty));
1507        assert!(!pool.is_struct(enum_ty));
1508        assert!(!pool.is_struct(array_ty));
1509        assert!(!pool.is_struct(InternedType::I32));
1510
1511        // Check is_enum
1512        assert!(!pool.is_enum(struct_ty));
1513        assert!(pool.is_enum(enum_ty));
1514        assert!(!pool.is_enum(array_ty));
1515        assert!(!pool.is_enum(InternedType::I32));
1516
1517        // Check is_array
1518        assert!(!pool.is_array(struct_ty));
1519        assert!(!pool.is_array(enum_ty));
1520        assert!(pool.is_array(array_ty));
1521        assert!(!pool.is_array(InternedType::I32));
1522    }
1523
1524    #[test]
1525    fn test_pool_get_struct_def() {
1526        let pool = TypeInternPool::new();
1527        let interner = ThreadedRodeo::default();
1528
1529        let name = interner.get_or_intern("Point");
1530        let def = StructDef {
1531            name: "Point".to_string(),
1532            fields: vec![],
1533            is_copy: true,
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        let (struct_id, _) = pool.register_struct(name, def.clone());
1542
1543        // Test the new Phase 3 struct_def() method that takes StructId directly
1544        let retrieved = pool.struct_def(struct_id);
1545        assert_eq!(retrieved.name, def.name);
1546        assert_eq!(retrieved.is_copy, def.is_copy);
1547
1548        // Test the old get_struct_def() that takes InternedType
1549        let interned = pool.struct_id_to_interned(struct_id);
1550        let retrieved2 = pool
1551            .get_struct_def(interned)
1552            .expect("should get struct def");
1553        assert_eq!(retrieved2.name, def.name);
1554
1555        // Non-struct returns None for get_struct_def
1556        let array_ty = pool.intern_array(InternedType::I32, 5);
1557        assert!(pool.get_struct_def(array_ty).is_none());
1558        assert!(pool.get_struct_def(InternedType::I32).is_none());
1559    }
1560
1561    #[test]
1562    fn test_pool_get_enum_def() {
1563        let pool = TypeInternPool::new();
1564        let interner = ThreadedRodeo::default();
1565
1566        let name = interner.get_or_intern("Status");
1567        let def = EnumDef {
1568            name: "Status".to_string(),
1569            variants: vec![EnumVariantDef::unit("A"), EnumVariantDef::unit("B")],
1570            is_pub: false,
1571            file_id: gruel_span::FileId::DEFAULT,
1572        };
1573        let (enum_id, _) = pool.register_enum(name, def.clone());
1574
1575        // Test the new Phase 3 enum_def() method that takes EnumId directly
1576        let retrieved = pool.enum_def(enum_id);
1577        assert_eq!(retrieved.name, def.name);
1578        assert_eq!(retrieved.variants.len(), 2);
1579
1580        // Test the old get_enum_def() that takes InternedType
1581        let interned = pool.enum_id_to_interned(enum_id);
1582        let retrieved2 = pool.get_enum_def(interned).expect("should get enum def");
1583        assert_eq!(retrieved2.name, def.name);
1584
1585        // Non-enum returns None for get_enum_def
1586        let array_ty = pool.intern_array(InternedType::I32, 5);
1587        assert!(pool.get_enum_def(array_ty).is_none());
1588        assert!(pool.get_enum_def(InternedType::I32).is_none());
1589    }
1590
1591    #[test]
1592    fn test_pool_get_array_info() {
1593        let pool = TypeInternPool::new();
1594
1595        let array_ty = pool.intern_array(InternedType::I64, 100);
1596        let (element, len) = pool
1597            .get_array_info(array_ty)
1598            .expect("should get array info");
1599        assert_eq!(element, InternedType::I64);
1600        assert_eq!(len, 100);
1601
1602        // Non-array returns None
1603        let interner = ThreadedRodeo::default();
1604        let name = interner.get_or_intern("X");
1605        let def = StructDef {
1606            name: "X".to_string(),
1607            fields: vec![],
1608            is_copy: false,
1609            is_handle: false,
1610            is_linear: false,
1611            destructor: None,
1612            is_builtin: false,
1613            is_pub: false,
1614            file_id: gruel_span::FileId::DEFAULT,
1615        };
1616        let (struct_id, _) = pool.register_struct(name, def);
1617        let struct_ty = pool.struct_id_to_interned(struct_id);
1618        assert!(pool.get_array_info(struct_ty).is_none());
1619        assert!(pool.get_array_info(InternedType::I32).is_none());
1620    }
1621
1622    #[test]
1623    fn test_pool_stats() {
1624        let pool = TypeInternPool::new();
1625        let interner = ThreadedRodeo::default();
1626
1627        let stats = pool.stats();
1628        assert_eq!(stats.struct_count, 0);
1629        assert_eq!(stats.enum_count, 0);
1630        assert_eq!(stats.array_count, 0);
1631        assert_eq!(stats.total, 0);
1632
1633        // Add some types
1634        let s1 = interner.get_or_intern("S1");
1635        let s2 = interner.get_or_intern("S2");
1636        let e1 = interner.get_or_intern("E1");
1637
1638        let def = StructDef {
1639            name: "S1".to_string(),
1640            fields: vec![],
1641            is_copy: false,
1642            is_handle: false,
1643            is_linear: false,
1644            destructor: None,
1645            is_builtin: false,
1646            is_pub: false,
1647            file_id: gruel_span::FileId::DEFAULT,
1648        };
1649        pool.register_struct(s1, def.clone());
1650        pool.register_struct(
1651            s2,
1652            StructDef {
1653                name: "S2".to_string(),
1654                ..def
1655            },
1656        );
1657
1658        pool.register_enum(
1659            e1,
1660            EnumDef {
1661                name: "E1".to_string(),
1662                variants: vec![],
1663                is_pub: false,
1664                file_id: gruel_span::FileId::DEFAULT,
1665            },
1666        );
1667
1668        pool.intern_array(InternedType::I32, 5);
1669        pool.intern_array(InternedType::I32, 10);
1670        pool.intern_array(InternedType::BOOL, 3);
1671
1672        let stats = pool.stats();
1673        assert_eq!(stats.struct_count, 2);
1674        assert_eq!(stats.enum_count, 1);
1675        assert_eq!(stats.array_count, 3);
1676        assert_eq!(stats.total, 6);
1677    }
1678
1679    #[test]
1680    fn test_pool_nested_arrays() {
1681        let pool = TypeInternPool::new();
1682
1683        // Create [i32; 3]
1684        let inner = pool.intern_array(InternedType::I32, 3);
1685
1686        // Create [[i32; 3]; 4]
1687        let outer = pool.intern_array(inner, 4);
1688
1689        // Verify structure
1690        let (outer_elem, outer_len) = pool.get_array_info(outer).expect("outer array info");
1691        assert_eq!(outer_elem, inner);
1692        assert_eq!(outer_len, 4);
1693
1694        let (inner_elem, inner_len) = pool.get_array_info(inner).expect("inner array info");
1695        assert_eq!(inner_elem, InternedType::I32);
1696        assert_eq!(inner_len, 3);
1697    }
1698
1699    #[test]
1700    fn test_pool_type_to_interned() {
1701        let pool = TypeInternPool::new();
1702
1703        // Primitive types convert correctly
1704        assert_eq!(pool.type_to_interned(Type::I8), Some(InternedType::I8));
1705        assert_eq!(pool.type_to_interned(Type::I16), Some(InternedType::I16));
1706        assert_eq!(pool.type_to_interned(Type::I32), Some(InternedType::I32));
1707        assert_eq!(pool.type_to_interned(Type::I64), Some(InternedType::I64));
1708        assert_eq!(pool.type_to_interned(Type::U8), Some(InternedType::U8));
1709        assert_eq!(pool.type_to_interned(Type::U16), Some(InternedType::U16));
1710        assert_eq!(pool.type_to_interned(Type::U32), Some(InternedType::U32));
1711        assert_eq!(pool.type_to_interned(Type::U64), Some(InternedType::U64));
1712        assert_eq!(pool.type_to_interned(Type::BOOL), Some(InternedType::BOOL));
1713        assert_eq!(pool.type_to_interned(Type::UNIT), Some(InternedType::UNIT));
1714        assert_eq!(
1715            pool.type_to_interned(Type::NEVER),
1716            Some(InternedType::NEVER)
1717        );
1718        assert_eq!(
1719            pool.type_to_interned(Type::ERROR),
1720            Some(InternedType::ERROR)
1721        );
1722
1723        // Composite types return None (need name lookup)
1724        assert!(
1725            pool.type_to_interned(Type::new_struct(crate::types::StructId(0)))
1726                .is_none()
1727        );
1728        assert!(
1729            pool.type_to_interned(Type::new_enum(crate::types::EnumId(0)))
1730                .is_none()
1731        );
1732        assert!(
1733            pool.type_to_interned(Type::new_array(crate::types::ArrayTypeId(0)))
1734                .is_none()
1735        );
1736    }
1737
1738    #[test]
1739    fn test_pool_interned_to_type() {
1740        let pool = TypeInternPool::new();
1741
1742        // Primitive types convert back correctly
1743        assert_eq!(pool.interned_to_type(InternedType::I8), Some(Type::I8));
1744        assert_eq!(pool.interned_to_type(InternedType::I16), Some(Type::I16));
1745        assert_eq!(pool.interned_to_type(InternedType::I32), Some(Type::I32));
1746        assert_eq!(pool.interned_to_type(InternedType::I64), Some(Type::I64));
1747        assert_eq!(pool.interned_to_type(InternedType::U8), Some(Type::U8));
1748        assert_eq!(pool.interned_to_type(InternedType::U16), Some(Type::U16));
1749        assert_eq!(pool.interned_to_type(InternedType::U32), Some(Type::U32));
1750        assert_eq!(pool.interned_to_type(InternedType::U64), Some(Type::U64));
1751        assert_eq!(pool.interned_to_type(InternedType::BOOL), Some(Type::BOOL));
1752        assert_eq!(pool.interned_to_type(InternedType::UNIT), Some(Type::UNIT));
1753        assert_eq!(
1754            pool.interned_to_type(InternedType::NEVER),
1755            Some(Type::NEVER)
1756        );
1757        assert_eq!(
1758            pool.interned_to_type(InternedType::ERROR),
1759            Some(Type::ERROR)
1760        );
1761
1762        // Composite types return None
1763        assert!(
1764            pool.interned_to_type(InternedType::from_pool_index(0))
1765                .is_none()
1766        );
1767    }
1768
1769    // ========================================================================
1770    // Thread safety tests
1771    // ========================================================================
1772
1773    #[test]
1774    fn test_pool_concurrent_access() {
1775        use std::sync::Arc;
1776        use std::thread;
1777
1778        let pool = Arc::new(TypeInternPool::new());
1779        let interner = Arc::new(ThreadedRodeo::default());
1780
1781        // Pre-register names for thread safety
1782        let names: Vec<Spur> = (0..100)
1783            .map(|i| interner.get_or_intern(format!("Type{}", i)))
1784            .collect();
1785
1786        let handles: Vec<_> = (0..10)
1787            .map(|thread_id| {
1788                let pool = Arc::clone(&pool);
1789                let names = names.clone();
1790                thread::spawn(move || {
1791                    // Each thread registers 10 types
1792                    for i in 0..10 {
1793                        let idx = thread_id * 10 + i;
1794                        let name = names[idx];
1795                        let def = StructDef {
1796                            name: format!("Type{}", idx),
1797                            fields: vec![],
1798                            is_copy: false,
1799                            is_handle: false,
1800                            is_linear: false,
1801                            destructor: None,
1802                            is_builtin: false,
1803                            is_pub: false,
1804                            file_id: gruel_span::FileId::DEFAULT,
1805                        };
1806                        pool.register_struct(name, def);
1807                    }
1808                })
1809            })
1810            .collect();
1811
1812        for handle in handles {
1813            handle.join().expect("thread panicked");
1814        }
1815
1816        // All 100 types should be registered
1817        assert_eq!(pool.len(), 100);
1818
1819        // Each name should map to a valid type
1820        for name in &names {
1821            assert!(pool.get_struct_by_name(*name).is_some());
1822        }
1823    }
1824
1825    #[test]
1826    fn test_pool_concurrent_array_interning() {
1827        use std::sync::Arc;
1828        use std::thread;
1829
1830        let pool = Arc::new(TypeInternPool::new());
1831
1832        // Multiple threads try to intern the same array type
1833        let handles: Vec<_> = (0..10)
1834            .map(|_| {
1835                let pool = Arc::clone(&pool);
1836                thread::spawn(move || pool.intern_array(InternedType::I32, 42))
1837            })
1838            .collect();
1839
1840        let results: Vec<_> = handles
1841            .into_iter()
1842            .map(|h| h.join().expect("thread panicked"))
1843            .collect();
1844
1845        // All threads should get the same type
1846        let first = results[0];
1847        for result in &results {
1848            assert_eq!(*result, first);
1849        }
1850
1851        // Only one array type should be in the pool
1852        assert_eq!(pool.stats().array_count, 1);
1853    }
1854
1855    // ========================================================================
1856    // Struct ID reservation tests
1857    // ========================================================================
1858
1859    #[test]
1860    fn test_pool_reserve_and_complete_struct() {
1861        let pool = TypeInternPool::new();
1862        let interner = ThreadedRodeo::default();
1863
1864        // Reserve an ID
1865        let struct_id = pool.reserve_struct_id();
1866        assert_eq!(struct_id.pool_index(), 0);
1867        assert_eq!(pool.len(), 1); // Placeholder was pushed
1868
1869        // Use the ID to create a name
1870        let name_str = format!("__anon_struct_{}", struct_id.0);
1871        let name = interner.get_or_intern(&name_str);
1872
1873        let def = StructDef {
1874            name: name_str.clone(),
1875            fields: vec![],
1876            is_copy: false,
1877            is_handle: false,
1878            is_linear: false,
1879            destructor: None,
1880            is_builtin: false,
1881            is_pub: false,
1882            file_id: gruel_span::FileId::DEFAULT,
1883        };
1884
1885        // Complete registration
1886        pool.complete_struct_registration(struct_id, name, def);
1887
1888        // Verify registration succeeded
1889        assert_eq!(pool.len(), 1); // No new entry, just updated
1890        assert!(pool.get_struct_by_name(name).is_some());
1891
1892        // Can retrieve the struct definition
1893        let retrieved = pool.struct_def(struct_id);
1894        assert_eq!(retrieved.name, name_str);
1895    }
1896
1897    #[test]
1898    fn test_pool_reserve_multiple_structs() {
1899        let pool = TypeInternPool::new();
1900        let interner = ThreadedRodeo::default();
1901
1902        // Reserve multiple IDs
1903        let id1 = pool.reserve_struct_id();
1904        let id2 = pool.reserve_struct_id();
1905        let id3 = pool.reserve_struct_id();
1906
1907        assert_eq!(id1.pool_index(), 0);
1908        assert_eq!(id2.pool_index(), 1);
1909        assert_eq!(id3.pool_index(), 2);
1910        assert_eq!(pool.len(), 3);
1911
1912        // Complete them in any order (here: reverse)
1913        for (i, id) in [(2, id3), (1, id2), (0, id1)] {
1914            let name_str = format!("__anon_struct_{}", i);
1915            let name = interner.get_or_intern(&name_str);
1916            let def = StructDef {
1917                name: name_str,
1918                fields: vec![],
1919                is_copy: false,
1920                is_handle: false,
1921                is_linear: false,
1922                destructor: None,
1923                is_builtin: false,
1924                is_pub: false,
1925                file_id: gruel_span::FileId::DEFAULT,
1926            };
1927            pool.complete_struct_registration(id, name, def);
1928        }
1929
1930        // All three should be registered
1931        assert_eq!(pool.stats().struct_count, 3);
1932    }
1933
1934    // Compile-time assertion that TypeInternPool is Send + Sync
1935    fn assert_send_sync<T: Send + Sync>() {}
1936
1937    #[test]
1938    fn test_pool_is_send_sync() {
1939        assert_send_sync::<TypeInternPool>();
1940    }
1941}