1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
|
- [x] read the compiler, runtime, vm cores, language server, and build tools.
- [x] verify formatted markdown and code in vs code hovers, including bundled server builds.
- [x] add mandatory documentation for every built-in type.
- [x] use `/* ... */` comments as documentation, including comments for individual variables and arguments on the same line.
- [x] run focused tests between changes and avoid repeating the full suite.
- [x] check every core source file through the lsp document protocol with its expected source dependencies; reproduce and fix editor diagnostics, including those in the compiler (`src/compiler/parts`, checked as one document).
- [x] rename current i/o and string helpers consistently, including `puts` → `put_s`, `putn` → `put_n`, and `scat` → `cat_s`; update compiler references, examples, tests, and documentation.
- [x] review passing a temporary owned return to an output function; preserve the restriction if it is needed for lifetime safety, otherwise fix it with regression coverage.
- [x] draft `docs/BORROW.md` with a concrete plan for a less intrusive borrow checker and lifetime analyzer.
- [x] grow collector mark work without losing pending objects or counting compiler storage as user objects on the default map.
- [x] verify more than 64 generic declarations, more than 128 instantiations, more than 32 captures, and more than 8 nested closures on the default map.
- [x] move source files into appropriate subdirectories of `src/`, tests into subdirectories of `tests/`, and documentation and designs into `docs/`.
- [x] consolidate windows build, run, and test entry points into one interactive `tinyvm.bat`.
- [x] add a matching interactive `tinyvm.sh` for linux, with direct command mode.
- [x] remove stale planning references from current documentation and code.
- [x] consolidate useful memory-limit information, then remove `CEILINGS.md` if redundant.
- [x] reject stale lsp diagnostics after closing and reopening a document.
- [x] make raw native stack picks obey the same address bounds as the portable backends.
- [x] show array, pointer, struct, enum, alias, and function types in hover previews.
- [x] show inferred types for untyped variables, function returns, arguments, and other inferred declarations.
- [x] verify inference for 600 functions and 5000 locals without silently dropping later edits.
- [x] grow stable frame segments and preserve older local addresses through collection on native, microcore, and javascript.
- [x] verify stable frame growth with llvm 23 and fix the macos libc helper-name conflict.
- [x] inventory fixed addresses and capacities across native, microcore, javascript, and lsp backends in docs/design/memory.md.
- [x] grow execution data and return stacks on native, microcore, and javascript, with explicit runtime reservations and collection after relocation.
- [x] verify operand and return stack growth on llvm and check invalid requests and denied growth without corrupting the vm.
- [x] repeat growth checks on a small generated map.
- [x] verify growing frames on small generated maps.
- [x] preserve all generic parameter names and inference state across nested compilation; infer later function arguments.
- [x] grow assembly and remaining parser scratch storage.
- [x] preserve long lsp request identifiers, grow json and header scratch buffers, and reset tagged-enum records between document checks.
- [x] grow image-building output independently of the building host’s genome slot while preserving logical image addresses.
- [x] grow native code-address metadata and preserve warm calls and invalidation after bulk rewrites.
- [x] restore missing javascript float opcodes and binary32 conversions, then compare focused arithmetic and formatting tests across backends.
- [x] grow program emission and image-building capacity without relocating live code or globals.
- [x] correct the lexer’s float-buffer load and read long float spellings without clipping their exponents.
- [x] point documented image boot headers at the symbol table after packed documentation.
- [x] polish the learning guide and verify its examples against their documented output.
- [x] fix reproducible bugs and apply useful optimizations with regression coverage.
- [x] support build-time adjustment through a consistent generated layout.
- [x] finish growing all compiler tables and execution stacks during execution, preserving live references; heap growth alone does not satisfy this requirement.
- [x] test changed limits, memory growth, invalid configurations, and agreement between image and host layout.
- [x] document available controls and any remaining fixed constraints precisely.
- [x] update builds, runners, editor bundles, tests, and links for the new paths, keeping the repository root focused on entry points.
- [x] preserve useful commands, propagate failures, and handle paths containing spaces.
- [x] verify runner behavior on available hosts and document untested platform behavior.
- [x] polish the learning guide and reference documentation; keep examples executable.
- [x] replace obsolete build commands and distinguish current behavior from historical notes.
- [x] make comments consistent and keep section overviews separate from unrelated declarations.
- [x] keep documentation and comment prose lowercase, matching the older style; preserve case-sensitive code and identifiers.
- [x] preserve full 64-bit integer literals, including values outside the single-word literal format.
- [x] grow stage 0 source input and image output beyond their initial capacities.
- [x] execute javascript vm and micro enhancement recipes with the same slot and output rules as the native hosts.
- [x] add basic bounds and lifetime analysis that warns about provable out-of-bounds access and use after free.
- [x] test definite failures and safe cases so uncertain behavior does not produce a definite warning.
- [x] show known size and lifetime information when hovering over an array or pointer.
- [x] fix the macos first-call crash in native mutual o3 recursion and verify it with a focused regression.
- [x] preserve nonfatal bounds and lifetime warnings across native, microcore, javascript, and both extension hosts.
- [x] verify block documentation, inferred pointer types, and lifetime metadata in the real editor.
- [x] replace quadratic hint deduplication with a growing set and verify large javascript documents.
- [x] make compact compiler maps grow past startup pressure and update obsolete fixed-limit test expectations.
- [x] fix darwin stdio variadic argument placement exposed by native integration tests.
- [x] index local names and preserve shadow chains to avoid rescanning all earlier locals.
- [x] keep cast bounds conservative and infer aggregate aliases as pointers to their stored addresses.
- [x] allow run-only heaps to grow and separate freestanding physical backing from logical vm limits.
- [x] complete final integration checks and refresh generated editor artifacts after the final fixes.
- [x] stop the leftover background test terminal and ensure focused tests exit cleanly.
- [x] show position-aware known values in variable and constant hovers; discard facts after unknown writes and effects.
- [x] verify value previews with focused compiler and editor tests and refresh the editor package.
- [x] separate section comments from declarations, exclude section headings from hover docs, and attach trailing declaration comments correctly.
- [x] make owned-object assignments to globals consume m or u source references through an automatic u transfer, with regression coverage.
- [x] explain inferred lifetimes in hovers, including ownership and borrow sources and where variables are passed up or down.
- [x] support explicitly typed closure returns with fn:type(args) syntax and show the declared result in hovers.
- [x] default compiler hints to blue informational squiggles and support positional hints on, small, and off directives.
- [x] add definition and reference navigation across the current document and included files.
- [x] stream diagnostics and cancel stale check contexts immediately when source changes, keeping editor feedback responsive.
- [x] optional, lowest priority: add a configurable pretty-printer with formatting behavior similar to clang-format; implementation is not required to finish the other work.
- [x] verify embedded runtime, compiler, server and standard library source navigation in generated editor hosts.
- [x] verify relative and nested includes, diagnostics on include strings, and explicit `include std;` without automatic imports.
- [x] finish compiler-backed lsp completions with signatures, documentation, scope, fuzzy relevance and consumed variables last and crossed out.
- [x] repair embedded-source hovers for every declaration in comma-separated and same-line declarations, including constants and backing arrays.
- [x] put section overview comments next to the structures they describe; give initial closure capacities their own descriptions.
- [x] describe base and encoded opcodes inside asm and vm blocks without treating comments, operands or ordinary expressions as instructions.
- [x] describe std in include std;.
- [x] replace fixed-array type syntax [N]T with T[N] in the compiler, bootstrap parser, maintained sources, tests and documentation.
- [x] refresh generated compiler and editor hosts and verify hovers and fixed-array syntax across the available backends.
- [x] use T[?] for growable arrays and T[] for fixed arrays with an inferred declaration-time length; migrate maintained sources and docs.
- [x] support array initializer lists, zero filling and index designators such as { 1, [5] = 0 }, including nested arrays.
- [x] support first-class array compound literals such as (int[]){1,2,3}, with argument decay and lifetime-safe temporary storage.
- [x] add //: non-documentation comments to the compiler, bootstrap compiler and source-only hovers; mark organizational source comments explicitly.
- [x] extend cached analysis results for repeated hover, navigation, semantic-token and completion requests, with source and include invalidation.
- [x] format local hovers as (type) let.lifetime name; // local, retaining transfer details without redundant ownership prose.
- [x] document compiler built-ins, including symbol-table columns such as sval, and expose their source comments in built-in hovers.
- [x] add static to syntax highlighting and show its form and meaning in hover.
- [x] audit built-in runtime signatures for complete source documentation and add hover help for len_s and transfer helpers.
- [x] infer the owned return transfer so returning a local owner does not require an explicit u* recast.
- [x] expand the standard library last with word-string suffix checks and packed-byte length, equality, search, and suffix helpers.
- [x] remove 45 `unchecked` annotations from compiler, runtime, standard library, and language-server helpers where checked ownership contracts and bootstrap/runtime tests proved them safe; give include paths an explicit consumed `.d` lifetime, extract checked format-file parsing, and make navigation walk parent paths in place.
- [x] remove all 12 remaining compiler `unchecked` function annotations and generate checked native struct-return stubs; declare owned token/source returns and consumed generic buffers, make internal allocation lifetimes explicit, and accept same-scope replacement moves into released owners with expired-source diagnostics.
- [x] implement BORROW.md temporary slots and cleanup for proved non-escaping calls, including returned aliases, nested calls, branches and early returns.
- [x] infer parameter read/write/retain/consume/returned-alias effects, resolve recursive summaries to a fixed point, and keep unknown/unchecked/native effects conservative.
- [x] record compiler ownership, alias, bounds, and lifetime facts; infer last-use borrow endings and unambiguous consuming-call transfers.
- [x] track field and index projections, proven disjoint accesses, and invalidation after array mutation or unknown effects.
- [x] improve definite lifetime/bounds diagnostics with cause and later-use locations, retaining hard errors and conservative unknown results.
- [x] make mutable closure captures share storage so counters and stateful callbacks observe updates.
- [x] modernize void-like examples and document the already implemented generated layouts and growing compiler tables without reintroducing fixed limits.
- [x] verify every BORROW.md acceptance case, cleanup/allocation counts and safe neighbors across available backends; update BORROW.md to describe implemented guarantees.
- [x] add deterministic compiler/runtime/stdlib fuzzing for 128 generated expressions, 300 scoped allocations and GC, token-input immutability, and 32 randomized Text/Bytes cases on native and microcore; fuzz LSP open/change, semantic tokens, and `include std` across 32 documents.
- [x] extend deterministic fuzz coverage to 320 malformed and byte-mutated compiler inputs across native and microcore, the same generated runtime/stdlib checks across native, microcore, JavaScript and WebAssembly, and 96 open/change/close/reopen LSP cache cycles in one server session.
- [x] cache packed-byte haystack and prefix lengths once in `Bytes.starts`; verify its results in the stdlib fuzz run and measure a 7.6% median improvement over 7 paired runs of 800,000 prefix checks.
- [x] repair the byte-load benchmark to auto-size its run-only image and use a monotonic timer with repeated samples, so benchmark failures cannot masquerade as timings.
- [x] before further implementation, add acceptance, rejection and regression coverage for every unchecked TODO item, then keep working until all of those tests and the required integration checks pass.
- [x] suppress magic-number hints for literals immediately preceded by an inline block comment, explain this escape in the hint and documentation, and test nearby-comment and uncommented cases.
- [x] hint on unused named function arguments at the parameter, respect hints on/small/off, and cover writes, reads, shadowing, methods, closures, generics and repeated `_` discards.
- [x] infer untyped word strings as `char*` and packed strings as `bstr`; cover reassignment, grouped declarations, parameters, returns, completions and hovers.
- [x] support comma-separated includes, repeated imports, and diagnostics for unknown modules and empty entries.
- [x] repair F12 navigation for every built-in, including `s_to_n`, so definitions open the correct embedded source (`rt.zc` for runtime functions); verify native, JavaScript and WebAssembly LSP hosts and the extension's virtual-source provider.
- [x] normalize all variable hovers to show the type first, including globals, fixed arrays, parameters, fields, inferred and embedded-source declarations; verify `(char*) let hello // global;` and `(word[20000]) let prog // global;` at declarations and uses without a trailing `; type ...`.
- [x] audit maintained string arguments, variables and results so string data uses explicit string types instead of word; preserve word for actual raw cells and buffers.
- [x] implement the compiler API namespace before further standard-library expansion. Outside `compiler.zc`, require `include run;` and `run.name` (for example `run.evals`); bare compiler names are allowed only inside the compiler module. Reserve `core` for VM core APIs.
- [x] implement matching runtime and language-server namespaces before standard-library expansion. Outside their respective modules, require `include rt;` with `rt.name`, or `include lsp;` with `lsp.name`.
- [x] support array receiver calls for len, cap, push, pop and resize, with element-aware lsp completions; reject fixed-array mutation through receiver and free calls, and verify 15 focused accept/reject cases on native and microcore plus lsp completion regressions.
- [x] add consistent `--help` output to the compiler, lsp and other command-line toolchain programs; describe available options and input modes, return successfully without reading source or starting the server, and cover help output and invalid options with focused tests.
- [x] add array operations for push, pop, find, map, filter and related common transformations; support receiver calls such as `x.push(4)` and `x.map(fn (e) { ... })`, include array-method completions, and document and test fixed-array and growable-array behavior.
- [x] install enhancement descriptors before starting self-hosted run-only images; verify arithmetic, generic array methods and reference pop ownership in standalone microcore images.
- [x] indent `case` and `default` labels one level deeper than their `switch` when pretty-printing, and update formatter documentation and regressions.
- [x] propagate inferred types across calls and return flows, including inferring a caller's untyped parameter from a callee's typed parameter and a caller's return type from the returned callee result; cover chains such as `mklist(n)` and `return l` with compiler and hover regressions.
- [x] allow `fixed` and `const` to qualify the same declaration, with consistent parsing, immutable fixed-array semantics, documentation and accept/reject coverage.
- [x] suggest qualified namespace members after prefixes such as `rt.` and verify completion ordering, signatures and documentation for each imported module.
- [x] show library-specific hover documentation on each module name in `include name;`, including comma-separated includes.
- [x] add LSP quick fixes for missing module imports and related compiler diagnostics; insert the relevant explicit include without changing unrelated source text.
- [x] verify collector-growth accounting relative to existing allocations and investigate conservative roots retained after clearing large global arrays.
- [x] allow repeated `_` discard placeholders in parameters and patterns, including tagged-enum payloads and catch-all cases; warn when a bound discard value is read.
- [x] add generic native and hosted microcore thread contexts with isolated vm state, channels, cancellation and cleanup; verify 14 native and 12 microcore fixtures while keeping actor-specific code in its library.
- [x] give core built-ins and every other toolchain module their own namespace and require an explicit include to access qualified exports: `include core;` enables `core.name`, with the same rule for all other modules. remove implicit toolchain imports, migrate maintained sources and generated code, and verify missing-include diagnostics, completions, hovers and navigation before expanding the standard library.
- [x] add a generic include-time syntax-extension interface so imported libraries can extend compiler parsing and lowering without module-specific compiler code; keep all extension hooks generic and inactive unless the corresponding library is included.
- [x] implement `include actor;` as a library that injects actor syntax into the compiler through the generic include-time extension interface: `actor` declarations in place of `struct`, implicit `this`, and generic `Actor<I,O>` input/output types. Provide asynchronous parallel execution with independent state and multiple instances; keep actor-specific code out of `compiler.zc` and every file it includes or reads by default, and cover syntax activation, execution, messaging and type safety with tests.
- [x] implement and integrate `src/lib/icon.zc` with structured runtime syntax transformations, template matching, execution inspection and stepping, with explicit contracts and comprehensive tests.
- [x] change the producer of the genome header (`build/core/genome.h`) to emit a c translation unit that embeds the compiler, core and runtime zc sources with a `#embed` directive instead of concatenating them into a 1.5 mb generated header; keep the `.zc` sources as the single source of truth and extend the fixpoint check in `make genome` so it still verifies the emitted genome without materializing a header at all.
- [x] close the two ownership completeness limits that stop the compiler compiling itself, so `make genome` completes from a clean checkout: allow an owned value held in a local to move into another local when the destination's previous owner is released in the same statement list, and allow a global to hold a bounded borrow of a local so an output stream can be redirected at a scratch buffer without a module-level variable. also give `rt.code_blocks = m*block;` a supported spelling and fix the `only a named owned object can be recast` diagnostic, which does not describe the real constraint. written up in `docs/history/BOOTSTRAP-OWNERSHIP.md`, which also records a verified partial fix that removes five of the eight current errors.
- [x] bring documentation, examples, learn entries, history notes and test fixtures in line with the include and namespace format and the other language changes made alongside it: qualify runtime built-ins as `rt.put_s`, `rt.put_n`, `rt.put_c`, `rt.eof` and `rt.get_c`, require the explicit `include` each module needs, and remove the remaining bare builtin names so every shipped sample, every documented snippet and every expected-output fixture compiles and runs against the current compiler. the learn guide and `README.md` snippets are checked by the suite, so they are the gate, but the prose and the non-exercised samples are not.
- [x] add accept, reject and regression coverage for the actor and icon libraries once each is finished: for actors, syntax activation through the include-time extension, spawn/recv/send across input and output types, multiple instances of one actor, parallel execution, implicit `this`, and confirmation that no actor code is reachable from the compiler or anything it reads by default; for icon, find/replace over real syntax, undeclaring names, changing types, adding and rewriting blocks, evaluating parts of an expression, and stepping execution while reading locals and globals, including the failure cases for each.
## handoff: where things stand
this section is written so work can resume from the repository alone. the
branch is `ccr-0054c02e-dfdvnd`; everything listed as done below is committed
and pushed there.
### how the work is organized
- start with `ONBOARD.md` (reading order and exact commands), then
`docs/SPEC.md` (the language reference), then this file.
- the compiler is twelve files listed in order in `src/compiler/parts`; their
concatenation is the old `compiler.zc`. the genome sources (runtime, core,
compiler parts, language server) must stay inside zc0's subset: zc0 has no
structs, enums or `?:` yet (in progress, see below); `u`, `m`, `d` are
ordinary names; parenthesize mixed `&&`/`||`; global arrays as `word[64] x;`.
- focused checks, never the whole suite until the last item:
`make` (ends with `fixpoint: N words`), `bash tests/runerr.sh build/bin/zy
tests/errors` (and `typeerrors`, `langerrors`), `python3
tests/drivers/learn.py build/bin/zy`, `python3 tests/drivers/icon_test.py
build/bin/zy`, `python3 tests/drivers/actor_test.py build/bin/zy`,
`ZC_TEST_TIMEOUT_SCALE=4 node tests/drivers/lsp_test.js build/bin/zclsp`,
`ZC_TEST_TIMEOUT_SCALE=4 node tests/drivers/hint_fixes_test.js
build/bin/zclsp`, `make parity` (zc0 against the self-hosted compiler; by
design differences live in `tests/parity.known`). on x86 the language server
is interpreted and slow; `ZC_TEST_TIMEOUT_SCALE` stretches the deadlines.
- work was done by delegating each item to a separate agent in its own git
worktree (outside the repo, under `/workspace/tinyvm-wt/`) and merging its branch.
every brief started with "read ONBOARD.md first" plus the sections relevant
to the job, stated the invariants (fixpoint, byte-identical images where a
change should not alter output, zc0 subset), the focused checks, and asked
for a report of files changed, tests run and open questions.
- prose everywhere is lowercase with plain punctuation; `//` line comments,
`/* */` only between code; a comment above a declaration is its hover text.
### vision and direction
the coordinator's view of where zc stands and where it should go, recorded so
the reasoning behind the open items survives the session.
- what zc gets right: the compiler lives in memory next to the program, so
`eval`, parse words, immediate functions and library-defined syntax (actors
with no actor code in the compiler) fall out of the architecture: forth's
strength with c's syntax. the ownership analyzer infers most of what other
languages make you annotate (it already infers that `skip(s)` returns a
borrow of `s`). unchecked code falls back to a conservative collector instead
of undefined behavior. the language server is the real compiler in check
mode, so diagnostics cannot drift from compilation. one image runs on the
interpreter, microcore, javascript, wasm, arm64 jit and llvm.
- what holds it back: trust and visibility. until `docs/SPEC.md` there was no
spec, and the two implementations disagreed (`&&`/`||`) without anyone
noticing. run-time failures carry no position and a wild read can exit 0.
"checked" is not purely static: turning the checker on inserts cleanup that
can change behavior (the reverted `unchecked` trial hung `share.zc`).
friction: the toolchain's own sources must stay in zc0's subset; word-per-
character strings cost eight times the memory of `bstr` and split every api
in two (`put_s` and `put_bs`); the compiler's state is global parallel
arrays; on x86 the language server is interpreted (about 18 s to check the
compiler).
- priorities, in order: things that turn wrong behavior into a located,
reproducible failure come first, because everything else (fuzzing, a proving
scaffold, outside users) depends on them: runtime line tables, bounds
checking, and the spec with its parity runner and conformance suite. then the
language features that make zc pleasant (`defer`, destructors, variadics and
`std.print`, test blocks, borrow returns for declarations). then speed (the
x86-64 jit).
- bootstrap philosophy: zc0 is an independent implementation in c that never
retires (like go 1.4, unlike rust's downloaded stage 0 or ocaml's and zig's
committed binaries), so the system builds from source with only a c compiler
and no committed blob. its value is being small, so it grows only where a
feature is cheap and produces no code of its own (tags, recasts, byte
strings, maybe enums and `?:`), never structs. the parity runner is a small
form of wheeler's diverse double-compiling: the two front ends check each
other.
- managing the compiler's global state: (1) a cross-reference report of
writers and readers per global, checked in ci; (2) group each family of
parallel arrays in one place with its invariants written once, and route
snapshot and restore through one pair of functions; (3) the split into phase
files (done). do not try to make the state non-global: the single-pass design
is fast because of it; the goal is visible ownership of state, not removal.
- debugging, beyond line tables and bounds checks: every effect a program has
goes through one syscall gate, so record and replay is cheap here (log each
gate result, replay a run exactly, then add tracing or watchpoints at will).
once line tables exist, a debug adapter for vscode (breakpoints, stepping,
locals) on top of the interpreter is the next step.
- a proving scaffold (the owner asked for an opinion): yes, but check before
prove. a written semantics comes first (the spec, then a small executable
reference evaluator). grow it out of the analyzer that already exists rather
than beside it. order: stack-effect checking for every function and `asm`
block (the commonest invisible bug on a stack machine); declared side
effects at module boundaries (globals touched, syscalls or not) checked
against the effect masks the compiler already computes; `requires`/`ensures`
contracts compiled to run-time checks under a switch, with the easy ones
discharged statically from the value facts. keep the trusted base small and
outside the self-compiling loop (a prover inside the genome can approve its
own mistakes, as the fixpoint approved the precedence bug); prefer
translation validation of each compile over proving the compiler whole.
- native code: the hand-written jit exists only for arm64; the llvm backend
(`-DZYLLVM`) is the portable native tier but the default build leaves it
out. cheap win: build the llvm core by default whenever `llvm-config` is
found. the x86-64 port of the hand-written jit is worth it for compile
latency (microseconds per block against milliseconds for llvm), which
matters for short programs and the language server.
### feature decisions (owner's requirements, settled)
- bounds checking is on by default; `unbounded` (used like `unchecked`:
function prefix, block, top-level statement) turns it off; `o3` implies
`unbounded`.
- `defer` runs at the end of the current block on every exit path, like the c
defer proposal, plus ownership awareness; destructors (`fn drop()`) exist as
well.
- borrow returns: inferred from the body wherever possible; any spelling for
declarations must stay unobtrusive (proposed: `borrows s` after the
parameter list).
- `string` is a built-in alias of `char*` (done); c typedefs (done).
- variadics: count and per-argument types available, several packs per
function when element types differ (`void f(...int a, ...char b)`), a mixed
`...any` pack, and `...*any` for comma-free calls; `std.print` is a global,
javascript-like print built on it.
- struct-level untyped constants (done), typo suggestions for members (done).
- every new agent brief starts with "read ONBOARD.md first".
### done in this session (all merged)
- build from source with c23 `#embed`, everything generated under `build/`,
`ONBOARD.md`, the embed declaration (`embed "f" as [type] name;`).
- language: untyped `const` (local, top level, inside structs), c precedence
for `&&`/`||` (the self-hosted compiler had them swapped), `typedef` and the
built-in `string` alias, struct constants (`std.Text.MAX`).
- editor: quick fixes for hints (rename to `_`, make const, `<<`, stack
buffer), missing-import fixes for every module (rt, core, run, loaded files,
and std, icon and libraries listed in `src/lib/modules.txt`, scanned from
source, including static groups like `std.Text.itoa`), typo fixes for
functions, module exports, fields and static members.
- zc0: packed byte strings, `u`/`m`/`d` as names, local `embed`, and the asm
enhancement bug parity found.
- tooling: `make parity` (`src/tools/parity.sh`), the compiler split into
phase files, `docs/SPEC.md`, standard-library api docs and
`examples/std_tour.zc`, temporary notes removed, `PROPOSALS.md`.
### 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. 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".
- 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
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.
- [ ] (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.
- [ ] (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.
- [ ] (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.
- [ ] (in flight) a hand-written x86-64 jit, ported from the arm64 one in `src/core/core.c` (the `#ifdef ZYJIT` section; `ZYJIT` is only defined for `__aarch64__`). phase 1: a separate macro (for example `ZYJIT_X64`), shared architecture-neutral parts (translation table `tab`, coverage `cov`, invalidation `be_drop`, block boundaries, dispatcher) and an x86 emitter. registers: r12 memory base, r13 data stack, r14 return stack, r15 translation table, rbx state block (the arm64 x19..x23); system v calls to c helpers. semantics identical to the interpreter (wrapping, 0/1 comparisons, literal encoding, stack and `MW` checks, retranslation after stores into code). o3 regions take the portable path in phase 1. validate every `tests/programs`, `examples` and `examples/learn` program on interpreter and jit with identical output (a driver under `tests/drivers/`), the rejection suites, icon and actor fixtures; report timings for a compute example and for compiling the compiler. make it the default x86-64 linux core only when everything matches; `-DZYINTERP` still forces the interpreter. phase 2: o3 regions and inlined enhancements. note the llvm backend (`-DZYLLVM`) is the other native path; the default build could also enable it whenever `llvm-config` is found.
- [ ] struct destructors: a struct may declare `fn drop()`, called wherever the compiler frees an owned value of that type, before the memory is released. architecture: the compiler frees owned locals in `freevar` (analyzer part), called from `exit_vars`, reassignment and explicit `rt.free`/`rt.recycle` calls (`s_free`, `s_recycle` handling in the call code); when the local's static type is a pointer to a struct with a `Name.drop` method, emit a call to it first. objects freed by the collector or inside unchecked code do not run `drop`: document that. first users: file descriptors in `std.Host`, processes in `actor.zc`.
- [ ] (in flight) runtime source positions. architecture: at each statement start (`stmt()`) record (code address `here`, line); per function a permanent data block [entry, end, name, file, count, pairs...] linked into a runtime list (`rt.line_blocks`), or appended to run-only images by `build_image` like the symbol blob. `lines on;`/`lines off;` like `docs on;`; on for user programs, off for the genome unless the growth is small. a new syscall op returns the return-stack pointer (the return stack is in vm memory, 0x1A4100..0x1B40FF, growing up); implement it in `src/core/core.c`, `src/micro/micro.c`, `src/js/tinyvm.ts` and the wasm port. `rt.trace()` walks the return stack, printing ` at file:line in function` for words inside a known function range. the runtime's fatal reports (`oob`, bad heap pointer, double free, out of memory, frame overflow) print the trace after their unchanged first line. check mode skips all of it.
- [ ] borrow returns tied to an argument. inference already works for a callee defined before the call: `char* skip(char* s) { ...; return s + i; }` makes `let r = skip(t); rt.free(t); r[0]` an error. the gap: when the call comes before the callee's definition, nothing is reported and the program reads freed memory. extend the deferred effect requirements (`effect_require`, `effect_finish` at unit end) so a forward-called function whose return turns out to alias a parameter produces the same error. for bodyless declarations, externs and function types, add an unobtrusive spelling, for example a `borrows s` clause after the parameter list (`char* skip(char* s) borrows s;`), checked against the body when one exists.
- [ ] 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).
- [ ] 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.
- [ ] `requires`/`ensures` contracts on functions, compiled to run-time checks under a switch, with the cases the value facts settle (constants, simple ranges) checked statically.
- [ ] record and replay: a core option that logs every syscall gate result to a file and replays a run from the log exactly, for reproducing failures and adding tracing after the fact.
- [ ] a debug adapter for vscode (breakpoints, stepping, locals) on the interpreter, built on the line tables.
- [ ] reduce the cost of word-per-character strings: measure, then decide between making `bstr` the default string, unifying the two apis behind one name, or both.
- [ ] continue fuzzing the compiler, analyzer, runtime, standard library, lsp, build tools and generated artifacts, including analyzer properties, memory-limit edges, compiler image mutation and long-running lsp memory use; feed generated programs to `make parity` for differential fuzzing between zc0 and the self-hosted compiler; fix discovered bugs.
- [ ] keep improving the borrow checker: fewer annotations in ordinary code, clearer messages naming both the owner and the conflicting use, and new fixtures for each rule that changes.
- [ ] 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] remove temporary markdown files (`TASKLIST.md`, `docs/design/handoff.md` deleted; `BOOTSTRAP-OWNERSHIP.md` moved to `docs/history/`).
- [x] verify every command in `ONBOARD.md` from a clean checkout and fix the ones that drifted.
- [x] add `string` as a built-in alias of `char*`.
- [x] untyped constants inside structs, reached as `Text.MAX` and `std.Text.MAX`.
- [x] typo suggestions for field and member names, with the change-to quick fix.
- [x] packed byte strings and `u`/`m`/`d` in zc0, byte-identical genome.
- [x] `docs/SPEC.md`, with the zc0 subset (appendix a) and disagreements and bugs (appendix b).
- [x] `make parity`: zc0 against the self-hosted compiler on every program zc0 accepts.
- [x] c typedefs.
- [x] the compiler split into phase files listed in `src/compiler/parts`.
- [x] standard-library api documentation, `examples/std_tour.zc`.
|