aboutsummaryrefslogtreecommitdiff
path: root/TODO.md
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 10:47:30 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 10:47:30 +0000
commit4a22cf28742bf33577678de494aa09ab60a3c820 (patch)
treed9133c7ab431fd3f41ef7713a869d23be4539244 /TODO.md
parentMerge four small fixes: std hovers, capture terminator, bare static calls, vo... (diff)
parentzc: add the defer statement (diff)
Merge the defer statement; todo: tick it, add its follow-ups
Diffstat (limited to 'TODO.md')
-rw-r--r--TODO.md4
1 files changed, 2 insertions, 2 deletions
diff --git a/TODO.md b/TODO.md
index c4b6c6e..e5a91a1 100644
--- a/TODO.md
+++ b/TODO.md
@@ -294,7 +294,6 @@ worktree is uploaded to the owner after each update.
- 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".
-- the `defer` statement, see "`defer` statement".
### decisions
- no structs in zc0 (owner's call): zc0 stays the small trusted root of the
@@ -317,7 +316,6 @@ brief someone without re-reading the code.
- [ ] 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.
-- [ ] (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.
@@ -327,6 +325,7 @@ brief someone without re-reading the code.
- [ ] variadic arguments and `std.print`. design: a pack is passed as one hidden argument, a caller-built read-only growable-array handle (`fixed T[?]` view: [len, cap, items, element size] in the caller's frame, so `len(a)` and `a[i]` work unchanged and nothing is allocated). `...int a` is a typed pack; several packs per function when their element types differ (`void f(...int a, ...char b)`): each extra argument goes to the pack whose element type matches its static type, an ambiguous match is an error, packs come after the fixed parameters. `...any args` packs each value with a kind code (int, float, char, string, bstr, pointer, ...) and its type record: `any` is a small built-in record { kind, value, type }. `...*any args` makes the call comma-free: after the fixed arguments, the parser reads unary expressions until `)`, commas optional, so `std.print("x = " name ", age " age "\n");` works and binary expressions must be parenthesized. `std.print(...*any args)` prints each argument by its kind (`put_n`, the float printer, `put_s`, `put_bs`, a character for `char`, hex for pointers).
- [ ] x86-64 jit phase 2: compile o3 regions to x86-64 (port `rcompile` and the region register allocation; regions now run on the region interpreter and are 2 to 4 times slower than their plain versions on this jit) and inline enhancements (lower the optimized micro-ops to x86 registers as the arm64 path does, instead of calling `xrun`). validate with `tests/drivers/jit_x64_test.sh` and `tests/bench/bench.sh`, which now has an x86-64 jit column. add the driver to `tests/test.sh` on x86-64 linux.
- [ ] `memdebug`'s `obj_base` searches only 64 words back for a block header, so under `memdebug on` an interior access more than 64 words into a block (`buf[255]` on a 256-word block) reports "bad heap pointer". find the header from the buddy allocator's size classes instead of a bounded scan, with a regression program.
+- [ ] `defer` follow-ups (small): accept `defer if (...) ...;` and other single statements besides an expression; decide whether deferred code may name an owner declared after the `defer` (now rejected by the lexical rule; one line in `defer_one`).
- [ ] build the llvm core by default when `llvm-config` is available (the makefile falls back to the interpreter otherwise), so x86 hosts get a native tier and the language server stops being interpreted; keep `ZYNOLLVM=1` and `-DZYINTERP` as the opt-outs.
- [ ] stack-effect checking: every function and `asm` block declares or is inferred to have a fixed data-stack effect, and the compiler reports a block whose paths leave the stack unbalanced. the first step of a proving scaffold.
- [ ] declared side effects at module boundaries (globals touched, syscalls or not), checked against the effect masks the compiler already computes.
@@ -339,6 +338,7 @@ brief someone without re-reading the code.
- [ ] run the full suite once everything above is done (`make test JOBS=4`; `make check` is the quick one), fix what it finds, and only then tick this item.
## done in the sorted list
+- [x] `defer expr;` and `defer { ... }`: tokens grabbed at the `defer`, recompiled inline at every exit (block end through `block_close`, `return` after its value, `break`/`continue` for entries deeper than the loop), newest first; only defers reached so far register. names are lexical (a local declared after the `defer` is hidden in the replay). a deferred `rt.free(p)` is `p`'s transfer to the analyzer. each entry replays in the analyzer mode it was written in; closures get their own base through `ctx_push`. `return`, `break` and `continue` that would leave a deferred statement are rejected. a failed entry reports once. limit 256 pending per function (`DFR_MAX`). spec 4.14, learn section 34, `tests/programs/defer.zc`, seven reject fixtures.
- [x] four small fixes: hover on standard-library members (`hover_embedded_sym` sends std/icon symbols to the library lookup; `bs_hover` renders a member hit), `capture.end`'s terminator (the buffer has `cap + 1` words; latent, since the allocator's power-of-two slack hid it; `tests/programs/capture_long.zc`), bare sibling static calls inside a struct's methods (`mstruct` set for every non-generic struct; the struct's own constant or static beats a top-level name, a local beats both; `tests/programs/struct_static_call.zc`, spec 5.5.5), and `void main()` in run-only images (`run.zc` declares an untyped `fn main();`; spec 5.2.5).
- [x] a hand-written x86-64 jit, phase 1 (`ZYJIT_X64` in `src/core/core.c`, default on x86-64 linux; `-DZYINTERP` and `make build/bin/zyi` give the interpreter). r12 memory, r13 data stack, r14 return stack, r15 table, rbx state; stack slots addressed with a virtual offset so r13 moves only at calls and exits; shares the table, coverage map, store hook and invalidation with the arm64 jit. enhancements are called through `xrun`, o3 regions run on the region interpreter (`ZYNOO3=1` runs their vm code through the jit instead). 179 of 179 programs agree with the interpreter (`tests/drivers/jit_x64_test.sh`), the self-compile is byte-identical and about 7x faster (1.5 s against 11 s), and the rejection, learn, icon, actor, lsp and hint suites pass on it.