diff options
Diffstat (limited to 'TODO.md')
| -rw-r--r-- | TODO.md | 30 |
1 files changed, 20 insertions, 10 deletions
@@ -194,12 +194,22 @@ and pushed there. ### in flight when this was written (not on the branch; re-brief if missing) these were running as agents in worktrees. if their branches were not merged before the session ended, the work is lost and each item below must be started -again from its description in the open list. +again from its description in the open list. a zip of the repository with every +worktree is uploaded to the owner after each update. - spec verification (`tests/conformance/`), see "verify both compilers". - x86-64 jit phase 1, see "a hand-written x86-64 jit". -- zc0 structs, enums and `?:`, see "zc0 structs". - runtime line tables and backtraces, see "runtime source positions". - bounds checking with `unbounded`, see "bounds checking". +- the cross-reference report, see "a cross-reference report". +- four small fixes: std member hovers, `capture.end`, bare sibling static calls, `void main` in run-only images. +- the `defer` statement, see "`defer` statement". + +### decisions +- no structs in zc0 (owner's call): zc0 stays the small trusted root of the + bootstrap. so the compiler keeps its parallel arrays; the records refactor + becomes grouping and documenting them (see that item). enums and `?:` in zc0 + stay optional and low priority. +- agents: sonnet 5.5 for the jobs; do not cancel running agents. ## open, easiest first @@ -207,19 +217,19 @@ sorted by how hard each is to implement; the final full suite stays last. items with an architecture note carry the design worked out so far, enough to brief someone without re-reading the code. -- [ ] hover on standard-library members (`std.Text.itoa`, `std.array_contains`, a group name) returns nothing in the language server, though completion shows the same documentation; a struct static function in the document itself does show its hover. find where `do_hover` resolves qualified library members and fix it, with a hover check. -- [ ] `std.capture.end` writes its terminator at `rt.outbuf[rt.outn]`; when more than 256 characters were captured, `outn` equals the capacity and the 0 lands one word past the buffer. check and fix, with a regression program. -- [ ] a bare call to a sibling static function inside a struct's method (`lim()` for a static `lim`) reports `lim is used but never defined`; resolve it like the bare struct constants now are (`mstruct` in the declarations part). -- [ ] a run-only image accepts `void main()` from zc0 but the self-hosted compiler rejects it against `run.zc`'s `int main();` prototype; make the two agree. +- [ ] (in flight) hover on standard-library members (`std.Text.itoa`, `std.array_contains`, a group name) returns nothing in the language server, though completion shows the same documentation; a struct static function in the document itself does show its hover. find where `do_hover` resolves qualified library members and fix it, with a hover check. +- [ ] (in flight) `std.capture.end` writes its terminator at `rt.outbuf[rt.outn]`; when more than 256 characters were captured, `outn` equals the capacity and the 0 lands one word past the buffer. check and fix, with a regression program. +- [ ] (in flight) a bare call to a sibling static function inside a struct's method (`lim()` for a static `lim`) reports `lim is used but never defined`; resolve it like the bare struct constants now are (`mstruct` in the declarations part). +- [ ] (in flight) a run-only image accepts `void main()` from zc0 but the self-hosted compiler rejects it against `run.zc`'s `int main();` prototype; make the two agree. - [ ] remaining zc0 gaps: string literals over 4095 bytes, `embed` of an array type inside a function, `x as T` with more than a single name (`x as int*`), zc0 silently accepting `.u` on a parameter, and integer literals (`010` is 8 in zc0, 10 in the compiler; zc0 saturates on overflow). `docs/history/43-stdlib/notes.md` still says zc0 reserves u/m/d: add a pointer forward rather than rewriting history. - [ ] (in flight) verify both compilers against `docs/SPEC.md` with a fresh agent that did not write it: expectations derived from the spec text before running anything, one program per rule under `tests/conformance/<section>/`, a runner `tests/conformance/run.sh`, each failure classified as self-hosted bug, zc0 bug, or spec error, and appendix b confirmed item by item. then add the runner to `tests/test.sh`. - [ ] fix the implementation bugs listed in `docs/SPEC.md` appendix b, once confirmed: `memdebug scrub;` rejected; enum members can't reference earlier members; a plain function redefinition silently shadows the first; a prototype after the definition hides it; `?:` with one int and one float branch gives garbage; `bool` not normalised; float global initializers rejected; a global initializer naming another global uses its address; `include std;` inside a function body; syntax-error recovery hiding later errors; unterminated `/*` and string literals; `x as int * y`; prototypes needing parameter names; `len(char*)` with typecheck off; duplicate `case` labels; local `const` in constant expressions; constant expressions without precedence (`1+2*3` is 9). fix the prose items the appendix lists too. -- [ ] a cross-reference report: for every global of the compiler, the functions that write it and the ones that only read it. build it from the language server's references (`textDocument/references` on `build/bin/zclsp`, so scoping is exact) and classify each reference as a write when an assignment operator, `++` or `--` follows it. output under `build/xref/`; a committed baseline and a `--check` mode that fails when a new writer of a global appears, so it can run in ci. do it after the records refactor, which reshapes the globals. -- [ ] (in flight) zc0 structs (word-sized fields only, matching the self-hosted layout of one word per field in order; reject packed small fields, nested structs by value, array fields, methods, constants and generics with clear messages), struct values, arrays of structs, `p.f`/`p->f`/`xs[i].f`, `rt.new(T)`, `sizeof`; plain enums (`name.B` access, the name as an int type); `c ? a : b` with c precedence and right associativity. zc0 needs just enough type tracking to know a name's struct. its output for the genome sources must stay byte-identical. tests `tests/programs/zc0struct.zc`, `zc0enum.zc`, `zc0cond.zc`; `make parity` should compare many more programs afterwards. this blocks the records refactor. -- [ ] (needs zc0 structs) group the compiler's parallel per-local arrays (`lname`, `ltype`, `lstate`, `lhome`, `lown`, `lslot`, `ltag`, `lpos`, `lnsz`, `lleaf`, ...; many are declared as `word[NL] x_initial; word x = x_initial;` and grow) into one record per local with accessor functions, so a branch snapshot is one block copy and each field's invariant is written once next to the record. then symbols, then types. the genome must keep reaching its fixpoint and the analyzer's behavior must not change (rejection suites and program checks identical). +- [ ] (in flight) a cross-reference report: for every global of the compiler, the functions that write it and the ones that only read it. build it from the language server's references (`textDocument/references` on `build/bin/zclsp`, so scoping is exact) and classify each reference as a write when an assignment operator, `++` or `--` follows it. output under `build/xref/`; a committed baseline and a `--check` mode that fails when a new writer of a global appears, so it can run in ci. +- [ ] optional, low priority: plain enums and `?:` in zc0 (both are small and produce ordinary code), so parity covers more programs. no structs in zc0 (decided). +- [ ] group the compiler's parallel per-local arrays (`lname`, `ltype`, `lstate`, `lhome`, `lown`, `lslot`, `ltag`, `lpos`, `lnsz`, `lleaf`, ...) without structs: declare each family together in one place, with one comment block stating each array's meaning and invariant (for example when `lhome` is meaningful), route snapshot and restore of the family through one pair of functions, and use the cross-reference report to confirm nothing else writes them. then symbols and types the same way. fixpoint and analyzer behavior unchanged. - [ ] continue removing `unchecked` from the runtime. a reverted trial showed: `copy_w`/`fill_w` pass the analyzer when their gate stores go through `syscall(SYS_COPY, ...)`, `dgrow` with `own(w, it)` instead of `w[-2] = it`, `dpush` with `own` before the store, `dnew` as is; but the checked versions changed behavior (7 program checks failed, `tests/programs/share.zc` hung with a checked `cons`), so checked code inserts cleanup these functions can't afford. each needs its contract taught to the analyzer. `free`, `recycle`, `recycle_leaf`, `gc` are the allocator and collector the analyzer's own cleanup calls: document them as the irreducible escapes. `src/lib/actor.zc` has about 20 more. - [ ] revise diagnostics, comments and learning/reference prose using professional compiler documentation guidance, preserving lowercase and adding message regressions. touches messages everywhere, so do it when few other compiler changes are in flight. -- [ ] `defer` statement: `defer expr;` or `defer { ... }` runs when the enclosing block ends, on every exit path, in reverse order, evaluating its expressions when it runs (like the c defer proposal), with ownership awareness: a deferred call that consumes an owner is that owner's transfer. architecture: grab the deferred statement's tokens at the `defer` (like `grab()` in the statements part) and keep a stack of [block depth, token list]. replay a list with `src_push(1, q, 0, 0); next(); stmt(); src_pop();`. block ends go through `block_close` (call it there before `exit_vars`, for entries at the current depth, newest first); `kw_return` evaluates its value first, then must replay every entry of the function (depth >= 1) before `exit_vars(1, 0)`; `kw_break` and `kw_continue` replay entries deeper than the loop's depth (`f[2] + 1`) before their `exit_vars`. after an early exit `live = 0`, so the analyzer state changed by the replay is discarded. closures keep their own base index. reject `return`, `break`, `continue` inside a deferred statement. defer must also work in unchecked code (`exit_vars` returns early there, so replay independently of `fchk`). +- [ ] (in flight) `defer` statement: `defer expr;` or `defer { ... }` runs when the enclosing block ends, on every exit path, in reverse order, evaluating its expressions when it runs (like the c defer proposal), with ownership awareness: a deferred call that consumes an owner is that owner's transfer. architecture: grab the deferred statement's tokens at the `defer` (like `grab()` in the statements part) and keep a stack of [block depth, token list]. replay a list with `src_push(1, q, 0, 0); next(); stmt(); src_pop();`. block ends go through `block_close` (call it there before `exit_vars`, for entries at the current depth, newest first); `kw_return` evaluates its value first, then must replay every entry of the function (depth >= 1) before `exit_vars(1, 0)`; `kw_break` and `kw_continue` replay entries deeper than the loop's depth (`f[2] + 1`) before their `exit_vars`. after an early exit `live = 0`, so the analyzer state changed by the replay is discarded. closures keep their own base index. reject `return`, `break`, `continue` inside a deferred statement. defer must also work in unchecked code (`exit_vars` returns early there, so replay independently of `fchk`). - [ ] test blocks: `test "name" { ... }` with `expect(cond)`, compiled only by `zy --test file.zc`, which runs each block and prints one line per test with the position of the first failed `expect`; skipped at parse time otherwise. build it as a library through `run.syntax` and `run.statement_hook`, like `src/lib/actor.zc`. - [ ] (in flight) bounds checking on by default, `unbounded` to turn it off (function prefix, block, top-level `unbounded;`, mirroring `unchecked`), implied by `o3`. architecture: indexing is compiled in the postfix `[` handling of the expressions part; growable arrays already check (`dat`, `range_jz`, the `outside` enhancement, `proven()` removing checks a loop test already did). fixed arrays `T[n]` with a variable index get the same inline check against the constant `n`. pointers get a runtime call (for example `rt.bchk(p, i)`) that checks only when `p` is the start of a live heap block (header check word `p ^ MAGIC`, `paylen`), skipping constant index 0 and sizes the analyzer knows (`lnsz`). reports go through `oob`. the runtime, core, compiler parts, server, `run.zc` and `build.zc` start with `unbounded;`, which zc0 accepts and ignores (genome bytes unchanged). keep std, icon and actor checked unless a function touches raw memory on purpose. measure interpreter time before and after. - [ ] after the fuzzing work, add more general functionality to the standard library. |
