Plan: Fix Remaining Type Checker Errors (109 → 0) C
Goal
Run cargo run -- run examples/simple.naml with zero type errors.
Error Categories (109 total)
┌────────────────────────────────────┬───────┬────────────────────────────────────────────────┐
│ Category │ Count │ Root Cause │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Array methods/fields missing │ ~15 │ No built-in .push(), .length for [T] │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Generic struct literal typing │ ~10 │ Type params not substituted in struct literals │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Option field access without unwrap │ ~25 │ Code bugs in simple.naml │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Exception struct literals │ ~2 │ Exceptions not handled in infer_struct_literal │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Async/promise return types │ ~15 │ Async functions not wrapped in promise │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Spawn block return type │ ~5 │ Always returns promise │
├────────────────────────────────────┼───────┼────────────────────────────────────────────────┤
│ Generic type parameter issues │ ~35 │ Various T substitution failures │
└────────────────────────────────────┴───────┴────────────────────────────────────────────────┘
Implementation Phases
Phase 1: Array Built-in Methods and Fields
File: namlc/src/typechecker/infer.rs
Changes:
- In infer_method_call(), add handling for Type::Array(elem) before the type_name extraction:
- push(item: T) → Type::Unit (validates item unifies with elem)
- pop() → Type::Option(elem)
- clear() → Type::Unit
- len() → Type::Int
- In infer_field(), add handling for Type::Array:
- length → Type::Int
Phase 2: Exception Type Struct Literals
File: namlc/src/typechecker/infer.rs
Change: In infer_struct_literal(), handle TypeDef::Exception same as TypeDef::Struct:
- Extract exception fields
- Type-check each field value
- Return appropriate type
File: namlc/src/typechecker/types.rs
Optional: Add Type::Exception(ExceptionType) variant for semantic clarity.
Phase 3: Async Function Promise Wrapping
File: namlc/src/typechecker/mod.rs
Change: In collect_function(), if func.is_async, wrap return type in Type::Promise:
let return_ty = if func.is_async {
Type::Promise(Box::new(return_ty))
} else {
return_ty
};
Phase 4: Spawn Block Return Type Inference
File: namlc/src/typechecker/infer.rs
Change: In infer_spawn(), infer the block's actual return type:
fn infer_spawn(&mut self, spawn: &ast::SpawnExpr) -> Type {
let body_ty = self.infer_block(&spawn.body);
Type::Promise(Box::new(body_ty))
}
Phase 5: Generic Type Parameter Context
Files:
- namlc/src/typechecker/infer.rs
- namlc/src/typechecker/mod.rs
- namlc/src/typechecker/env.rs
Changes:
- Add type parameter tracking to TypeEnv:
type_params: HashMap<Spur, Type> // Maps T -> concrete type or type var - In check_function(), register function type parameters in env before checking body
- In infer_struct_literal(), when creating struct types:
- Look up type args from annotation or infer from field values
- Apply substitution to field types using Type::substitute()
Phase 6: Fix Code Issues in simple.naml
File: examples/simple.naml
Some errors are actual code bugs, not type checker issues:
- Option field access without .unwrap() (lines 280, 289, 292, etc.)
- Assigning option to T without unwrapping
Fix: Add .unwrap() calls where needed, or use proper pattern matching.
Implementation Order
┌──────────┬───────────────────────────┬────────────┬────────────┐
│ Priority │ Phase │ Complexity │ Impact │
├──────────┼───────────────────────────┼────────────┼────────────┤
│ 1 │ Phase 1 (Array methods) │ Low │ ~15 errors │
├──────────┼───────────────────────────┼────────────┼────────────┤
│ 2 │ Phase 2 (Exceptions) │ Low │ ~2 errors │
├──────────┼───────────────────────────┼────────────┼────────────┤
│ 3 │ Phase 3 (Async wrap) │ Low │ ~10 errors │
├──────────┼───────────────────────────┼────────────┼────────────┤
│ 4 │ Phase 4 (Spawn return) │ Low │ ~5 errors │
├──────────┼───────────────────────────┼────────────┼────────────┤
│ 5 │ Phase 6 (Fix simple.naml) │ Medium │ ~25 errors │
├──────────┼───────────────────────────┼────────────┼────────────┤
│ 6 │ Phase 5 (Generic context) │ High │ ~50 errors │
└──────────┴───────────────────────────┴────────────┴────────────┘
Files to Modify
┌────────────────────────────────┬────────────┐
│ File │ Phases │
├────────────────────────────────┼────────────┤
│ namlc/src/typechecker/infer.rs │ 1, 2, 4, 5 │
├────────────────────────────────┼────────────┤
│ namlc/src/typechecker/mod.rs │ 3, 5 │
├────────────────────────────────┼────────────┤
│ namlc/src/typechecker/env.rs │ 5 │
├────────────────────────────────┼────────────┤
│ examples/simple.naml │ 6 │
└────────────────────────────────┴────────────┘
Verification
After each phase:
cargo run -- run examples/simple.naml 2>&1 | grep -c "^ ×"
Final success:
cargo run -- run examples/simple.naml
Risk Assessment
- Phase 1-4: Low risk, isolated changes
- Phase 5: High risk, may require significant refactoring of type inference
- Phase 6: May need to significantly simplify generic code in simple.naml
Alternative Approach
If Phase 5 proves too complex, we can:
- Simplify examples/simple.naml to avoid complex generics
- Mark generic type inference as a known limitation
- Focus on getting basic programs to work first