Skip to main content

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 rustc_hash::FxHashMap as HashMap;
39use std::sync::{PoisonError, RwLock};
40
41use gruel_builtins::Posture;
42use lasso::Spur;
43
44use crate::layout::Layout;
45use crate::types::{
46    ArrayTypeId, EnumDef, EnumId, MutRefTypeId, MutSliceTypeId, PtrConstTypeId, PtrMutTypeId,
47    RefTypeId, SliceTypeId, StructDef, StructId, Type, TypeKind, VecTypeId,
48};
49
50/// Interned type index - 32 bits, Copy, cheap comparison.
51///
52/// Reserved indices 0-15 are primitives (no lookup needed).
53/// Index 16+ are composite types stored in the pool.
54///
55/// # Primitive Encoding
56///
57/// The following indices are reserved for primitive types:
58/// - 0: i8
59/// - 1: i16
60/// - 2: i32
61/// - 3: i64
62/// - 4: u8
63/// - 5: u16
64/// - 6: u32
65/// - 7: u64
66/// - 8: isize
67/// - 9: usize
68/// - 10: f16
69/// - 11: f32
70/// - 12: f64
71/// - 13: bool
72/// - 14: unit
73/// - 15: never
74/// - 16: error
75/// - 17-18: reserved for future primitives
76#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
77pub struct InternedType(u32);
78
79impl InternedType {
80    // Reserved indices for primitives
81    pub const I8: InternedType = InternedType(0);
82    pub const I16: InternedType = InternedType(1);
83    pub const I32: InternedType = InternedType(2);
84    pub const I64: InternedType = InternedType(3);
85    pub const U8: InternedType = InternedType(4);
86    pub const U16: InternedType = InternedType(5);
87    pub const U32: InternedType = InternedType(6);
88    pub const U64: InternedType = InternedType(7);
89    pub const ISIZE: InternedType = InternedType(8);
90    pub const USIZE: InternedType = InternedType(9);
91    pub const F16: InternedType = InternedType(10);
92    pub const F32: InternedType = InternedType(11);
93    pub const F64: InternedType = InternedType(12);
94    pub const BOOL: InternedType = InternedType(13);
95    pub const UNIT: InternedType = InternedType(14);
96    pub const NEVER: InternedType = InternedType(15);
97    pub const ERROR: InternedType = InternedType(16);
98    /// ADR-0071: Unicode scalar value (`char`).
99    pub const CHAR: InternedType = InternedType(20);
100
101    // ADR-0086: C named primitive types. Slot numbers match the `Type` tag
102    // encoding for these variants (21-33).
103    pub const C_SCHAR: InternedType = InternedType(21);
104    pub const C_SHORT: InternedType = InternedType(22);
105    pub const C_INT: InternedType = InternedType(23);
106    pub const C_LONG: InternedType = InternedType(24);
107    pub const C_LONGLONG: InternedType = InternedType(25);
108    pub const C_UCHAR: InternedType = InternedType(26);
109    pub const C_USHORT: InternedType = InternedType(27);
110    pub const C_UINT: InternedType = InternedType(28);
111    pub const C_ULONG: InternedType = InternedType(29);
112    pub const C_ULONGLONG: InternedType = InternedType(30);
113    pub const C_FLOAT: InternedType = InternedType(31);
114    pub const C_DOUBLE: InternedType = InternedType(32);
115    pub const C_VOID: InternedType = InternedType(33);
116
117    const PRIMITIVE_COUNT: u32 = 34;
118
119    /// Check if this is a primitive type (no pool lookup needed).
120    #[inline]
121    pub fn is_primitive(self) -> bool {
122        self.0 < Self::PRIMITIVE_COUNT
123    }
124
125    /// Get the raw index value.
126    #[inline]
127    pub fn index(self) -> u32 {
128        self.0
129    }
130
131    /// Create an InternedType from a raw index.
132    ///
133    /// # Safety
134    ///
135    /// The caller must ensure the index is valid (either a primitive index 0-15,
136    /// or a composite index that exists in the pool).
137    #[inline]
138    pub fn from_raw(index: u32) -> Self {
139        InternedType(index)
140    }
141
142    /// Create an InternedType for a composite type from its pool index.
143    ///
144    /// The pool index is offset by `PRIMITIVE_COUNT` to produce the final index.
145    #[inline]
146    fn from_pool_index(pool_index: u32) -> Self {
147        InternedType(pool_index + Self::PRIMITIVE_COUNT)
148    }
149
150    /// Get the pool index for a composite type.
151    ///
152    /// Returns `None` for primitive types.
153    #[inline]
154    pub fn pool_index(self) -> Option<u32> {
155        if self.is_primitive() {
156            None
157        } else {
158            Some(self.0 - Self::PRIMITIVE_COUNT)
159        }
160    }
161}
162
163impl std::fmt::Debug for InternedType {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        if self.is_primitive() {
166            let name = match self.0 {
167                0 => "i8",
168                1 => "i16",
169                2 => "i32",
170                3 => "i64",
171                4 => "u8",
172                5 => "u16",
173                6 => "u32",
174                7 => "u64",
175                8 => "bool",
176                9 => "()",
177                10 => "!",
178                11 => "<error>",
179                _ => "<reserved>",
180            };
181            write!(f, "InternedType({name})")
182        } else {
183            write!(f, "InternedType(pool:{})", self.0 - Self::PRIMITIVE_COUNT)
184        }
185    }
186}
187
188/// Type data stored in the intern pool.
189///
190/// This is NOT Copy - it lives in the pool. You work with `InternedType` indices.
191///
192/// # Type Categories
193///
194/// - **Struct** and **Enum** are nominal types: identity comes from the name
195/// - **Array**, **PtrConst**, and **PtrMut** are structural types: identity comes from element/pointee type
196#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
197pub enum TypeData {
198    /// User-defined struct (nominal type).
199    ///
200    /// Two structs with the same fields but different names are different types.
201    Struct(StructData),
202
203    /// User-defined enum (nominal type).
204    ///
205    /// Two enums with the same variants but different names are different types.
206    Enum(EnumData),
207
208    /// Fixed-size array (structural type).
209    ///
210    /// Arrays with the same element type and length are the same type,
211    /// regardless of where they were defined.
212    Array { element: InternedType, len: u64 },
213
214    /// Raw const pointer (structural type).
215    ///
216    /// `ptr const T` - pointer to immutable data.
217    PtrConst { pointee: InternedType },
218
219    /// Raw mut pointer (structural type).
220    ///
221    /// `ptr mut T` - pointer to mutable data.
222    PtrMut { pointee: InternedType },
223
224    /// Immutable reference (structural type, ADR-0062).
225    ///
226    /// `Ref(T)` - scope-bound non-mutating borrow.
227    Ref { referent: InternedType },
228
229    /// Mutable reference (structural type, ADR-0062).
230    ///
231    /// `MutRef(T)` - scope-bound exclusive mutating borrow.
232    MutRef { referent: InternedType },
233
234    /// Immutable slice (structural type, ADR-0064).
235    ///
236    /// `Slice(T)` - scope-bound fat pointer `{ptr, len}`.
237    Slice { element: InternedType },
238
239    /// Mutable slice (structural type, ADR-0064).
240    ///
241    /// `MutSlice(T)` - scope-bound exclusive fat pointer `{ptr, len}`.
242    MutSlice { element: InternedType },
243
244    /// Owned, growable vector (structural type, ADR-0066).
245    ///
246    /// `Vec(T)` - heap-allocated `{ptr, len, cap}`.
247    Vec { element: InternedType },
248}
249
250/// Data for a struct type in the intern pool.
251///
252/// During Phase 1, this mirrors the existing `StructDef` to verify correctness.
253/// In later phases, `StructDef` will be replaced by this.
254#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
255pub struct StructData {
256    /// The name symbol (interned string).
257    pub name: Spur,
258    /// Reference to the full struct definition.
259    /// During Phase 1, we keep a clone of the StructDef for verification.
260    /// In later phases, the pool will be the canonical source.
261    pub def: StructDef,
262}
263
264/// Data for an enum type in the intern pool.
265///
266/// During Phase 1, this mirrors the existing `EnumDef` to verify correctness.
267/// In later phases, `EnumDef` will be replaced by this.
268#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
269pub struct EnumData {
270    /// The name symbol (interned string).
271    pub name: Spur,
272    /// Reference to the full enum definition.
273    /// During Phase 1, we keep a clone of the EnumDef for verification.
274    /// In later phases, the pool will be the canonical source.
275    pub def: EnumDef,
276}
277
278/// Thread-safe intern pool for all composite types.
279///
280/// The pool is designed to be built during declaration gathering (sequential)
281/// and then queried during function body analysis (potentially parallel).
282///
283/// # Thread Safety
284///
285/// Uses `RwLock` for interior mutability:
286/// - Read lock for lookups (most common)
287/// - Write lock for insertions (only during declaration gathering)
288///
289/// # Usage
290///
291/// ```ignore
292/// let pool = TypeInternPool::new();
293///
294/// // Register nominal types (structs/enums)
295/// let (struct_type, is_new) = pool.register_struct(name_spur, struct_def);
296///
297/// // Intern structural types (arrays)
298/// let array_type = pool.intern_array(element_type, 10);
299///
300/// // Look up type data
301/// if let Some(data) = pool.try_get(some_type) {
302///     match data {
303///         TypeData::Struct(s) => println!("struct {}", s.def.name),
304///         TypeData::Enum(e) => println!("enum {}", e.def.name),
305///         TypeData::Array { element, len } => println!("array of {:?}; {}", element, len),
306///     }
307/// }
308/// ```
309#[derive(Debug)]
310pub struct TypeInternPool {
311    inner: RwLock<TypeInternPoolInner>,
312}
313
314// ADR-0074 Phase 4: serialize / deserialize TypeInternPool by snapshotting
315// only its canonical `types: Vec<TypeData>`. The structural-dedup HashMaps
316// (array_map, ptr_const_map, etc.) are reconstructed from `types` on load
317// because they are pure caches over its contents. The lazy layout_cache
318// starts empty after deserialization and re-populates on demand.
319//
320// IMPORTANT: cached InternedType values index into `types` and are stable
321// only against the snapshotted pool. Loading a pool replaces — does not
322// merge — the build's TypeInternPool. Any cross-file use of cached AIR
323// requires a TypeId remap walker (Phase 4 follow-up).
324#[derive(serde::Serialize, serde::Deserialize)]
325struct TypeInternPoolWire {
326    types: Vec<TypeData>,
327}
328
329impl serde::Serialize for TypeInternPool {
330    fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
331        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
332        let wire = TypeInternPoolWire {
333            types: inner.types.clone(),
334        };
335        wire.serialize(ser)
336    }
337}
338
339impl<'de> serde::Deserialize<'de> for TypeInternPool {
340    fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
341        let wire = TypeInternPoolWire::deserialize(de)?;
342        // Reconstruct the dedup maps by walking `types` in order. Each
343        // TypeData index becomes the InternedType key (offset by the
344        // primitive count via try_from_index, identical to the original
345        // intern path).
346        let mut inner = TypeInternPoolInner {
347            types: Vec::with_capacity(wire.types.len()),
348            array_map: HashMap::default(),
349            ptr_const_map: HashMap::default(),
350            ptr_mut_map: HashMap::default(),
351            ref_map: HashMap::default(),
352            mut_ref_map: HashMap::default(),
353            slice_map: HashMap::default(),
354            mut_slice_map: HashMap::default(),
355            vec_map: HashMap::default(),
356            struct_by_name: HashMap::default(),
357            enum_by_name: HashMap::default(),
358            layout_cache: HashMap::default(),
359        };
360        for data in wire.types {
361            // The interned index is `PRIMITIVE_COUNT + types.len()` BEFORE
362            // pushing the new entry, matching the original intern path.
363            let idx = InternedType(InternedType::PRIMITIVE_COUNT + inner.types.len() as u32);
364            match &data {
365                TypeData::Struct(s) => {
366                    inner.struct_by_name.insert(s.name, idx);
367                }
368                TypeData::Enum(e) => {
369                    inner.enum_by_name.insert(e.name, idx);
370                }
371                TypeData::Array { element, len } => {
372                    inner.array_map.insert((*element, *len), idx);
373                }
374                TypeData::PtrConst { pointee } => {
375                    inner.ptr_const_map.insert(*pointee, idx);
376                }
377                TypeData::PtrMut { pointee } => {
378                    inner.ptr_mut_map.insert(*pointee, idx);
379                }
380                TypeData::Ref { referent } => {
381                    inner.ref_map.insert(*referent, idx);
382                }
383                TypeData::MutRef { referent } => {
384                    inner.mut_ref_map.insert(*referent, idx);
385                }
386                TypeData::Slice { element } => {
387                    inner.slice_map.insert(*element, idx);
388                }
389                TypeData::MutSlice { element } => {
390                    inner.mut_slice_map.insert(*element, idx);
391                }
392                TypeData::Vec { element } => {
393                    inner.vec_map.insert(*element, idx);
394                }
395            }
396            inner.types.push(data);
397        }
398        Ok(Self {
399            inner: RwLock::new(inner),
400        })
401    }
402}
403
404#[derive(Debug)]
405struct TypeInternPoolInner {
406    /// All composite type data, indexed by (InternedType.0 - PRIMITIVE_COUNT).
407    types: Vec<TypeData>,
408
409    /// Structural type deduplication: (element, len) -> InternedType for arrays.
410    array_map: HashMap<(InternedType, u64), InternedType>,
411
412    /// Structural type deduplication: pointee -> InternedType for ptr const.
413    ptr_const_map: HashMap<InternedType, InternedType>,
414
415    /// Structural type deduplication: pointee -> InternedType for ptr mut.
416    ptr_mut_map: HashMap<InternedType, InternedType>,
417
418    /// Structural type deduplication: referent -> InternedType for `Ref(T)`.
419    ref_map: HashMap<InternedType, InternedType>,
420
421    /// Structural type deduplication: referent -> InternedType for `MutRef(T)`.
422    mut_ref_map: HashMap<InternedType, InternedType>,
423
424    /// Structural type deduplication: element -> InternedType for `Slice(T)`.
425    slice_map: HashMap<InternedType, InternedType>,
426
427    /// Structural type deduplication: element -> InternedType for `MutSlice(T)`.
428    mut_slice_map: HashMap<InternedType, InternedType>,
429
430    /// Structural type deduplication: element -> InternedType for `Vec(T)` (ADR-0066).
431    vec_map: HashMap<InternedType, InternedType>,
432
433    /// Nominal type lookup: name -> InternedType for structs.
434    struct_by_name: HashMap<Spur, InternedType>,
435
436    /// Nominal type lookup: name -> InternedType for enums.
437    enum_by_name: HashMap<Spur, InternedType>,
438
439    /// Cached layouts (ADR-0069). Populated lazily by `layout::layout_of`.
440    /// Keyed by `Type` (a u32 index); since types are interned, the layout is
441    /// a pure function of the key.
442    layout_cache: HashMap<Type, Layout>,
443}
444
445impl TypeInternPool {
446    /// Clone the pool by snapshotting its canonical types and
447    /// reconstructing the structural-dedup HashMaps. Used by
448    /// ADR-0074 Phase 4's AIR cache write to capture sema's pool
449    /// state without taking ownership of it. Behavior matches
450    /// serde round-trip — both produce a pool whose
451    /// `intern_*(...)` calls return the same `InternedType`s as
452    /// the original.
453    pub fn clone_snapshot(&self) -> Self {
454        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
455        let mut new_inner = TypeInternPoolInner {
456            types: Vec::with_capacity(inner.types.len()),
457            array_map: HashMap::default(),
458            ptr_const_map: HashMap::default(),
459            ptr_mut_map: HashMap::default(),
460            ref_map: HashMap::default(),
461            mut_ref_map: HashMap::default(),
462            slice_map: HashMap::default(),
463            mut_slice_map: HashMap::default(),
464            vec_map: HashMap::default(),
465            struct_by_name: HashMap::default(),
466            enum_by_name: HashMap::default(),
467            layout_cache: HashMap::default(),
468        };
469        for data in &inner.types {
470            let idx = InternedType(InternedType::PRIMITIVE_COUNT + new_inner.types.len() as u32);
471            match data {
472                TypeData::Struct(s) => {
473                    new_inner.struct_by_name.insert(s.name, idx);
474                }
475                TypeData::Enum(e) => {
476                    new_inner.enum_by_name.insert(e.name, idx);
477                }
478                TypeData::Array { element, len } => {
479                    new_inner.array_map.insert((*element, *len), idx);
480                }
481                TypeData::PtrConst { pointee } => {
482                    new_inner.ptr_const_map.insert(*pointee, idx);
483                }
484                TypeData::PtrMut { pointee } => {
485                    new_inner.ptr_mut_map.insert(*pointee, idx);
486                }
487                TypeData::Ref { referent } => {
488                    new_inner.ref_map.insert(*referent, idx);
489                }
490                TypeData::MutRef { referent } => {
491                    new_inner.mut_ref_map.insert(*referent, idx);
492                }
493                TypeData::Slice { element } => {
494                    new_inner.slice_map.insert(*element, idx);
495                }
496                TypeData::MutSlice { element } => {
497                    new_inner.mut_slice_map.insert(*element, idx);
498                }
499                TypeData::Vec { element } => {
500                    new_inner.vec_map.insert(*element, idx);
501                }
502            }
503            new_inner.types.push(data.clone());
504        }
505        Self {
506            inner: RwLock::new(new_inner),
507        }
508    }
509
510    /// Create a new empty pool.
511    pub fn new() -> Self {
512        Self {
513            inner: RwLock::new(TypeInternPoolInner {
514                types: Vec::new(),
515                array_map: HashMap::default(),
516                ptr_const_map: HashMap::default(),
517                ptr_mut_map: HashMap::default(),
518                ref_map: HashMap::default(),
519                mut_ref_map: HashMap::default(),
520                slice_map: HashMap::default(),
521                mut_slice_map: HashMap::default(),
522                vec_map: HashMap::default(),
523                struct_by_name: HashMap::default(),
524                enum_by_name: HashMap::default(),
525                layout_cache: HashMap::default(),
526            }),
527        }
528    }
529
530    /// Look up a cached layout, if any (ADR-0069).
531    pub(crate) fn cached_layout(&self, ty: Type) -> Option<Layout> {
532        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
533        inner.layout_cache.get(&ty).cloned()
534    }
535
536    /// Insert a layout into the cache (ADR-0069).
537    pub(crate) fn cache_layout(&self, ty: Type, layout: Layout) {
538        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
539        inner.layout_cache.insert(ty, layout);
540    }
541
542    /// Register a new struct (nominal - no deduplication).
543    ///
544    /// Returns the `StructId` (containing the pool index) and whether it was newly inserted.
545    /// If a struct with this name already exists, returns the existing StructId.
546    pub fn register_struct(&self, name: Spur, def: StructDef) -> (StructId, bool) {
547        // Fast path: check with read lock
548        {
549            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
550            if let Some(&existing) = inner.struct_by_name.get(&name) {
551                // Convert InternedType back to StructId via pool_index
552                let pool_index = existing.pool_index().expect("struct must have pool index");
553                return (StructId::from_pool_index(pool_index), false);
554            }
555        }
556
557        // Slow path: acquire write lock
558        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
559
560        // Double-check after acquiring write lock
561        if let Some(&existing) = inner.struct_by_name.get(&name) {
562            let pool_index = existing.pool_index().expect("struct must have pool index");
563            return (StructId::from_pool_index(pool_index), false);
564        }
565
566        // Create new struct type
567        let pool_index = inner.types.len() as u32;
568        let interned = InternedType::from_pool_index(pool_index);
569
570        inner.types.push(TypeData::Struct(StructData { name, def }));
571        inner.struct_by_name.insert(name, interned);
572
573        (StructId::from_pool_index(pool_index), true)
574    }
575
576    /// Reserve a struct ID without registering the full definition yet.
577    ///
578    /// This is used for anonymous structs where we need to know the ID before
579    /// we can construct the name (which includes the ID). Call `complete_struct_registration`
580    /// with the reserved ID to finish registration.
581    ///
582    /// # Returns
583    ///
584    /// Returns the reserved `StructId`. The caller MUST call `complete_struct_registration`
585    /// with this ID before any other pool operations that might read this entry.
586    ///
587    /// # Example
588    ///
589    /// ```ignore
590    /// let struct_id = pool.reserve_struct_id();
591    /// let name = format!("__anon_struct_{}", struct_id.0);
592    /// let name_spur = interner.get_or_intern(&name);
593    /// let def = StructDef { name: name.clone(), ... };
594    /// pool.complete_struct_registration(struct_id, name_spur, def);
595    /// ```
596    pub fn reserve_struct_id(&self) -> StructId {
597        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
598
599        // Reserve a slot by pushing a placeholder
600        // We use a placeholder Struct with empty data that will be overwritten
601        let pool_index = inner.types.len() as u32;
602
603        // Push a placeholder - this reserves the index
604        // The placeholder will be replaced by complete_struct_registration
605        inner.types.push(TypeData::Struct(StructData {
606            name: Spur::default(),
607            def: StructDef {
608                name: String::new(),
609                fields: vec![],
610                posture: Posture::Affine,
611                is_clone: false,
612                thread_safety: gruel_builtins::ThreadSafety::Sync,
613                destructor: None,
614                is_builtin: false,
615                is_pub: false,
616                file_id: gruel_util::FileId::DEFAULT,
617                is_c_layout: false,
618            },
619        }));
620
621        StructId::from_pool_index(pool_index)
622    }
623
624    /// Complete the registration of a previously reserved struct ID.
625    ///
626    /// This must be called after `reserve_struct_id` to fill in the actual struct data.
627    /// The struct will be registered with the provided name for lookup purposes.
628    ///
629    /// # Panics
630    ///
631    /// Panics if:
632    /// - The struct_id wasn't created by `reserve_struct_id`
633    /// - The slot at struct_id doesn't contain a placeholder struct
634    /// - A struct with the given name already exists
635    pub fn complete_struct_registration(&self, struct_id: StructId, name: Spur, def: StructDef) {
636        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
637        let pool_index = struct_id.0 as usize;
638
639        // Verify this is a valid reserved slot
640        assert!(
641            pool_index < inner.types.len(),
642            "Invalid reserved struct ID: index {} out of bounds (len {})",
643            pool_index,
644            inner.types.len()
645        );
646
647        // Check that a struct with this name doesn't already exist
648        assert!(
649            !inner.struct_by_name.contains_key(&name),
650            "Struct with this name already exists"
651        );
652
653        // Update the placeholder with actual data
654        inner.types[pool_index] = TypeData::Struct(StructData { name, def });
655
656        // Register in the name lookup
657        let interned = InternedType::from_pool_index(pool_index as u32);
658        inner.struct_by_name.insert(name, interned);
659    }
660
661    /// Register a new enum (nominal - no deduplication).
662    ///
663    /// Returns the `EnumId` (containing the pool index) and whether it was newly inserted.
664    /// If an enum with this name already exists, returns the existing EnumId.
665    pub fn register_enum(&self, name: Spur, def: EnumDef) -> (EnumId, bool) {
666        // Fast path: check with read lock
667        {
668            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
669            if let Some(&existing) = inner.enum_by_name.get(&name) {
670                let pool_index = existing.pool_index().expect("enum must have pool index");
671                return (EnumId::from_pool_index(pool_index), false);
672            }
673        }
674
675        // Slow path: acquire write lock
676        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
677
678        // Double-check after acquiring write lock
679        if let Some(&existing) = inner.enum_by_name.get(&name) {
680            let pool_index = existing.pool_index().expect("enum must have pool index");
681            return (EnumId::from_pool_index(pool_index), false);
682        }
683
684        // Create new enum type
685        let pool_index = inner.types.len() as u32;
686        let interned = InternedType::from_pool_index(pool_index);
687
688        inner.types.push(TypeData::Enum(EnumData { name, def }));
689        inner.enum_by_name.insert(name, interned);
690
691        (EnumId::from_pool_index(pool_index), true)
692    }
693
694    /// Intern an array type (structural - deduplicates).
695    ///
696    /// Returns the canonical `InternedType` for arrays with this element type and length.
697    /// If an identical array type already exists, returns the existing type.
698    pub fn intern_array(&self, element: InternedType, len: u64) -> InternedType {
699        let key = (element, len);
700
701        // Fast path: check with read lock
702        {
703            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
704            if let Some(&existing) = inner.array_map.get(&key) {
705                return existing;
706            }
707        }
708
709        // Slow path: acquire write lock
710        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
711
712        // Double-check after acquiring write lock
713        if let Some(&existing) = inner.array_map.get(&key) {
714            return existing;
715        }
716
717        // Create new array type
718        let pool_index = inner.types.len() as u32;
719        let interned = InternedType::from_pool_index(pool_index);
720
721        inner.types.push(TypeData::Array { element, len });
722        inner.array_map.insert(key, interned);
723
724        interned
725    }
726
727    /// Intern a ptr const type (structural - deduplicates).
728    ///
729    /// Returns the canonical `InternedType` for pointers to this pointee type.
730    /// If an identical pointer type already exists, returns the existing type.
731    pub fn intern_ptr_const(&self, pointee: InternedType) -> InternedType {
732        // Fast path: check with read lock
733        {
734            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
735            if let Some(&existing) = inner.ptr_const_map.get(&pointee) {
736                return existing;
737            }
738        }
739
740        // Slow path: acquire write lock
741        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
742
743        // Double-check after acquiring write lock
744        if let Some(&existing) = inner.ptr_const_map.get(&pointee) {
745            return existing;
746        }
747
748        // Create new pointer type
749        let pool_index = inner.types.len() as u32;
750        let interned = InternedType::from_pool_index(pool_index);
751
752        inner.types.push(TypeData::PtrConst { pointee });
753        inner.ptr_const_map.insert(pointee, interned);
754
755        interned
756    }
757
758    /// Intern a ptr mut type (structural - deduplicates).
759    ///
760    /// Returns the canonical `InternedType` for mutable pointers to this pointee type.
761    /// If an identical pointer type already exists, returns the existing type.
762    pub fn intern_ptr_mut(&self, pointee: InternedType) -> InternedType {
763        // Fast path: check with read lock
764        {
765            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
766            if let Some(&existing) = inner.ptr_mut_map.get(&pointee) {
767                return existing;
768            }
769        }
770
771        // Slow path: acquire write lock
772        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
773
774        // Double-check after acquiring write lock
775        if let Some(&existing) = inner.ptr_mut_map.get(&pointee) {
776            return existing;
777        }
778
779        // Create new pointer type
780        let pool_index = inner.types.len() as u32;
781        let interned = InternedType::from_pool_index(pool_index);
782
783        inner.types.push(TypeData::PtrMut { pointee });
784        inner.ptr_mut_map.insert(pointee, interned);
785
786        interned
787    }
788
789    /// Intern a `Ref(T)` type (structural - deduplicates).
790    pub fn intern_ref(&self, referent: InternedType) -> InternedType {
791        {
792            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
793            if let Some(&existing) = inner.ref_map.get(&referent) {
794                return existing;
795            }
796        }
797        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
798        if let Some(&existing) = inner.ref_map.get(&referent) {
799            return existing;
800        }
801        let pool_index = inner.types.len() as u32;
802        let interned = InternedType::from_pool_index(pool_index);
803        inner.types.push(TypeData::Ref { referent });
804        inner.ref_map.insert(referent, interned);
805        interned
806    }
807
808    /// Intern a `MutRef(T)` type (structural - deduplicates).
809    pub fn intern_mut_ref(&self, referent: InternedType) -> InternedType {
810        {
811            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
812            if let Some(&existing) = inner.mut_ref_map.get(&referent) {
813                return existing;
814            }
815        }
816        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
817        if let Some(&existing) = inner.mut_ref_map.get(&referent) {
818            return existing;
819        }
820        let pool_index = inner.types.len() as u32;
821        let interned = InternedType::from_pool_index(pool_index);
822        inner.types.push(TypeData::MutRef { referent });
823        inner.mut_ref_map.insert(referent, interned);
824        interned
825    }
826
827    /// Intern a `Slice(T)` type (structural - deduplicates).
828    pub fn intern_slice(&self, element: InternedType) -> InternedType {
829        {
830            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
831            if let Some(&existing) = inner.slice_map.get(&element) {
832                return existing;
833            }
834        }
835        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
836        if let Some(&existing) = inner.slice_map.get(&element) {
837            return existing;
838        }
839        let pool_index = inner.types.len() as u32;
840        let interned = InternedType::from_pool_index(pool_index);
841        inner.types.push(TypeData::Slice { element });
842        inner.slice_map.insert(element, interned);
843        interned
844    }
845
846    /// Intern a `MutSlice(T)` type (structural - deduplicates).
847    pub fn intern_mut_slice(&self, element: InternedType) -> InternedType {
848        {
849            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
850            if let Some(&existing) = inner.mut_slice_map.get(&element) {
851                return existing;
852            }
853        }
854        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
855        if let Some(&existing) = inner.mut_slice_map.get(&element) {
856            return existing;
857        }
858        let pool_index = inner.types.len() as u32;
859        let interned = InternedType::from_pool_index(pool_index);
860        inner.types.push(TypeData::MutSlice { element });
861        inner.mut_slice_map.insert(element, interned);
862        interned
863    }
864
865    /// Intern a `Vec(T)` type (structural - deduplicates) (ADR-0066).
866    pub fn intern_vec(&self, element: InternedType) -> InternedType {
867        {
868            let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
869            if let Some(&existing) = inner.vec_map.get(&element) {
870                return existing;
871            }
872        }
873        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
874        if let Some(&existing) = inner.vec_map.get(&element) {
875            return existing;
876        }
877        let pool_index = inner.types.len() as u32;
878        let interned = InternedType::from_pool_index(pool_index);
879        inner.types.push(TypeData::Vec { element });
880        inner.vec_map.insert(element, interned);
881        interned
882    }
883
884    /// Look up a struct by name.
885    ///
886    pub fn get_struct_by_name(&self, name: Spur) -> Option<InternedType> {
887        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
888        inner.struct_by_name.get(&name).copied()
889    }
890
891    /// Look up an enum by name.
892    pub fn get_enum_by_name(&self, name: Spur) -> Option<InternedType> {
893        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
894        inner.enum_by_name.get(&name).copied()
895    }
896
897    /// Look up an array type by element and length.
898    pub fn get_array(&self, element: InternedType, len: u64) -> Option<InternedType> {
899        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
900        inner.array_map.get(&(element, len)).copied()
901    }
902
903    /// Get type data for a composite type.
904    ///
905    /// Returns `None` for primitive types (use `InternedType::is_primitive()` first).
906    ///
907    /// # Panics
908    ///
909    /// Panics if the index is invalid.
910    pub fn get(&self, ty: InternedType) -> Option<TypeData> {
911        if ty.is_primitive() {
912            return None;
913        }
914
915        let pool_index = ty.pool_index().expect("non-primitive must have pool index");
916        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
917        Some(inner.types[pool_index as usize].clone())
918    }
919
920    /// Check if this is a struct type.
921    pub fn is_struct(&self, ty: InternedType) -> bool {
922        if ty.is_primitive() {
923            return false;
924        }
925        matches!(self.get(ty), Some(TypeData::Struct(_)))
926    }
927
928    /// Check if this is an enum type.
929    pub fn is_enum(&self, ty: InternedType) -> bool {
930        if ty.is_primitive() {
931            return false;
932        }
933        matches!(self.get(ty), Some(TypeData::Enum(_)))
934    }
935
936    /// Check if this is an array type.
937    pub fn is_array(&self, ty: InternedType) -> bool {
938        if ty.is_primitive() {
939            return false;
940        }
941        matches!(self.get(ty), Some(TypeData::Array { .. }))
942    }
943
944    /// Get the struct definition if this is a struct type.
945    pub fn get_struct_def(&self, ty: InternedType) -> Option<StructDef> {
946        match self.get(ty)? {
947            TypeData::Struct(data) => Some(data.def),
948            _ => None,
949        }
950    }
951
952    /// Get the enum definition if this is an enum type.
953    pub fn get_enum_def(&self, ty: InternedType) -> Option<EnumDef> {
954        match self.get(ty)? {
955            TypeData::Enum(data) => Some(data.def),
956            _ => None,
957        }
958    }
959
960    /// Get array info (element type, length) if this is an array type.
961    pub fn get_array_info(&self, ty: InternedType) -> Option<(InternedType, u64)> {
962        match self.get(ty)? {
963            TypeData::Array { element, len } => Some((element, len)),
964            _ => None,
965        }
966    }
967
968    // ========================================================================
969    // Phase 3 helpers: Direct StructId/EnumId access
970    // ========================================================================
971    //
972    // These methods allow accessing struct and enum definitions directly via
973    // StructId/EnumId, which now store pool indices instead of vector indices.
974
975    /// Get a struct definition by StructId.
976    ///
977    /// The StructId contains a pool index. This method looks up the struct
978    /// in the pool and returns a clone of its definition.
979    ///
980    /// # Panics
981    ///
982    /// Panics if the StructId doesn't correspond to a struct in the pool.
983    pub fn struct_def(&self, struct_id: StructId) -> StructDef {
984        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
985        let pool_index = struct_id.0 as usize;
986        match &inner.types[pool_index] {
987            TypeData::Struct(data) => data.def.clone(),
988            other => panic!(
989                "Expected struct at pool index {}, got {:?}",
990                pool_index, other
991            ),
992        }
993    }
994
995    /// Get an enum definition by EnumId.
996    ///
997    /// The EnumId contains a pool index. This method looks up the enum
998    /// in the pool and returns a clone of its definition.
999    ///
1000    /// # Panics
1001    ///
1002    /// Panics if the EnumId doesn't correspond to an enum in the pool.
1003    pub fn enum_def(&self, enum_id: EnumId) -> EnumDef {
1004        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1005        let pool_index = enum_id.0 as usize;
1006        match &inner.types[pool_index] {
1007            TypeData::Enum(data) => data.def.clone(),
1008            other => panic!(
1009                "Expected enum at pool index {}, got {:?}",
1010                pool_index, other
1011            ),
1012        }
1013    }
1014
1015    /// Update a struct definition in the pool.
1016    ///
1017    /// This is used during semantic analysis when struct fields are resolved
1018    /// after the struct is initially registered.
1019    ///
1020    /// # Panics
1021    ///
1022    /// Panics if the StructId doesn't correspond to a struct in the pool.
1023    pub fn update_struct_def(&self, struct_id: StructId, new_def: StructDef) {
1024        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
1025        let pool_index = struct_id.0 as usize;
1026        match &mut inner.types[pool_index] {
1027            TypeData::Struct(data) => data.def = new_def,
1028            other => panic!(
1029                "Expected struct at pool index {}, got {:?}",
1030                pool_index, other
1031            ),
1032        }
1033    }
1034
1035    /// Update an enum definition in the pool.
1036    ///
1037    /// This is used during semantic analysis when enum variants are resolved
1038    /// after the enum is initially registered.
1039    ///
1040    /// # Panics
1041    ///
1042    /// Panics if the EnumId doesn't correspond to an enum in the pool.
1043    pub fn update_enum_def(&self, enum_id: EnumId, new_def: EnumDef) {
1044        let mut inner = self.inner.write().unwrap_or_else(PoisonError::into_inner);
1045        let pool_index = enum_id.0 as usize;
1046        match &mut inner.types[pool_index] {
1047            TypeData::Enum(data) => data.def = new_def,
1048            other => panic!(
1049                "Expected enum at pool index {}, got {:?}",
1050                pool_index, other
1051            ),
1052        }
1053    }
1054
1055    /// Convert a StructId to an InternedType.
1056    ///
1057    /// Since StructId now contains a pool index, we just add the primitive offset.
1058    #[inline]
1059    pub fn struct_id_to_interned(&self, struct_id: StructId) -> InternedType {
1060        InternedType::from_pool_index(struct_id.0)
1061    }
1062
1063    /// Convert an EnumId to an InternedType.
1064    ///
1065    /// Since EnumId now contains a pool index, we just add the primitive offset.
1066    #[inline]
1067    pub fn enum_id_to_interned(&self, enum_id: EnumId) -> InternedType {
1068        InternedType::from_pool_index(enum_id.0)
1069    }
1070
1071    /// Get an array type definition by ArrayTypeId.
1072    ///
1073    /// The ArrayTypeId contains a pool index. This method looks up the array
1074    /// in the pool and returns its element type and length as a tuple.
1075    ///
1076    /// # Returns
1077    ///
1078    /// Returns `(element_type, length)` where `element_type` is the array's element type
1079    /// and `length` is the array's fixed size.
1080    ///
1081    /// # Panics
1082    ///
1083    /// Panics if the ArrayTypeId doesn't correspond to an array in the pool.
1084    pub fn array_def(&self, array_id: ArrayTypeId) -> (Type, u64) {
1085        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1086        let pool_index = array_id.0 as usize;
1087        match &inner.types[pool_index] {
1088            TypeData::Array { element, len } => {
1089                // Convert InternedType back to Type
1090                let element_type = Self::interned_to_type_recursive(*element, &inner);
1091                (element_type, *len)
1092            }
1093            other => panic!(
1094                "Expected array at pool index {}, got {:?}",
1095                pool_index, other
1096            ),
1097        }
1098    }
1099
1100    /// Intern an array type from a Type element.
1101    ///
1102    /// This is a helper method that converts the Type to InternedType
1103    /// and then interns the array.
1104    ///
1105    /// # Panics
1106    ///
1107    /// Panics if the element type contains a struct/enum that isn't in the pool.
1108    pub fn intern_array_from_type(&self, element_type: Type, len: u64) -> ArrayTypeId {
1109        let element_interned = Self::type_to_interned_recursive(element_type);
1110        let array_interned = self.intern_array(element_interned, len);
1111        ArrayTypeId::from_pool_index(
1112            array_interned
1113                .pool_index()
1114                .expect("array must have pool index"),
1115        )
1116    }
1117
1118    /// Look up an array type by Type element and length.
1119    ///
1120    /// Returns None if no such array exists in the pool.
1121    pub fn get_array_by_type(&self, element_type: Type, len: u64) -> Option<ArrayTypeId> {
1122        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1123        let element_interned = Self::type_to_interned_recursive(element_type);
1124        let array_interned = inner.array_map.get(&(element_interned, len))?;
1125        Some(ArrayTypeId::from_pool_index(
1126            array_interned
1127                .pool_index()
1128                .expect("array must have pool index"),
1129        ))
1130    }
1131
1132    /// Intern a ptr const type from a Type pointee.
1133    ///
1134    /// # Panics
1135    ///
1136    /// Panics if the pointee type contains a struct/enum that isn't in the pool.
1137    pub fn intern_ptr_const_from_type(&self, pointee_type: Type) -> PtrConstTypeId {
1138        let pointee_interned = Self::type_to_interned_recursive(pointee_type);
1139        let ptr_interned = self.intern_ptr_const(pointee_interned);
1140        PtrConstTypeId::from_pool_index(
1141            ptr_interned
1142                .pool_index()
1143                .expect("ptr const must have pool index"),
1144        )
1145    }
1146
1147    /// Intern a ptr mut type from a Type pointee.
1148    ///
1149    /// # Panics
1150    ///
1151    /// Panics if the pointee type contains a struct/enum that isn't in the pool.
1152    pub fn intern_ptr_mut_from_type(&self, pointee_type: Type) -> PtrMutTypeId {
1153        let pointee_interned = Self::type_to_interned_recursive(pointee_type);
1154        let ptr_interned = self.intern_ptr_mut(pointee_interned);
1155        PtrMutTypeId::from_pool_index(
1156            ptr_interned
1157                .pool_index()
1158                .expect("ptr mut must have pool index"),
1159        )
1160    }
1161
1162    /// Get ptr const pointee type if this is a ptr const type.
1163    pub fn ptr_const_def(&self, ptr_id: PtrConstTypeId) -> Type {
1164        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1165        let pool_index = ptr_id.0 as usize;
1166        match &inner.types[pool_index] {
1167            TypeData::PtrConst { pointee } => Self::interned_to_type_recursive(*pointee, &inner),
1168            other => panic!(
1169                "Expected ptr const at pool index {}, got {:?}",
1170                pool_index, other
1171            ),
1172        }
1173    }
1174
1175    /// Get ptr mut pointee type if this is a ptr mut type.
1176    pub fn ptr_mut_def(&self, ptr_id: PtrMutTypeId) -> Type {
1177        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1178        let pool_index = ptr_id.0 as usize;
1179        match &inner.types[pool_index] {
1180            TypeData::PtrMut { pointee } => Self::interned_to_type_recursive(*pointee, &inner),
1181            other => panic!(
1182                "Expected ptr mut at pool index {}, got {:?}",
1183                pool_index, other
1184            ),
1185        }
1186    }
1187
1188    /// Intern a `Ref(T)` type from a Type referent.
1189    pub fn intern_ref_from_type(&self, referent_type: Type) -> RefTypeId {
1190        let referent_interned = Self::type_to_interned_recursive(referent_type);
1191        let ref_interned = self.intern_ref(referent_interned);
1192        RefTypeId::from_pool_index(ref_interned.pool_index().expect("ref must have pool index"))
1193    }
1194
1195    /// Intern a `MutRef(T)` type from a Type referent.
1196    pub fn intern_mut_ref_from_type(&self, referent_type: Type) -> MutRefTypeId {
1197        let referent_interned = Self::type_to_interned_recursive(referent_type);
1198        let mut_ref_interned = self.intern_mut_ref(referent_interned);
1199        MutRefTypeId::from_pool_index(
1200            mut_ref_interned
1201                .pool_index()
1202                .expect("mut ref must have pool index"),
1203        )
1204    }
1205
1206    /// Get the referent type of a `Ref(T)`.
1207    pub fn ref_def(&self, ref_id: RefTypeId) -> Type {
1208        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1209        let pool_index = ref_id.0 as usize;
1210        match &inner.types[pool_index] {
1211            TypeData::Ref { referent } => Self::interned_to_type_recursive(*referent, &inner),
1212            other => panic!("Expected ref at pool index {}, got {:?}", pool_index, other),
1213        }
1214    }
1215
1216    /// Get the referent type of a `MutRef(T)`.
1217    pub fn mut_ref_def(&self, ref_id: MutRefTypeId) -> Type {
1218        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1219        let pool_index = ref_id.0 as usize;
1220        match &inner.types[pool_index] {
1221            TypeData::MutRef { referent } => Self::interned_to_type_recursive(*referent, &inner),
1222            other => panic!(
1223                "Expected mut ref at pool index {}, got {:?}",
1224                pool_index, other
1225            ),
1226        }
1227    }
1228
1229    /// Intern a `Slice(T)` type from a Type element.
1230    pub fn intern_slice_from_type(&self, element_type: Type) -> SliceTypeId {
1231        let element_interned = Self::type_to_interned_recursive(element_type);
1232        let slice_interned = self.intern_slice(element_interned);
1233        SliceTypeId::from_pool_index(
1234            slice_interned
1235                .pool_index()
1236                .expect("slice must have pool index"),
1237        )
1238    }
1239
1240    /// Intern a `MutSlice(T)` type from a Type element.
1241    pub fn intern_mut_slice_from_type(&self, element_type: Type) -> MutSliceTypeId {
1242        let element_interned = Self::type_to_interned_recursive(element_type);
1243        let mut_slice_interned = self.intern_mut_slice(element_interned);
1244        MutSliceTypeId::from_pool_index(
1245            mut_slice_interned
1246                .pool_index()
1247                .expect("mut slice must have pool index"),
1248        )
1249    }
1250
1251    /// Get the element type of a `Slice(T)`.
1252    pub fn slice_def(&self, slice_id: SliceTypeId) -> Type {
1253        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1254        let pool_index = slice_id.0 as usize;
1255        match &inner.types[pool_index] {
1256            TypeData::Slice { element } => Self::interned_to_type_recursive(*element, &inner),
1257            other => panic!(
1258                "Expected slice at pool index {}, got {:?}",
1259                pool_index, other
1260            ),
1261        }
1262    }
1263
1264    /// Get the element type of a `MutSlice(T)`.
1265    pub fn mut_slice_def(&self, slice_id: MutSliceTypeId) -> Type {
1266        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1267        let pool_index = slice_id.0 as usize;
1268        match &inner.types[pool_index] {
1269            TypeData::MutSlice { element } => Self::interned_to_type_recursive(*element, &inner),
1270            other => panic!(
1271                "Expected mut slice at pool index {}, got {:?}",
1272                pool_index, other
1273            ),
1274        }
1275    }
1276
1277    /// Intern a `Vec(T)` type from a Type element (ADR-0066).
1278    pub fn intern_vec_from_type(&self, element_type: Type) -> VecTypeId {
1279        let element_interned = Self::type_to_interned_recursive(element_type);
1280        let vec_interned = self.intern_vec(element_interned);
1281        VecTypeId::from_pool_index(vec_interned.pool_index().expect("vec must have pool index"))
1282    }
1283
1284    /// Get the element type of a `Vec(T)` (ADR-0066).
1285    pub fn vec_def(&self, vec_id: VecTypeId) -> Type {
1286        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1287        let pool_index = vec_id.0 as usize;
1288        match &inner.types[pool_index] {
1289            TypeData::Vec { element } => Self::interned_to_type_recursive(*element, &inner),
1290            other => panic!("Expected vec at pool index {}, got {:?}", pool_index, other),
1291        }
1292    }
1293
1294    /// ADR-0084: thread-safety classification for any type.
1295    ///
1296    /// Returns one of `Unsend < Send < Sync`:
1297    ///
1298    /// - **Built-in negative facts.** Raw pointers (`Ptr(T)` /
1299    ///   `MutPtr(T)`) are intrinsically `Unsend` regardless of `T`.
1300    /// - **Built-in positive facts.** Primitive integer / float / bool
1301    ///   / char / unit / never types are intrinsically `Sync`.
1302    /// - **Composites.** Arrays, slices, vectors, references, and
1303    ///   pointer-to-`T` chains take the classification of their
1304    ///   element/referent. Struct / enum types read the
1305    ///   `thread_safety` field on their definition (computed by
1306    ///   `validate_consistency` as the structural minimum over members,
1307    ///   then optionally overridden by a `@mark(unsend)` /
1308    ///   `@mark(checked_send)` / `@mark(checked_sync)` directive).
1309    ///
1310    /// Module / interface / comptime-only types fall through to
1311    /// `Sync` — they have no runtime presence so the classification
1312    /// doesn't constrain anything.
1313    pub fn is_thread_safety_type(&self, ty: Type) -> gruel_builtins::ThreadSafety {
1314        use gruel_builtins::ThreadSafety;
1315        match ty.kind() {
1316            // Built-in negative facts: raw pointers can't safely cross
1317            // a thread boundary on their own. The user can override
1318            // with `@mark(checked_send)` / `@mark(checked_sync)` on
1319            // the containing struct (ADR-0084 § "checked_" naming).
1320            TypeKind::PtrConst(_) | TypeKind::PtrMut(_) => ThreadSafety::Unsend,
1321
1322            // Built-in positive facts: primitives are intrinsically
1323            // Sync. There's no shared mutable state behind a value of
1324            // these types.
1325            TypeKind::I8
1326            | TypeKind::I16
1327            | TypeKind::I32
1328            | TypeKind::I64
1329            | TypeKind::U8
1330            | TypeKind::U16
1331            | TypeKind::U32
1332            | TypeKind::U64
1333            | TypeKind::Isize
1334            | TypeKind::Usize
1335            | TypeKind::F16
1336            | TypeKind::F32
1337            | TypeKind::F64
1338            | TypeKind::Bool
1339            | TypeKind::Char
1340            | TypeKind::Unit
1341            | TypeKind::Never
1342            | TypeKind::Error
1343            // ADR-0086: C named primitive types are Sync — same reasoning
1344            // as the native primitives. `c_void` is an incomplete type with
1345            // no runtime values, but classifying it as Sync is harmless
1346            // because no value of it can exist to cross a thread boundary.
1347            | TypeKind::CSchar
1348            | TypeKind::CShort
1349            | TypeKind::CInt
1350            | TypeKind::CLong
1351            | TypeKind::CLonglong
1352            | TypeKind::CUchar
1353            | TypeKind::CUshort
1354            | TypeKind::CUint
1355            | TypeKind::CUlong
1356            | TypeKind::CUlonglong
1357            | TypeKind::CFloat
1358            | TypeKind::CDouble
1359            | TypeKind::CVoid => ThreadSafety::Sync,
1360
1361            // Composites delegate structurally.
1362            TypeKind::Array(array_id) => {
1363                let (element_type, _length) = self.array_def(array_id);
1364                self.is_thread_safety_type(element_type)
1365            }
1366            TypeKind::Slice(slice_id) => {
1367                let element_type = self.slice_def(slice_id);
1368                self.is_thread_safety_type(element_type)
1369            }
1370            TypeKind::MutSlice(slice_id) => {
1371                let element_type = self.mut_slice_def(slice_id);
1372                self.is_thread_safety_type(element_type)
1373            }
1374            TypeKind::Vec(vec_id) => {
1375                let element_type = self.vec_def(vec_id);
1376                self.is_thread_safety_type(element_type)
1377            }
1378            TypeKind::Ref(ref_id) => {
1379                let referent = self.ref_def(ref_id);
1380                self.is_thread_safety_type(referent)
1381            }
1382            TypeKind::MutRef(ref_id) => {
1383                let referent = self.mut_ref_def(ref_id);
1384                self.is_thread_safety_type(referent)
1385            }
1386
1387            TypeKind::Struct(struct_id) => self.struct_def(struct_id).thread_safety,
1388            TypeKind::Enum(enum_id) => self.enum_def(enum_id).thread_safety,
1389
1390            // Module / interface / comptime-only types have no runtime
1391            // representation; treat as Sync (no constraint on caller).
1392            TypeKind::Module(_)
1393            | TypeKind::Interface(_)
1394            | TypeKind::ComptimeType
1395            | TypeKind::ComptimeStr
1396            | TypeKind::ComptimeInt => ThreadSafety::Sync,
1397        }
1398    }
1399
1400    /// Check if a type is linear (must be explicitly consumed, can't be
1401    /// implicitly dropped — ADR-0008).
1402    ///
1403    /// Linearity propagates through compound types (ADR-0067):
1404    /// - `Struct(S)` is linear iff `S` was declared `linear struct`.
1405    /// - `[T; N]` is linear iff `N > 0` and `T` is linear.
1406    /// - `Vec(T)` is linear iff `T` is linear.
1407    /// - `Enum(E)` is linear iff any variant payload is linear.
1408    /// - All other types are non-linear.
1409    pub fn is_type_linear(&self, ty: Type) -> bool {
1410        match ty.kind() {
1411            TypeKind::Struct(struct_id) => self.struct_def(struct_id).posture == Posture::Linear,
1412            TypeKind::Array(array_id) => {
1413                let (element_type, length) = self.array_def(array_id);
1414                length > 0 && self.is_type_linear(element_type)
1415            }
1416            TypeKind::Vec(vec_id) => {
1417                let element_type = self.vec_def(vec_id);
1418                self.is_type_linear(element_type)
1419            }
1420            // ADR-0080: enums declared `linear` are linear directly.
1421            // For anonymous enums with no keyword (Option / Result and
1422            // similar generic helpers), `find_or_create_anon_enum`
1423            // infers the posture from members; this method then sees
1424            // it set. The propagation fallback below covers
1425            // pre-specialization paths where method bodies are
1426            // type-checked before the host enum has been constructed
1427            // through that codepath.
1428            TypeKind::Enum(enum_id) => {
1429                let def = self.enum_def(enum_id);
1430                def.posture == Posture::Linear
1431                    || def
1432                        .variants
1433                        .iter()
1434                        .any(|v| v.fields.iter().any(|f| self.is_type_linear(*f)))
1435            }
1436            _ => false,
1437        }
1438    }
1439
1440    /// Get all `Vec(T)` type IDs registered in the pool (ADR-0066).
1441    pub fn all_vec_ids(&self) -> Vec<VecTypeId> {
1442        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1443        inner
1444            .types
1445            .iter()
1446            .enumerate()
1447            .filter_map(|(i, td)| match td {
1448                TypeData::Vec { .. } => Some(VecTypeId::from_pool_index(i as u32)),
1449                _ => None,
1450            })
1451            .collect()
1452    }
1453
1454    /// Convert InternedType to Type recursively (handles composite types).
1455    ///
1456    /// This is used to convert array element types from InternedType back to Type.
1457    fn interned_to_type_recursive(ty: InternedType, inner: &TypeInternPoolInner) -> Type {
1458        if ty.is_primitive() {
1459            return match ty.0 {
1460                0 => Type::I8,
1461                1 => Type::I16,
1462                2 => Type::I32,
1463                3 => Type::I64,
1464                4 => Type::U8,
1465                5 => Type::U16,
1466                6 => Type::U32,
1467                7 => Type::U64,
1468                8 => Type::ISIZE,
1469                9 => Type::USIZE,
1470                10 => Type::F16,
1471                11 => Type::F32,
1472                12 => Type::F64,
1473                13 => Type::BOOL,
1474                14 => Type::UNIT,
1475                15 => Type::NEVER,
1476                16 => Type::ERROR,
1477                _ => panic!("Unknown primitive index: {}", ty.0),
1478            };
1479        }
1480
1481        let pool_index = ty.pool_index().expect("non-primitive must have pool index");
1482        match &inner.types[pool_index as usize] {
1483            TypeData::Struct(_) => Type::new_struct(StructId::from_pool_index(pool_index)),
1484            TypeData::Enum(_) => Type::new_enum(EnumId::from_pool_index(pool_index)),
1485            TypeData::Array { .. } => Type::new_array(ArrayTypeId::from_pool_index(pool_index)),
1486            TypeData::PtrConst { .. } => {
1487                Type::new_ptr_const(PtrConstTypeId::from_pool_index(pool_index))
1488            }
1489            TypeData::PtrMut { .. } => Type::new_ptr_mut(PtrMutTypeId::from_pool_index(pool_index)),
1490            TypeData::Ref { .. } => Type::new_ref(RefTypeId::from_pool_index(pool_index)),
1491            TypeData::MutRef { .. } => Type::new_mut_ref(MutRefTypeId::from_pool_index(pool_index)),
1492            TypeData::Slice { .. } => Type::new_slice(SliceTypeId::from_pool_index(pool_index)),
1493            TypeData::MutSlice { .. } => {
1494                Type::new_mut_slice(MutSliceTypeId::from_pool_index(pool_index))
1495            }
1496            TypeData::Vec { .. } => Type::new_vec(VecTypeId::from_pool_index(pool_index)),
1497        }
1498    }
1499
1500    /// Convert Type to InternedType recursively (handles composite types).
1501    ///
1502    /// This is used during Phase 2B migration to convert Type to InternedType
1503    /// for array interning.
1504    fn type_to_interned_recursive(ty: Type) -> InternedType {
1505        match ty.kind() {
1506            TypeKind::I8 => InternedType::I8,
1507            TypeKind::I16 => InternedType::I16,
1508            TypeKind::I32 => InternedType::I32,
1509            TypeKind::I64 => InternedType::I64,
1510            TypeKind::U8 => InternedType::U8,
1511            TypeKind::U16 => InternedType::U16,
1512            TypeKind::U32 => InternedType::U32,
1513            TypeKind::U64 => InternedType::U64,
1514            TypeKind::Isize => InternedType::ISIZE,
1515            TypeKind::Usize => InternedType::USIZE,
1516            TypeKind::F16 => InternedType::F16,
1517            TypeKind::F32 => InternedType::F32,
1518            TypeKind::F64 => InternedType::F64,
1519            TypeKind::Bool => InternedType::BOOL,
1520            TypeKind::Char => InternedType::CHAR,
1521            TypeKind::Unit => InternedType::UNIT,
1522            TypeKind::Never => InternedType::NEVER,
1523            TypeKind::Error => InternedType::ERROR,
1524            // ADR-0086 C named primitive types.
1525            TypeKind::CSchar => InternedType::C_SCHAR,
1526            TypeKind::CShort => InternedType::C_SHORT,
1527            TypeKind::CInt => InternedType::C_INT,
1528            TypeKind::CLong => InternedType::C_LONG,
1529            TypeKind::CLonglong => InternedType::C_LONGLONG,
1530            TypeKind::CUchar => InternedType::C_UCHAR,
1531            TypeKind::CUshort => InternedType::C_USHORT,
1532            TypeKind::CUint => InternedType::C_UINT,
1533            TypeKind::CUlong => InternedType::C_ULONG,
1534            TypeKind::CUlonglong => InternedType::C_ULONGLONG,
1535            TypeKind::CFloat => InternedType::C_FLOAT,
1536            TypeKind::CDouble => InternedType::C_DOUBLE,
1537            TypeKind::CVoid => InternedType::C_VOID,
1538            TypeKind::Struct(id) => InternedType::from_pool_index(id.pool_index()),
1539            TypeKind::Enum(id) => InternedType::from_pool_index(id.pool_index()),
1540            TypeKind::Array(id) => InternedType::from_pool_index(id.pool_index()),
1541            TypeKind::PtrConst(id) => InternedType::from_pool_index(id.pool_index()),
1542            TypeKind::PtrMut(id) => InternedType::from_pool_index(id.pool_index()),
1543            TypeKind::Ref(id) => InternedType::from_pool_index(id.pool_index()),
1544            TypeKind::MutRef(id) => InternedType::from_pool_index(id.pool_index()),
1545            TypeKind::Slice(id) => InternedType::from_pool_index(id.pool_index()),
1546            TypeKind::MutSlice(id) => InternedType::from_pool_index(id.pool_index()),
1547            TypeKind::Vec(id) => InternedType::from_pool_index(id.pool_index()),
1548            TypeKind::Module(_) => panic!("Cannot intern module types"),
1549            TypeKind::Interface(_) => panic!("Cannot intern interface types"),
1550            TypeKind::ComptimeType => panic!("Cannot intern comptime types"),
1551            TypeKind::ComptimeStr => panic!("Cannot intern comptime_str types"),
1552            TypeKind::ComptimeInt => panic!("Cannot intern comptime_int types"),
1553        }
1554    }
1555
1556    /// Convert an ArrayTypeId to an InternedType.
1557    ///
1558    /// Since ArrayTypeId now contains a pool index, we just add the primitive offset.
1559    #[inline]
1560    pub fn array_id_to_interned(&self, array_id: ArrayTypeId) -> InternedType {
1561        InternedType::from_pool_index(array_id.0)
1562    }
1563
1564    /// Get all struct IDs registered in the pool.
1565    ///
1566    /// Returns a vector of all StructId values, useful for iterating over all
1567    /// structs (e.g., for drop glue synthesis).
1568    pub fn all_struct_ids(&self) -> Vec<StructId> {
1569        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1570        inner
1571            .struct_by_name
1572            .values()
1573            .map(|interned| {
1574                let pool_index = interned.pool_index().expect("struct must have pool index");
1575                StructId::from_pool_index(pool_index)
1576            })
1577            .collect()
1578    }
1579
1580    /// Get all enum IDs registered in the pool.
1581    ///
1582    /// Returns a vector of all EnumId values, useful for iterating over all
1583    /// enums.
1584    pub fn all_enum_ids(&self) -> Vec<EnumId> {
1585        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1586        inner
1587            .enum_by_name
1588            .values()
1589            .map(|interned| {
1590                let pool_index = interned.pool_index().expect("enum must have pool index");
1591                EnumId::from_pool_index(pool_index)
1592            })
1593            .collect()
1594    }
1595
1596    /// Get all array IDs registered in the pool.
1597    ///
1598    /// Returns a vector of all ArrayTypeId values, useful for iterating over all
1599    /// arrays (e.g., for drop glue synthesis).
1600    pub fn all_array_ids(&self) -> Vec<ArrayTypeId> {
1601        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1602        inner
1603            .types
1604            .iter()
1605            .enumerate()
1606            .filter_map(|(idx, data)| match data {
1607                TypeData::Array { .. } => Some(ArrayTypeId(idx as u32)),
1608                _ => None,
1609            })
1610            .collect()
1611    }
1612
1613    /// Get the number of composite types in the pool.
1614    pub fn len(&self) -> usize {
1615        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1616        inner.types.len()
1617    }
1618
1619    /// Check if the pool is empty (no composite types).
1620    pub fn is_empty(&self) -> bool {
1621        self.len() == 0
1622    }
1623
1624    /// Get statistics about the pool contents.
1625    pub fn stats(&self) -> TypeInternPoolStats {
1626        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1627        let mut struct_count = 0;
1628        let mut enum_count = 0;
1629        let mut array_count = 0;
1630
1631        for data in &inner.types {
1632            match data {
1633                TypeData::Struct(_) => struct_count += 1,
1634                TypeData::Enum(_) => enum_count += 1,
1635                TypeData::Array { .. } => array_count += 1,
1636                TypeData::PtrConst { .. }
1637                | TypeData::PtrMut { .. }
1638                | TypeData::Ref { .. }
1639                | TypeData::MutRef { .. }
1640                | TypeData::Slice { .. }
1641                | TypeData::MutSlice { .. }
1642                | TypeData::Vec { .. } => {
1643                    // Pointer/reference/slice/vec types are not counted separately in stats
1644                }
1645            }
1646        }
1647
1648        TypeInternPoolStats {
1649            struct_count,
1650            enum_count,
1651            array_count,
1652            total: inner.types.len(),
1653        }
1654    }
1655
1656    // ========================================================================
1657    // Conversion helpers for migration (Phase 1)
1658    // ========================================================================
1659
1660    /// Convert an old-style `Type` to an `InternedType`.
1661    ///
1662    /// This is a temporary helper for Phase 1 migration. It converts the
1663    /// existing `Type` enum to the new interned representation.
1664    ///
1665    /// # Note
1666    ///
1667    /// For struct/enum types, the corresponding type must already be registered
1668    /// in the pool. For array types, this returns an error since array interning
1669    /// requires the pool to already have the element type interned.
1670    pub fn type_to_interned(&self, ty: Type) -> Option<InternedType> {
1671        match ty.kind() {
1672            TypeKind::I8 => Some(InternedType::I8),
1673            TypeKind::I16 => Some(InternedType::I16),
1674            TypeKind::I32 => Some(InternedType::I32),
1675            TypeKind::I64 => Some(InternedType::I64),
1676            TypeKind::U8 => Some(InternedType::U8),
1677            TypeKind::U16 => Some(InternedType::U16),
1678            TypeKind::U32 => Some(InternedType::U32),
1679            TypeKind::U64 => Some(InternedType::U64),
1680            TypeKind::Isize => Some(InternedType::ISIZE),
1681            TypeKind::Usize => Some(InternedType::USIZE),
1682            TypeKind::F16 => Some(InternedType::F16),
1683            TypeKind::F32 => Some(InternedType::F32),
1684            TypeKind::F64 => Some(InternedType::F64),
1685            TypeKind::Bool => Some(InternedType::BOOL),
1686            TypeKind::Char => Some(InternedType::CHAR),
1687            TypeKind::Unit => Some(InternedType::UNIT),
1688            TypeKind::Never => Some(InternedType::NEVER),
1689            TypeKind::Error => Some(InternedType::ERROR),
1690            // ADR-0086 C named primitive types.
1691            TypeKind::CSchar => Some(InternedType::C_SCHAR),
1692            TypeKind::CShort => Some(InternedType::C_SHORT),
1693            TypeKind::CInt => Some(InternedType::C_INT),
1694            TypeKind::CLong => Some(InternedType::C_LONG),
1695            TypeKind::CLonglong => Some(InternedType::C_LONGLONG),
1696            TypeKind::CUchar => Some(InternedType::C_UCHAR),
1697            TypeKind::CUshort => Some(InternedType::C_USHORT),
1698            TypeKind::CUint => Some(InternedType::C_UINT),
1699            TypeKind::CUlong => Some(InternedType::C_ULONG),
1700            TypeKind::CUlonglong => Some(InternedType::C_ULONGLONG),
1701            TypeKind::CFloat => Some(InternedType::C_FLOAT),
1702            TypeKind::CDouble => Some(InternedType::C_DOUBLE),
1703            TypeKind::CVoid => Some(InternedType::C_VOID),
1704            // Struct, enum, array, pointer, and module require pool lookup by ID - we need the name
1705            // to find the interned type. This conversion is not straightforward
1706            // without additional context. Return None to indicate we can't convert.
1707            TypeKind::Struct(_)
1708            | TypeKind::Enum(_)
1709            | TypeKind::Array(_)
1710            | TypeKind::PtrConst(_)
1711            | TypeKind::PtrMut(_)
1712            | TypeKind::Ref(_)
1713            | TypeKind::MutRef(_)
1714            | TypeKind::Slice(_)
1715            | TypeKind::MutSlice(_)
1716            | TypeKind::Vec(_)
1717            | TypeKind::Module(_)
1718            | TypeKind::Interface(_) => None,
1719            // Comptime-only types cannot be interned for runtime
1720            TypeKind::ComptimeType | TypeKind::ComptimeStr | TypeKind::ComptimeInt => None,
1721        }
1722    }
1723
1724    /// Convert an `InternedType` back to the old-style `Type`.
1725    ///
1726    /// This is a temporary helper for Phase 1 migration to verify correctness.
1727    /// Returns `None` for composite types since we need the old IDs.
1728    pub fn interned_to_type(&self, ty: InternedType) -> Option<Type> {
1729        if !ty.is_primitive() {
1730            return None;
1731        }
1732
1733        Some(match ty.0 {
1734            0 => Type::I8,
1735            1 => Type::I16,
1736            2 => Type::I32,
1737            3 => Type::I64,
1738            4 => Type::U8,
1739            5 => Type::U16,
1740            6 => Type::U32,
1741            7 => Type::U64,
1742            8 => Type::ISIZE,
1743            9 => Type::USIZE,
1744            10 => Type::F16,
1745            11 => Type::F32,
1746            12 => Type::F64,
1747            13 => Type::BOOL,
1748            14 => Type::UNIT,
1749            15 => Type::NEVER,
1750            16 => Type::ERROR,
1751            20 => Type::CHAR,
1752            // ADR-0086 C named primitive types.
1753            21 => Type::C_SCHAR,
1754            22 => Type::C_SHORT,
1755            23 => Type::C_INT,
1756            24 => Type::C_LONG,
1757            25 => Type::C_LONGLONG,
1758            26 => Type::C_UCHAR,
1759            27 => Type::C_USHORT,
1760            28 => Type::C_UINT,
1761            29 => Type::C_ULONG,
1762            30 => Type::C_ULONGLONG,
1763            31 => Type::C_FLOAT,
1764            32 => Type::C_DOUBLE,
1765            33 => Type::C_VOID,
1766            _ => return None,
1767        })
1768    }
1769
1770    /// Return the number of ABI slots that `ty` occupies when passed as a function argument.
1771    ///
1772    /// - Scalars (integers, bool, enum, pointer) → 1
1773    /// - Struct → sum of field slot counts (flattened)
1774    /// - Array → element slot count × length
1775    /// - Zero-sized types (unit, never, comptime-only, module) → 0
1776    pub fn abi_slot_count(&self, ty: Type) -> u32 {
1777        match ty.kind() {
1778            TypeKind::Struct(id) => {
1779                let def = self.struct_def(id);
1780                def.fields.iter().map(|f| self.abi_slot_count(f.ty)).sum()
1781            }
1782            TypeKind::Array(id) => {
1783                let (elem, len) = self.array_def(id);
1784                self.abi_slot_count(elem) * len as u32
1785            }
1786            TypeKind::Unit
1787            | TypeKind::Never
1788            | TypeKind::ComptimeType
1789            | TypeKind::ComptimeStr
1790            | TypeKind::ComptimeInt
1791            | TypeKind::Module(_) => 0,
1792            // Fat pointers: 2 slots (ptr + len or ptr + vtable)
1793            TypeKind::Slice(_) | TypeKind::MutSlice(_) | TypeKind::Interface(_) => 2,
1794            // Vec(T) (ADR-0066): 3 slots (ptr + len + cap).
1795            TypeKind::Vec(_) => 3,
1796            _ => 1,
1797        }
1798    }
1799
1800    /// Return the human-readable name of `ty`, suitable for error messages.
1801    ///
1802    /// Examples: `"i32"`, `"bool"`, `"MyStruct"`, `"[i32; 4]"`, `"Ptr(i32)"`.
1803    /// Pointer types are formatted in the ADR-0061 surface form
1804    /// (`Ptr(T)` / `MutPtr(T)`) regardless of which syntax the user wrote;
1805    /// the old `ptr const T` / `ptr mut T` form remains accepted by the
1806    /// parser during the migration but is not produced by diagnostics.
1807    pub fn format_type_name(&self, ty: Type) -> String {
1808        match ty.kind() {
1809            TypeKind::I8 => "i8".to_string(),
1810            TypeKind::I16 => "i16".to_string(),
1811            TypeKind::I32 => "i32".to_string(),
1812            TypeKind::I64 => "i64".to_string(),
1813            TypeKind::U8 => "u8".to_string(),
1814            TypeKind::U16 => "u16".to_string(),
1815            TypeKind::U32 => "u32".to_string(),
1816            TypeKind::U64 => "u64".to_string(),
1817            TypeKind::Isize => "isize".to_string(),
1818            TypeKind::Usize => "usize".to_string(),
1819            TypeKind::F16 => "f16".to_string(),
1820            TypeKind::F32 => "f32".to_string(),
1821            TypeKind::F64 => "f64".to_string(),
1822            TypeKind::Bool => "bool".to_string(),
1823            TypeKind::Char => "char".to_string(),
1824            TypeKind::Unit => "()".to_string(),
1825            TypeKind::Never => "!".to_string(),
1826            TypeKind::Error => "<error>".to_string(),
1827            // ADR-0086 C named primitive types.
1828            TypeKind::CSchar => "c_schar".to_string(),
1829            TypeKind::CShort => "c_short".to_string(),
1830            TypeKind::CInt => "c_int".to_string(),
1831            TypeKind::CLong => "c_long".to_string(),
1832            TypeKind::CLonglong => "c_longlong".to_string(),
1833            TypeKind::CUchar => "c_uchar".to_string(),
1834            TypeKind::CUshort => "c_ushort".to_string(),
1835            TypeKind::CUint => "c_uint".to_string(),
1836            TypeKind::CUlong => "c_ulong".to_string(),
1837            TypeKind::CUlonglong => "c_ulonglong".to_string(),
1838            TypeKind::CFloat => "c_float".to_string(),
1839            TypeKind::CDouble => "c_double".to_string(),
1840            TypeKind::CVoid => "c_void".to_string(),
1841            TypeKind::ComptimeType => "type".to_string(),
1842            TypeKind::ComptimeStr => "comptime_str".to_string(),
1843            TypeKind::ComptimeInt => "comptime_int".to_string(),
1844            TypeKind::Struct(id) => self.struct_def(id).name.clone(),
1845            TypeKind::Enum(id) => self.enum_def(id).name.clone(),
1846            TypeKind::Array(id) => {
1847                let (elem, len) = self.array_def(id);
1848                format!("[{}; {}]", self.format_type_name(elem), len)
1849            }
1850            TypeKind::PtrConst(id) => {
1851                let pointee = self.ptr_const_def(id);
1852                format!("Ptr({})", self.format_type_name(pointee))
1853            }
1854            TypeKind::PtrMut(id) => {
1855                let pointee = self.ptr_mut_def(id);
1856                format!("MutPtr({})", self.format_type_name(pointee))
1857            }
1858            TypeKind::Ref(id) => {
1859                let referent = self.ref_def(id);
1860                format!("Ref({})", self.format_type_name(referent))
1861            }
1862            TypeKind::MutRef(id) => {
1863                let referent = self.mut_ref_def(id);
1864                format!("MutRef({})", self.format_type_name(referent))
1865            }
1866            TypeKind::Slice(id) => {
1867                let element = self.slice_def(id);
1868                format!("Slice({})", self.format_type_name(element))
1869            }
1870            TypeKind::MutSlice(id) => {
1871                let element = self.mut_slice_def(id);
1872                format!("MutSlice({})", self.format_type_name(element))
1873            }
1874            TypeKind::Vec(id) => {
1875                let element = self.vec_def(id);
1876                format!("Vec({})", self.format_type_name(element))
1877            }
1878            TypeKind::Module(_) => "<module>".to_string(),
1879            // Interface names are stored on `Sema::interfaces`, not in the
1880            // type pool. Caller-side error messages should resolve them via
1881            // `Sema`; this fallback is for cases where only the pool is
1882            // available.
1883            TypeKind::Interface(id) => format!("<interface#{}>", id.0),
1884        }
1885    }
1886}
1887
1888impl Default for TypeInternPool {
1889    fn default() -> Self {
1890        Self::new()
1891    }
1892}
1893
1894impl Clone for TypeInternPool {
1895    /// Clone the pool by copying all type data into a new pool.
1896    ///
1897    /// This is used when building `SemaContext` from `Sema`, as the context
1898    /// needs its own copy of the pool for thread-safe sharing.
1899    fn clone(&self) -> Self {
1900        let inner = self.inner.read().unwrap_or_else(PoisonError::into_inner);
1901        Self {
1902            inner: RwLock::new(TypeInternPoolInner {
1903                types: inner.types.clone(),
1904                array_map: inner.array_map.clone(),
1905                ptr_const_map: inner.ptr_const_map.clone(),
1906                ptr_mut_map: inner.ptr_mut_map.clone(),
1907                ref_map: inner.ref_map.clone(),
1908                mut_ref_map: inner.mut_ref_map.clone(),
1909                slice_map: inner.slice_map.clone(),
1910                mut_slice_map: inner.mut_slice_map.clone(),
1911                vec_map: inner.vec_map.clone(),
1912                struct_by_name: inner.struct_by_name.clone(),
1913                enum_by_name: inner.enum_by_name.clone(),
1914                layout_cache: inner.layout_cache.clone(),
1915            }),
1916        }
1917    }
1918}
1919
1920/// Statistics about the intern pool contents.
1921#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1922pub struct TypeInternPoolStats {
1923    pub struct_count: usize,
1924    pub enum_count: usize,
1925    pub array_count: usize,
1926    pub total: usize,
1927}
1928
1929#[cfg(test)]
1930mod tests {
1931    use super::*;
1932    use crate::types::EnumVariantDef;
1933    use lasso::ThreadedRodeo;
1934
1935    // ========================================================================
1936    // InternedType tests
1937    // ========================================================================
1938
1939    #[test]
1940    fn type_intern_pool_round_trips_through_serde() {
1941        // ADR-0074 Phase 4: snapshot a pool, serialize via bincode, load
1942        // back, verify all interned types resolve to the same data.
1943        let pool = TypeInternPool::new();
1944        let inner_array = pool.intern_array(InternedType::I32, 4);
1945        let outer_array = pool.intern_array(inner_array, 2);
1946        let ptr = pool.intern_ptr_const(InternedType::I64);
1947
1948        let bytes =
1949            bincode::serde::encode_to_vec(&pool, bincode::config::standard()).expect("serialize");
1950        let (restored, _): (TypeInternPool, _) =
1951            bincode::serde::decode_from_slice(&bytes, bincode::config::standard())
1952                .expect("deserialize");
1953
1954        // The restored pool's structural-dedup maps should be reconstructed:
1955        // re-interning identical types should return the same InternedType
1956        // values that the original pool produced.
1957        assert_eq!(restored.intern_array(InternedType::I32, 4), inner_array);
1958        assert_eq!(restored.intern_array(inner_array, 2), outer_array);
1959        assert_eq!(restored.intern_ptr_const(InternedType::I64), ptr);
1960    }
1961
1962    #[test]
1963    fn test_interned_type_primitives() {
1964        assert!(InternedType::I8.is_primitive());
1965        assert!(InternedType::I16.is_primitive());
1966        assert!(InternedType::I32.is_primitive());
1967        assert!(InternedType::I64.is_primitive());
1968        assert!(InternedType::U8.is_primitive());
1969        assert!(InternedType::U16.is_primitive());
1970        assert!(InternedType::U32.is_primitive());
1971        assert!(InternedType::U64.is_primitive());
1972        assert!(InternedType::BOOL.is_primitive());
1973        assert!(InternedType::UNIT.is_primitive());
1974        assert!(InternedType::NEVER.is_primitive());
1975        assert!(InternedType::ERROR.is_primitive());
1976    }
1977
1978    #[test]
1979    fn test_interned_type_indices() {
1980        assert_eq!(InternedType::I8.index(), 0);
1981        assert_eq!(InternedType::I16.index(), 1);
1982        assert_eq!(InternedType::I32.index(), 2);
1983        assert_eq!(InternedType::I64.index(), 3);
1984        assert_eq!(InternedType::U8.index(), 4);
1985        assert_eq!(InternedType::ISIZE.index(), 8);
1986        assert_eq!(InternedType::USIZE.index(), 9);
1987        assert_eq!(InternedType::F16.index(), 10);
1988        assert_eq!(InternedType::F32.index(), 11);
1989        assert_eq!(InternedType::F64.index(), 12);
1990        assert_eq!(InternedType::BOOL.index(), 13);
1991        assert_eq!(InternedType::UNIT.index(), 14);
1992    }
1993
1994    #[test]
1995    fn test_interned_type_pool_index() {
1996        // Primitives don't have pool indices
1997        assert_eq!(InternedType::I32.pool_index(), None);
1998        assert_eq!(InternedType::BOOL.pool_index(), None);
1999
2000        // Composite types have pool indices
2001        let composite = InternedType::from_pool_index(0);
2002        assert_eq!(composite.pool_index(), Some(0));
2003        assert!(!composite.is_primitive());
2004
2005        let composite2 = InternedType::from_pool_index(42);
2006        assert_eq!(composite2.pool_index(), Some(42));
2007    }
2008
2009    #[test]
2010    fn test_interned_type_equality() {
2011        assert_eq!(InternedType::I32, InternedType::I32);
2012        assert_ne!(InternedType::I32, InternedType::I64);
2013        assert_ne!(InternedType::I32, InternedType::from_pool_index(0));
2014    }
2015
2016    #[test]
2017    fn test_interned_type_debug() {
2018        let i32_str = format!("{:?}", InternedType::I32);
2019        assert!(i32_str.contains("i32"));
2020
2021        let composite_str = format!("{:?}", InternedType::from_pool_index(5));
2022        assert!(composite_str.contains("pool:5"));
2023    }
2024
2025    // ========================================================================
2026    // TypeInternPool tests
2027    // ========================================================================
2028
2029    #[test]
2030    fn test_pool_new() {
2031        let pool = TypeInternPool::new();
2032        assert!(pool.is_empty());
2033        assert_eq!(pool.len(), 0);
2034    }
2035
2036    #[test]
2037    fn test_pool_register_struct() {
2038        let pool = TypeInternPool::new();
2039        let interner = ThreadedRodeo::default();
2040        let name = interner.get_or_intern("Point");
2041
2042        let def = StructDef {
2043            name: "Point".to_string(),
2044            fields: vec![],
2045            posture: Posture::Affine,
2046            is_clone: false,
2047            thread_safety: gruel_builtins::ThreadSafety::Sync,
2048            destructor: None,
2049            is_builtin: false,
2050            is_pub: false,
2051            file_id: gruel_util::FileId::DEFAULT,
2052            is_c_layout: false,
2053        };
2054
2055        let (struct_id, is_new) = pool.register_struct(name, def.clone());
2056        assert!(is_new);
2057        assert_eq!(struct_id.pool_index(), 0); // First entry in pool
2058        assert_eq!(pool.len(), 1);
2059
2060        // Registering the same name returns the existing type
2061        let (struct_id2, is_new2) = pool.register_struct(name, def);
2062        assert!(!is_new2);
2063        assert_eq!(struct_id, struct_id2);
2064        assert_eq!(pool.len(), 1); // No new type added
2065    }
2066
2067    #[test]
2068    fn test_pool_register_enum() {
2069        let pool = TypeInternPool::new();
2070        let interner = ThreadedRodeo::default();
2071        let name = interner.get_or_intern("Color");
2072
2073        let def = EnumDef {
2074            name: "Color".to_string(),
2075            variants: vec![
2076                EnumVariantDef::unit("Red"),
2077                EnumVariantDef::unit("Green"),
2078                EnumVariantDef::unit("Blue"),
2079            ],
2080            posture: Posture::Affine,
2081            thread_safety: gruel_builtins::ThreadSafety::Sync,
2082            is_pub: false,
2083            file_id: gruel_util::FileId::DEFAULT,
2084            destructor: None,
2085            is_c_layout: false,
2086        };
2087
2088        let (enum_id, is_new) = pool.register_enum(name, def.clone());
2089        assert!(is_new);
2090        assert_eq!(enum_id.pool_index(), 0); // First entry in pool
2091        assert_eq!(pool.len(), 1);
2092
2093        // Registering the same name returns the existing type
2094        let (enum_id2, is_new2) = pool.register_enum(name, def);
2095        assert!(!is_new2);
2096        assert_eq!(enum_id, enum_id2);
2097    }
2098
2099    #[test]
2100    fn test_pool_intern_array() {
2101        let pool = TypeInternPool::new();
2102
2103        // Intern [i32; 5]
2104        let arr1 = pool.intern_array(InternedType::I32, 5);
2105        assert!(!arr1.is_primitive());
2106        assert_eq!(pool.len(), 1);
2107
2108        // Interning the same array returns the same type
2109        let arr2 = pool.intern_array(InternedType::I32, 5);
2110        assert_eq!(arr1, arr2);
2111        assert_eq!(pool.len(), 1);
2112
2113        // Different length is a different type
2114        let arr3 = pool.intern_array(InternedType::I32, 10);
2115        assert_ne!(arr1, arr3);
2116        assert_eq!(pool.len(), 2);
2117
2118        // Different element type is a different type
2119        let arr4 = pool.intern_array(InternedType::I64, 5);
2120        assert_ne!(arr1, arr4);
2121        assert_eq!(pool.len(), 3);
2122    }
2123
2124    #[test]
2125    fn test_pool_get_struct_by_name() {
2126        let pool = TypeInternPool::new();
2127        let interner = ThreadedRodeo::default();
2128        let name = interner.get_or_intern("Point");
2129
2130        assert!(pool.get_struct_by_name(name).is_none());
2131
2132        let def = StructDef {
2133            name: "Point".to_string(),
2134            fields: vec![],
2135            posture: Posture::Affine,
2136            is_clone: false,
2137            thread_safety: gruel_builtins::ThreadSafety::Sync,
2138            destructor: None,
2139            is_builtin: false,
2140            is_pub: false,
2141            file_id: gruel_util::FileId::DEFAULT,
2142            is_c_layout: false,
2143        };
2144
2145        let (struct_id, _) = pool.register_struct(name, def);
2146        // get_struct_by_name returns InternedType, convert StructId for comparison
2147        let expected = pool.struct_id_to_interned(struct_id);
2148        assert_eq!(pool.get_struct_by_name(name), Some(expected));
2149    }
2150
2151    #[test]
2152    fn test_pool_get_enum_by_name() {
2153        let pool = TypeInternPool::new();
2154        let interner = ThreadedRodeo::default();
2155        let name = interner.get_or_intern("Status");
2156
2157        assert!(pool.get_enum_by_name(name).is_none());
2158
2159        let def = EnumDef {
2160            name: "Status".to_string(),
2161            variants: vec![
2162                EnumVariantDef::unit("Active"),
2163                EnumVariantDef::unit("Inactive"),
2164            ],
2165            posture: Posture::Affine,
2166            thread_safety: gruel_builtins::ThreadSafety::Sync,
2167            is_pub: false,
2168            file_id: gruel_util::FileId::DEFAULT,
2169            destructor: None,
2170            is_c_layout: false,
2171        };
2172
2173        let (enum_id, _) = pool.register_enum(name, def);
2174        // get_enum_by_name returns InternedType, convert EnumId for comparison
2175        let expected = pool.enum_id_to_interned(enum_id);
2176        assert_eq!(pool.get_enum_by_name(name), Some(expected));
2177    }
2178
2179    #[test]
2180    fn test_pool_get_array() {
2181        let pool = TypeInternPool::new();
2182
2183        assert!(pool.get_array(InternedType::I32, 5).is_none());
2184
2185        let arr = pool.intern_array(InternedType::I32, 5);
2186        assert_eq!(pool.get_array(InternedType::I32, 5), Some(arr));
2187        assert!(pool.get_array(InternedType::I32, 10).is_none());
2188    }
2189
2190    #[test]
2191    fn test_pool_get_type_data() {
2192        let pool = TypeInternPool::new();
2193        let interner = ThreadedRodeo::default();
2194
2195        // Primitive types return None
2196        assert!(pool.get(InternedType::I32).is_none());
2197
2198        // Register a struct
2199        let struct_name = interner.get_or_intern("Point");
2200        let struct_def = StructDef {
2201            name: "Point".to_string(),
2202            fields: vec![],
2203            posture: Posture::Affine,
2204            is_clone: false,
2205            thread_safety: gruel_builtins::ThreadSafety::Sync,
2206            destructor: None,
2207            is_builtin: false,
2208            is_pub: false,
2209            file_id: gruel_util::FileId::DEFAULT,
2210            is_c_layout: false,
2211        };
2212        let (struct_id, _) = pool.register_struct(struct_name, struct_def);
2213        let struct_ty = pool.struct_id_to_interned(struct_id);
2214
2215        // Get struct data
2216        let data = pool.get(struct_ty).expect("should get struct data");
2217        assert!(matches!(data, TypeData::Struct(_)));
2218
2219        // Intern an array
2220        let arr_ty = pool.intern_array(InternedType::I32, 10);
2221        let arr_data = pool.get(arr_ty).expect("should get array data");
2222        match arr_data {
2223            TypeData::Array { element, len } => {
2224                assert_eq!(element, InternedType::I32);
2225                assert_eq!(len, 10);
2226            }
2227            _ => panic!("expected array data"),
2228        }
2229    }
2230
2231    #[test]
2232    fn test_pool_type_checks() {
2233        let pool = TypeInternPool::new();
2234        let interner = ThreadedRodeo::default();
2235
2236        let struct_name = interner.get_or_intern("Point");
2237        let struct_def = StructDef {
2238            name: "Point".to_string(),
2239            fields: vec![],
2240            posture: Posture::Affine,
2241            is_clone: false,
2242            thread_safety: gruel_builtins::ThreadSafety::Sync,
2243            destructor: None,
2244            is_builtin: false,
2245            is_pub: false,
2246            file_id: gruel_util::FileId::DEFAULT,
2247            is_c_layout: false,
2248        };
2249        let (struct_id, _) = pool.register_struct(struct_name, struct_def);
2250        let struct_ty = pool.struct_id_to_interned(struct_id);
2251
2252        let enum_name = interner.get_or_intern("Color");
2253        let enum_def = EnumDef {
2254            name: "Color".to_string(),
2255            variants: vec![EnumVariantDef::unit("Red")],
2256            posture: Posture::Affine,
2257            thread_safety: gruel_builtins::ThreadSafety::Sync,
2258            is_pub: false,
2259            file_id: gruel_util::FileId::DEFAULT,
2260            destructor: None,
2261            is_c_layout: false,
2262        };
2263        let (enum_id, _) = pool.register_enum(enum_name, enum_def);
2264        let enum_ty = pool.enum_id_to_interned(enum_id);
2265
2266        let array_ty = pool.intern_array(InternedType::I32, 5);
2267
2268        // Check is_struct
2269        assert!(pool.is_struct(struct_ty));
2270        assert!(!pool.is_struct(enum_ty));
2271        assert!(!pool.is_struct(array_ty));
2272        assert!(!pool.is_struct(InternedType::I32));
2273
2274        // Check is_enum
2275        assert!(!pool.is_enum(struct_ty));
2276        assert!(pool.is_enum(enum_ty));
2277        assert!(!pool.is_enum(array_ty));
2278        assert!(!pool.is_enum(InternedType::I32));
2279
2280        // Check is_array
2281        assert!(!pool.is_array(struct_ty));
2282        assert!(!pool.is_array(enum_ty));
2283        assert!(pool.is_array(array_ty));
2284        assert!(!pool.is_array(InternedType::I32));
2285    }
2286
2287    #[test]
2288    fn test_pool_get_struct_def() {
2289        let pool = TypeInternPool::new();
2290        let interner = ThreadedRodeo::default();
2291
2292        let name = interner.get_or_intern("Point");
2293        let def = StructDef {
2294            name: "Point".to_string(),
2295            fields: vec![],
2296            posture: Posture::Copy,
2297            is_clone: false,
2298            thread_safety: gruel_builtins::ThreadSafety::Sync,
2299            destructor: None,
2300            is_builtin: false,
2301            is_pub: false,
2302            file_id: gruel_util::FileId::DEFAULT,
2303            is_c_layout: false,
2304        };
2305        let (struct_id, _) = pool.register_struct(name, def.clone());
2306
2307        // Test the new Phase 3 struct_def() method that takes StructId directly
2308        let retrieved = pool.struct_def(struct_id);
2309        assert_eq!(retrieved.name, def.name);
2310        assert_eq!(retrieved.posture, def.posture);
2311
2312        // Test the old get_struct_def() that takes InternedType
2313        let interned = pool.struct_id_to_interned(struct_id);
2314        let retrieved2 = pool
2315            .get_struct_def(interned)
2316            .expect("should get struct def");
2317        assert_eq!(retrieved2.name, def.name);
2318
2319        // Non-struct returns None for get_struct_def
2320        let array_ty = pool.intern_array(InternedType::I32, 5);
2321        assert!(pool.get_struct_def(array_ty).is_none());
2322        assert!(pool.get_struct_def(InternedType::I32).is_none());
2323    }
2324
2325    #[test]
2326    fn test_pool_get_enum_def() {
2327        let pool = TypeInternPool::new();
2328        let interner = ThreadedRodeo::default();
2329
2330        let name = interner.get_or_intern("Status");
2331        let def = EnumDef {
2332            name: "Status".to_string(),
2333            variants: vec![EnumVariantDef::unit("A"), EnumVariantDef::unit("B")],
2334            posture: Posture::Affine,
2335            thread_safety: gruel_builtins::ThreadSafety::Sync,
2336            is_pub: false,
2337            file_id: gruel_util::FileId::DEFAULT,
2338            destructor: None,
2339            is_c_layout: false,
2340        };
2341        let (enum_id, _) = pool.register_enum(name, def.clone());
2342
2343        // Test the new Phase 3 enum_def() method that takes EnumId directly
2344        let retrieved = pool.enum_def(enum_id);
2345        assert_eq!(retrieved.name, def.name);
2346        assert_eq!(retrieved.variants.len(), 2);
2347
2348        // Test the old get_enum_def() that takes InternedType
2349        let interned = pool.enum_id_to_interned(enum_id);
2350        let retrieved2 = pool.get_enum_def(interned).expect("should get enum def");
2351        assert_eq!(retrieved2.name, def.name);
2352
2353        // Non-enum returns None for get_enum_def
2354        let array_ty = pool.intern_array(InternedType::I32, 5);
2355        assert!(pool.get_enum_def(array_ty).is_none());
2356        assert!(pool.get_enum_def(InternedType::I32).is_none());
2357    }
2358
2359    #[test]
2360    fn test_pool_get_array_info() {
2361        let pool = TypeInternPool::new();
2362
2363        let array_ty = pool.intern_array(InternedType::I64, 100);
2364        let (element, len) = pool
2365            .get_array_info(array_ty)
2366            .expect("should get array info");
2367        assert_eq!(element, InternedType::I64);
2368        assert_eq!(len, 100);
2369
2370        // Non-array returns None
2371        let interner = ThreadedRodeo::default();
2372        let name = interner.get_or_intern("X");
2373        let def = StructDef {
2374            name: "X".to_string(),
2375            fields: vec![],
2376            posture: Posture::Affine,
2377            is_clone: false,
2378            thread_safety: gruel_builtins::ThreadSafety::Sync,
2379            destructor: None,
2380            is_builtin: false,
2381            is_pub: false,
2382            file_id: gruel_util::FileId::DEFAULT,
2383            is_c_layout: false,
2384        };
2385        let (struct_id, _) = pool.register_struct(name, def);
2386        let struct_ty = pool.struct_id_to_interned(struct_id);
2387        assert!(pool.get_array_info(struct_ty).is_none());
2388        assert!(pool.get_array_info(InternedType::I32).is_none());
2389    }
2390
2391    #[test]
2392    fn test_pool_stats() {
2393        let pool = TypeInternPool::new();
2394        let interner = ThreadedRodeo::default();
2395
2396        let stats = pool.stats();
2397        assert_eq!(stats.struct_count, 0);
2398        assert_eq!(stats.enum_count, 0);
2399        assert_eq!(stats.array_count, 0);
2400        assert_eq!(stats.total, 0);
2401
2402        // Add some types
2403        let s1 = interner.get_or_intern("S1");
2404        let s2 = interner.get_or_intern("S2");
2405        let e1 = interner.get_or_intern("E1");
2406
2407        let def = StructDef {
2408            name: "S1".to_string(),
2409            fields: vec![],
2410            posture: Posture::Affine,
2411            is_clone: false,
2412            thread_safety: gruel_builtins::ThreadSafety::Sync,
2413            destructor: None,
2414            is_builtin: false,
2415            is_pub: false,
2416            file_id: gruel_util::FileId::DEFAULT,
2417            is_c_layout: false,
2418        };
2419        pool.register_struct(s1, def.clone());
2420        pool.register_struct(
2421            s2,
2422            StructDef {
2423                name: "S2".to_string(),
2424                ..def
2425            },
2426        );
2427
2428        pool.register_enum(
2429            e1,
2430            EnumDef {
2431                name: "E1".to_string(),
2432                variants: vec![],
2433                posture: Posture::Affine,
2434                thread_safety: gruel_builtins::ThreadSafety::Sync,
2435                is_pub: false,
2436                file_id: gruel_util::FileId::DEFAULT,
2437                destructor: None,
2438                is_c_layout: false,
2439            },
2440        );
2441
2442        pool.intern_array(InternedType::I32, 5);
2443        pool.intern_array(InternedType::I32, 10);
2444        pool.intern_array(InternedType::BOOL, 3);
2445
2446        let stats = pool.stats();
2447        assert_eq!(stats.struct_count, 2);
2448        assert_eq!(stats.enum_count, 1);
2449        assert_eq!(stats.array_count, 3);
2450        assert_eq!(stats.total, 6);
2451    }
2452
2453    #[test]
2454    fn test_pool_nested_arrays() {
2455        let pool = TypeInternPool::new();
2456
2457        // Create [i32; 3]
2458        let inner = pool.intern_array(InternedType::I32, 3);
2459
2460        // Create [[i32; 3]; 4]
2461        let outer = pool.intern_array(inner, 4);
2462
2463        // Verify structure
2464        let (outer_elem, outer_len) = pool.get_array_info(outer).expect("outer array info");
2465        assert_eq!(outer_elem, inner);
2466        assert_eq!(outer_len, 4);
2467
2468        let (inner_elem, inner_len) = pool.get_array_info(inner).expect("inner array info");
2469        assert_eq!(inner_elem, InternedType::I32);
2470        assert_eq!(inner_len, 3);
2471    }
2472
2473    #[test]
2474    fn test_pool_type_to_interned() {
2475        let pool = TypeInternPool::new();
2476
2477        // Primitive types convert correctly
2478        assert_eq!(pool.type_to_interned(Type::I8), Some(InternedType::I8));
2479        assert_eq!(pool.type_to_interned(Type::I16), Some(InternedType::I16));
2480        assert_eq!(pool.type_to_interned(Type::I32), Some(InternedType::I32));
2481        assert_eq!(pool.type_to_interned(Type::I64), Some(InternedType::I64));
2482        assert_eq!(pool.type_to_interned(Type::U8), Some(InternedType::U8));
2483        assert_eq!(pool.type_to_interned(Type::U16), Some(InternedType::U16));
2484        assert_eq!(pool.type_to_interned(Type::U32), Some(InternedType::U32));
2485        assert_eq!(pool.type_to_interned(Type::U64), Some(InternedType::U64));
2486        assert_eq!(pool.type_to_interned(Type::BOOL), Some(InternedType::BOOL));
2487        assert_eq!(pool.type_to_interned(Type::UNIT), Some(InternedType::UNIT));
2488        assert_eq!(
2489            pool.type_to_interned(Type::NEVER),
2490            Some(InternedType::NEVER)
2491        );
2492        assert_eq!(
2493            pool.type_to_interned(Type::ERROR),
2494            Some(InternedType::ERROR)
2495        );
2496
2497        // Composite types return None (need name lookup)
2498        assert!(
2499            pool.type_to_interned(Type::new_struct(crate::types::StructId(0)))
2500                .is_none()
2501        );
2502        assert!(
2503            pool.type_to_interned(Type::new_enum(crate::types::EnumId(0)))
2504                .is_none()
2505        );
2506        assert!(
2507            pool.type_to_interned(Type::new_array(crate::types::ArrayTypeId(0)))
2508                .is_none()
2509        );
2510    }
2511
2512    #[test]
2513    fn test_pool_interned_to_type() {
2514        let pool = TypeInternPool::new();
2515
2516        // Primitive types convert back correctly
2517        assert_eq!(pool.interned_to_type(InternedType::I8), Some(Type::I8));
2518        assert_eq!(pool.interned_to_type(InternedType::I16), Some(Type::I16));
2519        assert_eq!(pool.interned_to_type(InternedType::I32), Some(Type::I32));
2520        assert_eq!(pool.interned_to_type(InternedType::I64), Some(Type::I64));
2521        assert_eq!(pool.interned_to_type(InternedType::U8), Some(Type::U8));
2522        assert_eq!(pool.interned_to_type(InternedType::U16), Some(Type::U16));
2523        assert_eq!(pool.interned_to_type(InternedType::U32), Some(Type::U32));
2524        assert_eq!(pool.interned_to_type(InternedType::U64), Some(Type::U64));
2525        assert_eq!(pool.interned_to_type(InternedType::BOOL), Some(Type::BOOL));
2526        assert_eq!(pool.interned_to_type(InternedType::UNIT), Some(Type::UNIT));
2527        assert_eq!(
2528            pool.interned_to_type(InternedType::NEVER),
2529            Some(Type::NEVER)
2530        );
2531        assert_eq!(
2532            pool.interned_to_type(InternedType::ERROR),
2533            Some(Type::ERROR)
2534        );
2535
2536        // Composite types return None
2537        assert!(
2538            pool.interned_to_type(InternedType::from_pool_index(0))
2539                .is_none()
2540        );
2541    }
2542
2543    // ========================================================================
2544    // Thread safety tests
2545    // ========================================================================
2546
2547    #[test]
2548    fn test_pool_concurrent_access() {
2549        use std::sync::Arc;
2550        use std::thread;
2551
2552        let pool = Arc::new(TypeInternPool::new());
2553        let interner = Arc::new(ThreadedRodeo::default());
2554
2555        // Pre-register names for thread safety
2556        let names: Vec<Spur> = (0..100)
2557            .map(|i| interner.get_or_intern(format!("Type{}", i)))
2558            .collect();
2559
2560        let handles: Vec<_> = (0..10)
2561            .map(|thread_id| {
2562                let pool = Arc::clone(&pool);
2563                let names = names.clone();
2564                thread::spawn(move || {
2565                    // Each thread registers 10 types
2566                    for i in 0..10 {
2567                        let idx = thread_id * 10 + i;
2568                        let name = names[idx];
2569                        let def = StructDef {
2570                            name: format!("Type{}", idx),
2571                            fields: vec![],
2572                            posture: Posture::Affine,
2573                            is_clone: false,
2574                            thread_safety: gruel_builtins::ThreadSafety::Sync,
2575                            destructor: None,
2576                            is_builtin: false,
2577                            is_pub: false,
2578                            file_id: gruel_util::FileId::DEFAULT,
2579                            is_c_layout: false,
2580                        };
2581                        pool.register_struct(name, def);
2582                    }
2583                })
2584            })
2585            .collect();
2586
2587        for handle in handles {
2588            handle.join().expect("thread panicked");
2589        }
2590
2591        // All 100 types should be registered
2592        assert_eq!(pool.len(), 100);
2593
2594        // Each name should map to a valid type
2595        for name in &names {
2596            assert!(pool.get_struct_by_name(*name).is_some());
2597        }
2598    }
2599
2600    #[test]
2601    fn test_pool_concurrent_array_interning() {
2602        use std::sync::Arc;
2603        use std::thread;
2604
2605        let pool = Arc::new(TypeInternPool::new());
2606
2607        // Multiple threads try to intern the same array type
2608        let handles: Vec<_> = (0..10)
2609            .map(|_| {
2610                let pool = Arc::clone(&pool);
2611                thread::spawn(move || pool.intern_array(InternedType::I32, 42))
2612            })
2613            .collect();
2614
2615        let results: Vec<_> = handles
2616            .into_iter()
2617            .map(|h| h.join().expect("thread panicked"))
2618            .collect();
2619
2620        // All threads should get the same type
2621        let first = results[0];
2622        for result in &results {
2623            assert_eq!(*result, first);
2624        }
2625
2626        // Only one array type should be in the pool
2627        assert_eq!(pool.stats().array_count, 1);
2628    }
2629
2630    // ========================================================================
2631    // Struct ID reservation tests
2632    // ========================================================================
2633
2634    #[test]
2635    fn test_pool_reserve_and_complete_struct() {
2636        let pool = TypeInternPool::new();
2637        let interner = ThreadedRodeo::default();
2638
2639        // Reserve an ID
2640        let struct_id = pool.reserve_struct_id();
2641        assert_eq!(struct_id.pool_index(), 0);
2642        assert_eq!(pool.len(), 1); // Placeholder was pushed
2643
2644        // Use the ID to create a name
2645        let name_str = format!("__anon_struct_{}", struct_id.0);
2646        let name = interner.get_or_intern(&name_str);
2647
2648        let def = StructDef {
2649            name: name_str.clone(),
2650            fields: vec![],
2651            posture: Posture::Affine,
2652            is_clone: false,
2653            thread_safety: gruel_builtins::ThreadSafety::Sync,
2654            destructor: None,
2655            is_builtin: false,
2656            is_pub: false,
2657            file_id: gruel_util::FileId::DEFAULT,
2658            is_c_layout: false,
2659        };
2660
2661        // Complete registration
2662        pool.complete_struct_registration(struct_id, name, def);
2663
2664        // Verify registration succeeded
2665        assert_eq!(pool.len(), 1); // No new entry, just updated
2666        assert!(pool.get_struct_by_name(name).is_some());
2667
2668        // Can retrieve the struct definition
2669        let retrieved = pool.struct_def(struct_id);
2670        assert_eq!(retrieved.name, name_str);
2671    }
2672
2673    #[test]
2674    fn test_pool_reserve_multiple_structs() {
2675        let pool = TypeInternPool::new();
2676        let interner = ThreadedRodeo::default();
2677
2678        // Reserve multiple IDs
2679        let id1 = pool.reserve_struct_id();
2680        let id2 = pool.reserve_struct_id();
2681        let id3 = pool.reserve_struct_id();
2682
2683        assert_eq!(id1.pool_index(), 0);
2684        assert_eq!(id2.pool_index(), 1);
2685        assert_eq!(id3.pool_index(), 2);
2686        assert_eq!(pool.len(), 3);
2687
2688        // Complete them in any order (here: reverse)
2689        for (i, id) in [(2, id3), (1, id2), (0, id1)] {
2690            let name_str = format!("__anon_struct_{}", i);
2691            let name = interner.get_or_intern(&name_str);
2692            let def = StructDef {
2693                name: name_str,
2694                fields: vec![],
2695                posture: Posture::Affine,
2696                is_clone: false,
2697                thread_safety: gruel_builtins::ThreadSafety::Sync,
2698                destructor: None,
2699                is_builtin: false,
2700                is_pub: false,
2701                file_id: gruel_util::FileId::DEFAULT,
2702                is_c_layout: false,
2703            };
2704            pool.complete_struct_registration(id, name, def);
2705        }
2706
2707        // All three should be registered
2708        assert_eq!(pool.stats().struct_count, 3);
2709    }
2710
2711    // Compile-time assertion that TypeInternPool is Send + Sync
2712    fn assert_send_sync<T: Send + Sync>() {}
2713
2714    #[test]
2715    fn test_pool_is_send_sync() {
2716        assert_send_sync::<TypeInternPool>();
2717    }
2718
2719    // ========================================================================
2720    // ADR-0084: thread-safety inference tests
2721    // ========================================================================
2722
2723    /// Phase 2 verification: every primitive integer / float / bool /
2724    /// char / unit / never type is intrinsically `Sync`.
2725    #[test]
2726    fn thread_safety_primitives_are_sync() {
2727        use gruel_builtins::ThreadSafety;
2728        let pool = TypeInternPool::new();
2729        for ty in [
2730            Type::I8,
2731            Type::I16,
2732            Type::I32,
2733            Type::I64,
2734            Type::U8,
2735            Type::U16,
2736            Type::U32,
2737            Type::U64,
2738            Type::ISIZE,
2739            Type::USIZE,
2740            Type::F16,
2741            Type::F32,
2742            Type::F64,
2743            Type::BOOL,
2744            Type::UNIT,
2745            Type::NEVER,
2746        ] {
2747            assert_eq!(
2748                pool.is_thread_safety_type(ty),
2749                ThreadSafety::Sync,
2750                "{:?} should be Sync",
2751                ty
2752            );
2753        }
2754    }
2755
2756    /// Phase 2 verification: raw pointers are intrinsically `Unsend`,
2757    /// regardless of their pointee.
2758    #[test]
2759    fn thread_safety_raw_ptr_is_unsend() {
2760        use gruel_builtins::ThreadSafety;
2761        let pool = TypeInternPool::new();
2762        let ptr = pool.intern_ptr_const_from_type(Type::I32);
2763        let mutptr = pool.intern_ptr_mut_from_type(Type::U8);
2764        assert_eq!(
2765            pool.is_thread_safety_type(Type::new_ptr_const(ptr)),
2766            ThreadSafety::Unsend
2767        );
2768        assert_eq!(
2769            pool.is_thread_safety_type(Type::new_ptr_mut(mutptr)),
2770            ThreadSafety::Unsend
2771        );
2772    }
2773
2774    /// Phase 2 verification: arrays propagate the element's
2775    /// classification structurally — `[MutPtr(i32); 4]` is `Unsend`.
2776    #[test]
2777    fn thread_safety_array_of_ptr_is_unsend() {
2778        use gruel_builtins::ThreadSafety;
2779        let pool = TypeInternPool::new();
2780        let mutptr_ty = Type::new_ptr_mut(pool.intern_ptr_mut_from_type(Type::I32));
2781        let arr_id = pool.intern_array_from_type(mutptr_ty, 4);
2782        assert_eq!(
2783            pool.is_thread_safety_type(Type::new_array(arr_id)),
2784            ThreadSafety::Unsend
2785        );
2786    }
2787
2788    /// Phase 2 verification: arrays of primitives stay `Sync`.
2789    #[test]
2790    fn thread_safety_array_of_i32_is_sync() {
2791        use gruel_builtins::ThreadSafety;
2792        let pool = TypeInternPool::new();
2793        let arr_id = pool.intern_array_from_type(Type::I32, 8);
2794        assert_eq!(
2795            pool.is_thread_safety_type(Type::new_array(arr_id)),
2796            ThreadSafety::Sync
2797        );
2798    }
2799
2800    /// Phase 2 verification: `Vec(T)` propagates the element's
2801    /// classification.
2802    #[test]
2803    fn thread_safety_vec_of_ptr_is_unsend() {
2804        use gruel_builtins::ThreadSafety;
2805        let pool = TypeInternPool::new();
2806        let mutptr_ty = Type::new_ptr_mut(pool.intern_ptr_mut_from_type(Type::I32));
2807        let vec_id = pool.intern_vec_from_type(mutptr_ty);
2808        assert_eq!(
2809            pool.is_thread_safety_type(Type::new_vec(vec_id)),
2810            ThreadSafety::Unsend
2811        );
2812    }
2813
2814    /// Phase 2 verification: a single raw pointer field on an otherwise
2815    /// `Sync` struct poisons the structural minimum to `Unsend`. The
2816    /// inference is what `validate_consistency` would write into the
2817    /// struct's `thread_safety` field.
2818    #[test]
2819    fn thread_safety_struct_with_ptr_field_infers_unsend() {
2820        use crate::types::StructField;
2821        use gruel_builtins::ThreadSafety;
2822
2823        let pool = TypeInternPool::new();
2824        let interner = ThreadedRodeo::default();
2825        let mutptr_ty = Type::new_ptr_mut(pool.intern_ptr_mut_from_type(Type::U8));
2826
2827        // Build a struct manually with the structural minimum
2828        // pre-computed (mirroring what `validate_consistency` does).
2829        let inferred = [Type::I32, mutptr_ty, Type::USIZE]
2830            .iter()
2831            .map(|t| pool.is_thread_safety_type(*t))
2832            .min()
2833            .unwrap();
2834        assert_eq!(inferred, ThreadSafety::Unsend);
2835
2836        let name = interner.get_or_intern("Buf");
2837        let def = StructDef {
2838            name: "Buf".to_string(),
2839            fields: vec![
2840                StructField {
2841                    name: "len".into(),
2842                    ty: Type::USIZE,
2843                    is_pub: false,
2844                },
2845                StructField {
2846                    name: "ptr".into(),
2847                    ty: mutptr_ty,
2848                    is_pub: false,
2849                },
2850            ],
2851            posture: Posture::Affine,
2852            is_clone: false,
2853            thread_safety: inferred,
2854            destructor: None,
2855            is_builtin: false,
2856            is_pub: false,
2857            file_id: gruel_util::FileId::DEFAULT,
2858            is_c_layout: false,
2859        };
2860        let (sid, _) = pool.register_struct(name, def);
2861        assert_eq!(
2862            pool.is_thread_safety_type(Type::new_struct(sid)),
2863            ThreadSafety::Unsend
2864        );
2865    }
2866
2867    /// Phase 2 verification: nested struct-with-pointer propagates
2868    /// `Unsend` through the outer struct via `is_thread_safety_type`.
2869    #[test]
2870    fn thread_safety_nested_struct_with_ptr_is_unsend() {
2871        use crate::types::StructField;
2872        use gruel_builtins::ThreadSafety;
2873
2874        let pool = TypeInternPool::new();
2875        let interner = ThreadedRodeo::default();
2876        let mutptr_ty = Type::new_ptr_mut(pool.intern_ptr_mut_from_type(Type::U8));
2877
2878        // Inner struct holds the pointer — Unsend.
2879        let inner_name = interner.get_or_intern("Inner");
2880        let inner_def = StructDef {
2881            name: "Inner".to_string(),
2882            fields: vec![StructField {
2883                name: "ptr".into(),
2884                ty: mutptr_ty,
2885                is_pub: false,
2886            }],
2887            posture: Posture::Affine,
2888            is_clone: false,
2889            thread_safety: ThreadSafety::Unsend,
2890            destructor: None,
2891            is_builtin: false,
2892            is_pub: false,
2893            file_id: gruel_util::FileId::DEFAULT,
2894            is_c_layout: false,
2895        };
2896        let (inner_sid, _) = pool.register_struct(inner_name, inner_def);
2897        let inner_ty = Type::new_struct(inner_sid);
2898
2899        // Outer wraps inner — should also be Unsend by structural fold.
2900        let outer_inferred = [Type::I32, inner_ty]
2901            .iter()
2902            .map(|t| pool.is_thread_safety_type(*t))
2903            .min()
2904            .unwrap();
2905        assert_eq!(outer_inferred, ThreadSafety::Unsend);
2906
2907        let outer_name = interner.get_or_intern("Outer");
2908        let outer_def = StructDef {
2909            name: "Outer".to_string(),
2910            fields: vec![
2911                StructField {
2912                    name: "tag".into(),
2913                    ty: Type::I32,
2914                    is_pub: false,
2915                },
2916                StructField {
2917                    name: "inner".into(),
2918                    ty: inner_ty,
2919                    is_pub: false,
2920                },
2921            ],
2922            posture: Posture::Affine,
2923            is_clone: false,
2924            thread_safety: outer_inferred,
2925            destructor: None,
2926            is_builtin: false,
2927            is_pub: false,
2928            file_id: gruel_util::FileId::DEFAULT,
2929            is_c_layout: false,
2930        };
2931        let (outer_sid, _) = pool.register_struct(outer_name, outer_def);
2932        assert_eq!(
2933            pool.is_thread_safety_type(Type::new_struct(outer_sid)),
2934            ThreadSafety::Unsend
2935        );
2936    }
2937
2938    /// Phase 2 verification: ref/mut-ref types inherit the referent's
2939    /// classification. A `Ref(MutPtr(T))` is `Unsend`.
2940    #[test]
2941    fn thread_safety_ref_inherits_referent() {
2942        use gruel_builtins::ThreadSafety;
2943        let pool = TypeInternPool::new();
2944        let mutptr_ty = Type::new_ptr_mut(pool.intern_ptr_mut_from_type(Type::U8));
2945        let ref_id = pool.intern_ref_from_type(mutptr_ty);
2946        assert_eq!(
2947            pool.is_thread_safety_type(Type::new_ref(ref_id)),
2948            ThreadSafety::Unsend
2949        );
2950
2951        let ref_i32 = pool.intern_ref_from_type(Type::I32);
2952        assert_eq!(
2953            pool.is_thread_safety_type(Type::new_ref(ref_i32)),
2954            ThreadSafety::Sync
2955        );
2956    }
2957}