From be5ba01dcc5bfe314390dffd24a4162507c31fbb Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Thu, 19 Mar 2026 20:31:35 +0000 Subject: [PATCH] =?UTF-8?q?feat(type-resolution):=20consolidate=20Phases?= =?UTF-8?q?=2010=E2=80=9313=20into=20Milestone=20D=20=E2=80=94=20enhance?= =?UTF-8?q?=20completeness=20with=20full=20integration=20test=20coverage?= =?UTF-8?q?=20across=2011=20languages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- type-resolution-roadmap.md | 198 ++++++++++++------------------------- 1 file changed, 63 insertions(+), 135 deletions(-) diff --git a/type-resolution-roadmap.md b/type-resolution-roadmap.md index 2450f0aa6..2284d8a5c 100644 --- a/type-resolution-roadmap.md +++ b/type-resolution-roadmap.md @@ -51,122 +51,63 @@ The goal is not to build a compiler. The goal is to support high-value static an - Fixpoint iterates until stable (max 10 iterations), enabling chains like `getUser() → .address → .getCity() → city.save()` - Reverse-order copy chains now resolve (`const b = a; const a: User = x` → both resolve) +### Milestone D — Completeness ✅ + +**Shipped in** `feat/type-resolution-milestone-d` (PR #387). Consolidated original Phases 10–13 into 3 balanced phases. + +#### Phase A: Fixpoint Completeness ✅ + +- Post-fixpoint for-loop replay (ex-9B): `pendingForLoops` collection + replay after fixpoint resolves iterable types +- Object destructuring via `fieldAccess` items (TS/JS `object_pattern`, Rust `struct_pattern`) — no new `destructure` PendingAssignment variant needed +- Extracted `resolveFixpointBindings()` helper with exhaustive switch + `classDefCache` memoization + +#### Phase B: Inheritance & Receivers ✅ + +- `BuildTypeEnvOptions` interface replacing positional params for `buildTypeEnv` +- Heritage pre-pass constructing `parentMap` from tree-sitter query matches (not graph edges — heritage-processor runs in parallel) +- MRO-aware `walkParentChain()` (depth 5, cycle-safe BFS) for `resolveFieldType` and `resolveMethodReturnType` +- `this`/`self`/`$this`/`Me` receiver substitution via `substituteThisReceiver` hook +- Go `inc_statement`/`dec_statement` write-access queries + +#### Phase C: Branch-Sensitive Narrowing ✅ + +- Null-check narrowing (`!= null`, `!== undefined`, `is not null`) via position-indexed `patternOverrides` +- Supported for TS, Kotlin, C# — renamed `PATTERN_BRANCH_TYPES` → `NARROWING_BRANCH_TYPES` +- Bug fix: Kotlin narrowing required 3 fixes in `jvm.ts` (AST node type `equality_expression`, anonymous `null` node, `nullable_type` parameter fallback) + +#### Deferred from Milestone D + +- **Type predicates (13A):** Cross-function analysis for niche TS `x is User` feature — deferred +- **Swift parity (11D):** tree-sitter-swift Node 22 issues — all Swift work consolidated to Phase S +- **Positional destructuring (12C):** Python/Kotlin/C#/C++ tuple-position-to-field mapping — deferred +- **Discriminated union narrowing (13C):** Needs tagged union metadata not in SymbolTable — deferred + +#### Integration Test Coverage + +17 fixture directories, 23 describe blocks, 705 lines of test code covering all 11 languages: +- Grandparent MRO (depth-2 C→B→A): TS, JS, Kotlin, C#, C++, Java, PHP, Python, Ruby +- Object destructuring: TS, JS +- Struct destructuring: Rust +- Post-fixpoint for-loop replay: TS, JS +- Go inc/dec write access +- Null-check narrowing: TS, C#, Kotlin + --- ## Open Phases -### Phase 10: Loop-Fixpoint Bridge +### Phase S: Swift Parity -**Supersedes Phase 9B.** For-loop element bindings run during `walk()` before the fixpoint. Variables typed by the fixpoint are invisible to for-loop extraction. +**Blocked on** tree-sitter-swift Node 22 compatibility. -**Problem:** -```typescript -const users = getUsers(); // fixpoint: users → User[] -for (const u of users) { // walk-time: users untyped → u unresolved - u.save(); // missed CALLS edge -} -``` - -**Approach:** Post-fixpoint for-loop replay. During `walk()`, store for-loop AST nodes whose iterable is unresolved. After fixpoint completes, replay `extractForLoopBinding` on those nodes using the now-resolved iterable types. - -**Scope:** -- Infrastructure: `pendingForLoops` collection + replay (~20 lines in `type-env.ts`) -- No extractor changes — reuses existing `extractForLoopBinding` -- Swift: add for-loop element binding (currently missing) -- Nested for-loops with field-dependent iterables are NOT replayed (handled by call-processor chain resolution) - -**Risks:** AST node lifetime (safe — nodes valid within `buildTypeEnv` lifetime). - -**Impact: High | Effort: Low-Medium** - ---- - -### Phase 11: Inheritance & this/self - -Four items sharing infrastructure. - -#### 11A: MRO-aware field and method lookups - -**Problem:** `lookupFieldByOwner` only finds direct fields. Inherited fields (`Admin extends User`, field on `User`) don't resolve. - -**Approach:** Pre-compute `parentMap: Map` from EXTENDS + IMPLEMENTS edges. Pass to `buildTypeEnv`. Update `resolveFieldType` and `resolveMethodReturnType` to walk parent chain on miss (max depth 5, cycle-safe, first-match-wins for diamond inheritance). - -Includes IMPLEMENTS edges so interface-declared fields/methods resolve (Java, Kotlin, C#). - -#### 11B: this/self in fixpoint - -**Problem:** `this.field` and `this.method()` emit pending items with `receiver: 'this'`, but `scopeEnv.get('this')` returns `undefined`. - -**Approach:** At collection time during `walk()`, resolve the enclosing class name immediately via `findEnclosingClassName()` and substitute it as the receiver. Covers both `fieldAccess` and `methodCallResult`. No fixpoint changes needed. 5-10 lines per extractor. - -#### 11C: Go inc/dec write access - -**Problem:** `obj.field++`/`obj.field--` produce `inc_statement`/`dec_statement` — write-access tracking doesn't see them. - -**Approach:** Add these node types to call-processor write detection (~5 lines). - -#### 11D: Swift assignment chains - -**Problem:** Swift has no `extractPendingAssignment` — copy/callResult/fieldAccess/methodCallResult don't work. - -**Approach:** Implement `extractPendingAssignment` for Swift covering all 4 binding kinds. - -**Risks:** Performance of parent chain walking in fixpoint (bounded by `n_pending × depth × iterations`). Interface method ambiguity (mitigated by checking direct class first, parents on miss). - -**Impact: Medium-High | Effort: Medium** - ---- - -### Phase 12: Destructuring - -**Problem:** `const { address, name } = user` produces no bindings — LHS is a pattern node, not an identifier. - -**Approach:** Add `{ kind: 'destructure', source, bindings: [{ varName, fieldName }] }` to `PendingAssignment`. In the fixpoint, when source's type resolves, look up each field via `lookupFieldByOwner` and bind each variable. - -Works without Phase 11A (direct fields only). Phase 11A MRO enhances it to resolve inherited fields too. - -**Phased delivery:** - -| Sub-phase | Scope | Languages | -|-----------|-------|-----------| -| **12A** | Object destructuring | TS/JS (`object_pattern`) | -| **12B** | Struct pattern destructuring | Rust (`struct_pattern`) | -| **12C** (deferred) | Positional destructuring | Python, Kotlin, C#, C++ — needs tuple-position-to-field mapping | - -Skip: computed properties, rest elements, nested destructuring (`{ address: { city } }` — deferred). - -**Risks:** Nested destructuring requires recursive resolution (explicitly deferred). +- For-loop element binding (from Phase 10) +- Assignment chains: copy, callResult, fieldAccess, methodCallResult (from Phase 11D) +- `guard let` narrowing (from Phase 13B) — uses scopeEnv path, not `patternOverrides` **Impact: Medium | Effort: Medium** --- -### Phase 13: Branch-Sensitive Narrowing - -**Design principle:** Targeted narrowing, not general control-flow analysis. Skip anything that requires a control-flow graph. - -**Phased delivery:** - -#### 13A: Type predicate functions (TS only) - -`function isUser(x: unknown): x is User` — detect `type_predicate` return type. When called in an `if` condition, emit pattern binding for the narrowed parameter. - -#### 13B: Nullability narrowing (TS/Kotlin/C#/Swift) - -`if (x != null)` → strip nullable wrapper in truthy branch. Uses existing `patternOverrides` mechanism (position-indexed, scope-aware). Swift `guard let` uses standard scopeEnv (narrowing persists for rest of function). Swift work requires Phase 11D (assignment chains) first. - -#### 13C: Discriminated union narrowing (deferred) - -`if (shape.kind === 'circle')` → needs tagged union metadata not in SymbolTable. Defer. - -**What we skip entirely:** Full control-flow graph, arbitrary conditional narrowing, `typeof` guards, exhaustiveness checking. - -**Risks:** Scope leakage (mitigated by `patternOverrides` position indexing). Swift `guard let` needs scopeEnv path (different from `patternOverrides`). - -**Impact: Medium | Effort: Medium-High** - ---- - ### Phase 14: Cross-File Binding Propagation **Problem:** `buildTypeEnv` is per-file. Inferred types don't cross file boundaries. @@ -202,19 +143,12 @@ config.validate(); // missed ## Dependency Graph ``` -Phase 10 (loops) ──────────────────────┐ - │ -Phase 11 (MRO + this + Go + Swift) ───┤ - ├──→ Phase 14 (cross-file) -Phase 12 (destructuring) ─────────────┤ - │ -Phase 13 (branch narrowing) ───────────┘ +Milestone D (Phases A, B, C) ✅ ──┐ + ├──→ Phase 14 (cross-file) +Phase S (Swift parity) ───────────┘ -Phases 10–13 are independent of each other. - Exception: Phase 13B Swift (guard let) requires Phase 11D (Swift assignment chains). - Exception: Phase 12 benefits from Phase 11A (MRO) but works without it. -Phase 14 depends on all of 10–13 being stable. -Swift parity threaded through Phases 10–13 incrementally. +Phase S is independent of Phase 14. +Phase 14 depends on Milestone D being stable. ``` --- @@ -222,19 +156,11 @@ Swift parity threaded through Phases 10–13 incrementally. ## Language-Specific Gaps (remaining) ### Swift -- For-loop element binding → Phase 10 -- Assignment chains (copy, callResult, fieldAccess, methodCallResult) → Phase 11D -- Pattern binding → Phase 13B (`guard let`) - -### Go -- `obj.field++`/`obj.field--` write ACCESSES → Phase 11C - -### Rust -- Struct-pattern field destructuring → Phase 12B +- For-loop element binding → Phase S +- Assignment chains (copy, callResult, fieldAccess, methodCallResult) → Phase S +- `guard let` narrowing → Phase S ### All languages -- Inherited field/method resolution → Phase 11A -- `this`/`self` in fixpoint → Phase 11B - Cross-file binding propagation → Phase 14 --- @@ -253,14 +179,18 @@ Field/property maps, deep chains, mixed chains, stdlib passthroughs. Unified fixpoint loop, call-result binding, field access binding, method-call-result binding, arbitrary-depth chain propagation. -### Milestone D — Completeness ← **next** (Phases 10–13) +### Milestone D — Completeness ✅ (Phases A, B, C) -Loop-fixpoint bridge, inheritance walking, `this`/`self` resolution, destructuring, branch narrowing, Swift parity. +Consolidated Phases 10–13 into 3 balanced phases. Loop-fixpoint bridge, MRO-aware inheritance walking, `this`/`self` resolution, object/struct destructuring, null-check narrowing. Kotlin null-check bug fix. Full 11-language integration test coverage. -### Milestone E — Cross-Boundary (Phase 14) +### Milestone E — Cross-Boundary ← **next** (Phase 14) Export-type index, cross-file binding propagation. +### Milestone S — Swift Parity (Phase S) + +For-loop binding, assignment chains, `guard let` narrowing. Blocked on tree-sitter-swift Node 22. + --- ## Open Design Questions @@ -294,8 +224,6 @@ That supports: better call graphs, more accurate impact analysis, stronger AI co ## Summary -**Complete:** Phases 7, 8, 9, 9C — explicit types, constructor inference, loop inference, field/property resolution, deep chains, mixed chains, stdlib passthroughs, comment-based types, unified fixpoint with 4 binding kinds, arbitrary-depth chain propagation across 11 languages. +**Complete:** Phases 7, 8, 9, 9C, Milestone D (A, B, C) — explicit types, constructor inference, loop inference, field/property resolution, deep chains, mixed chains, stdlib passthroughs, comment-based types, unified fixpoint with 4 binding kinds, arbitrary-depth chain propagation, MRO-aware inheritance walking, this/self resolution, object/struct destructuring, null-check narrowing — across 11 languages with full integration test coverage. -**Next:** Phase 10 (loop-fixpoint bridge) → Phase 11 (MRO + this/self + Go + Swift) → Phase 12 (destructuring) → Phase 13 (branch narrowing) → Phase 14 (cross-file propagation). - -Each phase is independently deliverable (except Phase 14 which depends on 10–13 being stable). Swift parity is threaded incrementally through Phases 10–13. +**Next:** Phase 14 (cross-file binding propagation) — the architectural capstone. Phase S (Swift parity) is independent and unblocked once tree-sitter-swift Node 22 is resolved.