|
| 1 | +<!-- SPDX-License-Identifier: PMPL-1.0-or-later --> |
| 2 | +<!-- SPDX-FileCopyrightText: 2026 hyperpolymath --> |
| 3 | + |
| 4 | +# Stdlib AOT triage — #135 punch list |
| 5 | + |
| 6 | +**Generated 2026-05-17**, against the compiler at `feat/135a-lambda-expr` |
| 7 | +(origin/main + #131/#134 merged, #133/PR #152 auto-merging, + #135 slice 1 |
| 8 | +`fn(params) => expr`). This is the live per-file state of driving every |
| 9 | +`stdlib/*.affine` through `resolve → typecheck → codegen` (Deno-ESM). |
| 10 | + |
| 11 | +Methodology: `affinescript compile stdlib/<f>.affine -o /tmp/x.js |
| 12 | +--deno-esm`, first error per file, classified by pipeline stage. |
| 13 | + |
| 14 | +## Status snapshot |
| 15 | + |
| 16 | +| File | Stage | Site | Root cause / slice | |
| 17 | +|---|---|---|---| |
| 18 | +| `string` | ✅ OK | — | full pipeline | |
| 19 | +| `Core` | ✅ OK | — | full pipeline | |
| 20 | +| `Ajv` `Crypto` `Grammy` `Network` `Sqlite` `Vscode` `VscodeLanguageClient` | ✅ OK | — | full pipeline (already `module`-declared) | |
| 21 | +| `prelude` | TYPECHECK | `Unify.TypeMismatch (T, Int)` | **Slice 7** — empty-array literal `let result = []` then `result ++ [..]` not generalised | |
| 22 | +| `option` | PARSE | `238:15` `Some(list[1:])` | **Slice 2** — slice/range index `list[1:]` | |
| 23 | +| `collections` | PARSE | `24:35` `take(n-1, list[1:])` | **Slice 2** — same slice syntax | |
| 24 | +| `result` | PARSE | `221:1` `fn try<T>(...)` | **Slice 6** — `try` is a reserved keyword (TRY); used as a fn name | |
| 25 | +| `effects` | PARSE | `5:8` `effect io;` | **Slice 3** — bare `effect <name>;` declaration form | |
| 26 | +| `testing` | PARSE | `302:3` `let total = ...;` then `{` | **Slice 4** — statement sequencing: `let;` followed by a block expr | |
| 27 | +| `math` | PARSE | `354:3` `let total = 0.0;` then `for` | **Slice 4** — same: `let;` followed by `for` statement | |
| 28 | +| `traits` | PARSE | `12:43` `pub fn ne(ref self, ...) -> Bool {` | **Slice 5** — trait method *default bodies* / `ref self` receiver | |
| 29 | +| `io` | RESOLVE | `Resolve.UndefinedVariable` | **Slice 8** — builtin/extern + namespace wiring; interacts with #132/#133 module model | |
| 30 | + |
| 31 | +9/19 already compile end-to-end. The remaining 10 reduce to **7 distinct |
| 32 | +feature slices**, none of which is the #131 angle-bracket or #135-slice-1 |
| 33 | +lambda defect (both fixed). |
| 34 | + |
| 35 | +## Slices (proposed order, each its own PR) |
| 36 | + |
| 37 | +Ordering rationale: do the two-for-one parser slices first (max files |
| 38 | +unblocked per change), keep the model-coupled one (Slice 8) until after |
| 39 | +#133/PR #152 lands, and the typecheck/keyword ones are independent. |
| 40 | + |
| 41 | +### Slice 2 — slice/range index `list[1:]` *(unblocks `option`, `collections`)* |
| 42 | +Postfix index currently accepts `e[i]` only. Add range-index |
| 43 | +`e[a:b]` / `e[a:]` / `e[:b]` / `e[:]` to the postfix-expr rule, lowering |
| 44 | +to the existing slice/`Array` op (check `lib/parser.mly` postfix rule + |
| 45 | +how `typecheck.ml`/codegen model slices — `string` already slices, so a |
| 46 | +lowering target likely exists). Two files, one feature. Independent. |
| 47 | + |
| 48 | +### Slice 3 — `effect <name>;` declaration *(unblocks `effects`)* |
| 49 | +Grammar has `effect_decl` for `effect E { ops }`; `effects.affine` uses |
| 50 | +the bare forward-declaration form `effect io;`. Add the bare form to |
| 51 | +`effect_decl` (empty-op effect) or a dedicated production. Independent; |
| 52 | +small. |
| 53 | + |
| 54 | +### Slice 4 — statement sequencing: `let …;` then block/`for` *(unblocks `testing`, `math`)* |
| 55 | +Both fail where a `let …;` statement is immediately followed by another |
| 56 | +statement that is a block expr (`{ … }`) or a `for`. Root cause is in |
| 57 | +the block/statement-list rule (the `list(stmt)` vs `expr_record_body` |
| 58 | +reduce/reduce noted in the grammar). Needs the statement-sequence rule |
| 59 | +disambiguated so a trailing-semicolon `let` composes with a following |
| 60 | +block/`for`. Independent; medium (touches the known r/r conflict — do |
| 61 | +rigorously, re-check conflict counts before/after). |
| 62 | + |
| 63 | +### Slice 5 — trait method default bodies + `ref self` *(unblocks `traits`)* |
| 64 | +`trait_decl` accepts method *signatures*; `traits.affine` provides |
| 65 | +*default bodies* (`pub fn ne(ref self, ref other: Self) -> Bool { … }`). |
| 66 | +Add optional `fn_body` to trait method items and accept the `ref self` |
| 67 | +receiver form. Independent; medium. |
| 68 | + |
| 69 | +### Slice 6 — `try` as a value identifier *(unblocks `result`)* |
| 70 | +`try` is the TRY keyword (try/catch). `result.affine` defines |
| 71 | +`fn try<T>(…)`. Decide: (a) make `try` a contextual keyword (allow as |
| 72 | +fn name / ident), or (b) rename the stdlib function (e.g. `attempt`). |
| 73 | +(a) is the resolve-at-source fix but wider; (b) is local and safe. |
| 74 | +Recommend (b) unless `try`-as-ident is wanted broadly. Independent; small. |
| 75 | + |
| 76 | +### Slice 7 — empty-array literal generalisation *(unblocks `prelude`)* |
| 77 | +`map`/`filter` do `let result = []; for x in arr { result = result ++ [f(x)] }`. |
| 78 | +`[]` is inferred at a concrete element type (`Int`) and then conflicts |
| 79 | +with `[U]`. Needs the empty-array literal to take a fresh element tyvar |
| 80 | +unified by later use (or an annotation path). Typecheck/inference, not |
| 81 | +parsing. Independent; medium — touch `typecheck.ml` array-literal rule |
| 82 | +with care + regression test. |
| 83 | + |
| 84 | +### Slice 8 — `io` resolution / builtin + namespace *(unblocks `io`)* — **do after #133/PR #152** |
| 85 | +`io.affine` references runtime builtins (`print`, `read_file`, …) the |
| 86 | +static resolver doesn't know. This couples to the module/extern model |
| 87 | +(ADR-011 / #132) and the #133 ownership changes. Sequence it after |
| 88 | +PR #152 (#133) lands so it is solved against the real module model, not |
| 89 | +the interpreter-era flat namespace. Medium; model-coupled. |
| 90 | + |
| 91 | +## After all slices |
| 92 | + |
| 93 | +Then #135 closes; unblocks #136 (CI AOT smoke gate), #137 (multi-module |
| 94 | +integration test), #138 (remove b895374 band-aid). Estimated 3–5 further |
| 95 | +focused sessions for slices 2–8 (each: feature + tests + conflict |
| 96 | +re-verification), per the "rigorous over partial-hack" discipline. |
| 97 | + |
| 98 | +## Done so far (this epic) |
| 99 | + |
| 100 | +| Issue | PR | State | |
| 101 | +|---|---|---| |
| 102 | +| #131 `>>` nested-generic | #149 | merged | |
| 103 | +| #134 prelude unwrap soundness | #150 | merged | |
| 104 | +| #132 namespace ADR-011 | #151 | merged | |
| 105 | +| #133 single-ownership dedup | #152 | auto-merging | |
| 106 | +| #135 slice 1 (`fn(x)=>e`) | this PR | open | |
0 commit comments