Skip to content

Commit 4aaf940

Browse files
hyperpolymathclaude
andcommitted
docs(stdlib): #135 per-file triage punch list (STDLIB-AOT-TRIAGE.md)
Live per-file state of the #135 AOT sweep after slice 1. 9/19 files compile end-to-end; the remaining 10 reduce to 7 distinct feature slices (slice/range index, bare `effect E;`, statement sequencing, trait default bodies, `try`-as-ident, empty-array generalisation, io namespace), each scoped with root cause, dependencies and ordering. The high-value de-risking deliverable so the remaining multi-session #135 work starts from a confirmed plan, not raw symptoms. Refs #128, #135 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 53150ee commit 4aaf940

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

docs/history/STDLIB-AOT-TRIAGE.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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

Comments
 (0)