Design: TypeStore Unification - Single Source of Truth for Type Information
Date: 2026-03-11 Status: Approved Related Plans: Plan 084 (TypeStore), Plan 087 (Generics), Plan 064 (Database)
Overview
Consolidate all type registries into a single TypeStore as the source of truth for type information across the compiler. This fixes test_enum_decl_compiles and completes Plan 084's follow-up work.
Problem Statement
Current State - Four Type Storages
| Location | Contents | Owner | Purpose |
|---|---|---|---|
types.rs (TypeStore) | TypeDecl, Fn, Spec, GenericTemplate | Parser, Codegen | Plan 084 unified storage |
type_registry.rs | HashMap<String, Type> | Parser, autovm_persistent | REPL persistence |
infer/registry.rs | TypeDecl, ClassTemplate | InferenceContext | Type inference |
Database.type_info_store | TypeInfo (method names only) | Database | Incremental compilation |
Issues
- Data duplication - TypeDecl stored in 3 places
- Sync complexity - Changes must propagate to multiple registries
- Incomplete data -
TypeInfoonly has method names, not field types - Missing enum support - EnumDecl not registered anywhere
- Failing test -
test_enum_decl_compilesfails because enums aren't registered
Target Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ TypeStore - Single Source of Truth │
│ Arc<RwLock<TypeStore>> │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ type_decls: HashMap<AutoStr, Rc<TypeDecl>> │
│ enum_decls: HashMap<AutoStr, Rc<EnumDecl>> ← NEW │
│ fn_decls: HashMap<Name, Rc<Fn>> │
│ spec_decls: HashMap<AutoStr, Rc<SpecDecl>> │
│ generic_templates: HashMap<String, ClassTemplate> │
│ type_aliases: HashMap<AutoStr, AutoStr> │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Key Methods │
│ │
│ register_type_decl(decl) // Creates ClassTemplate too │
│ register_enum_decl(decl) // NEW │
│ lookup_type_decl(name) → Option<Rc<TypeDecl>> │
│ lookup_enum_decl(name) → Option<Rc<EnumDecl>> │
│ get_class_template(name) → Option<&ClassTemplate> │
│ is_type(name) → bool // Unified type check │
│ get_type(name) → Option<Type> // For REPL │
│ get_enum_variant_value(enum, variant) → Option<i32> │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Consumers │
│ │
│ Parser ────────────► type_store.write() (register types) │
│ Codegen ───────────► type_store.read() (lookup types) │
│ InferenceContext ──► type_store.read() (type inference) │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Deprecated → Removed │
│ │
│ ✗ type_registry.rs (simple) → TypeStore │
│ ✗ infer/registry.rs → TypeStore │
│ ✗ Database.type_info_store → TypeStore │
│ │
└─────────────────────────────────────────────────────────────────────┘Key Design Decisions
1. Use Rc<T> for Shared Immutable References
Type declarations are immutable after definition. Using Rc<TypeDecl> allows:
- Cheap cloning (just increment ref count)
- No lifetime issues when returning from behind
RwLock - Multiple consumers can hold references simultaneously
rust
pub struct TypeStore {
type_decls: HashMap<AutoStr, Rc<TypeDecl>>,
enum_decls: HashMap<AutoStr, Rc<EnumDecl>>,
// ...
}
impl TypeStore {
pub fn lookup_type_decl(&self, name: &AutoStr) -> Option<Rc<TypeDecl>> {
self.type_decls.get(name).cloned() // Clones Rc, not TypeDecl
}
}2. Use ClassTemplate from vm/generic_registry.rs
Reuse existing generic infrastructure instead of custom GenericTemplate:
- Already tested and working
- Maintains compatibility with Codegen
- Supports field type substitution
3. Separate Storage for EnumDecl
Enums have different semantics from TypeDecl:
- No instantiation (no
Point{x:1}syntax) - Variants have associated values
- Pattern matching support needed
Implementation Steps
Step 1: Add EnumDecl to TypeStore
Scope: Small | Risk: Low
Changes:
- Add
enum_decls: HashMap<AutoStr, Rc<EnumDecl>>toTypeStore - Add
register_enum_decl(),lookup_enum_decl(),is_enum(),get_enum_variant_value() - Update Parser to register enums in
type_store - Update Codegen to lookup enum variants from
type_store
Expected Result: test_enum_decl_compiles passes
Step 2: Merge infer/registry.rs into TypeStore
Scope: Medium | Risk: Medium
Changes:
- Move
ClassTemplategeneration toTypeStore.register_type_decl() - Update
InferenceContextto querytype_storeinstead of owntype_registry - Mark
infer/registry.rsas deprecated - Update all
lookup_type_decl()callers
Challenges:
- Lifetime issues when returning references from
RwLock - Solution: Return
Rc<TypeDecl>instead of&TypeDecl
Step 3: Merge type_registry.rs into TypeStore
Scope: Small | Risk: Low
Changes:
- Add
is_type()andget_type()convenience methods toTypeStore - Update Parser to use
type_store.is_type()instead oftype_registry - Update
autovm_persistent.rsto usetype_store - Mark
type_registry.rsas deprecated
Step 4: Deprecate Database.type_info_store
Scope: Medium | Risk: Medium
Changes:
- Add
type_store: Arc<RwLock<TypeStore>>to Database - Update Indexer to register to
type_store - Update consumers to use
type_storedirectly - Mark
type_info_storeas deprecated - Provide backward-compatible
type_info_store()view method
Success Criteria
- ✅
test_enum_decl_compilespasses - ✅
test_combined_type_enum_spec_compilespasses - ✅ All existing tests pass (999+ tests)
- ✅ No duplicate type storage
- ✅ Single
TypeStoreAPI for all type queries - ✅ REPL type persistence works
Risk Assessment
| Risk | Mitigation |
|---|---|
| Breaking existing code | Incremental steps, each step tested |
| Performance regression | Rc is cheap; benchmark if concerned |
| Missed call sites | Grep for all registry usages before removal |
Files Changed
| File | Change Type |
|---|---|
types.rs | Major enhancement |
parser.rs | Update type registration |
codegen.rs | Update type lookup |
infer/context.rs | Remove local type_registry |
infer/registry.rs | Deprecate, then remove |
type_registry.rs | Deprecate, then remove |
database.rs | Add type_store, deprecate type_info_store |
indexer.rs | Register to type_store |
autovm_persistent.rs | Use type_store |
Timeline
- Step 1: ~1 hour (enum support)
- Step 2: ~2-3 hours (infer/registry merge)
- Step 3: ~1 hour (type_registry merge)
- Step 4: ~2 hours (Database migration)
Total: ~6-7 hours