Skip to content

Realse v0.2.0 - #296

Merged
zaidejjo merged 201 commits into
mainfrom
dev
Oct 7, 2026
Merged

zaidejjo merged 201 commits into
mainfrom
dev

Conversation

@zaidejjo

@zaidejjo zaidejjo commented Oct 7, 2026

Copy link
Copy Markdown
Owner
  • fix/toml found miscompiles (fix/toml found miscompiles #166)
  • Realse v0.1.5
  • fix/deterministic warning order (fix/deterministic warning order #167)
  • chore/ci release pipeline (chore/ci release pipeline #169)
  • Realse v0.1.6
  • ci: PAT-created releases so downstream triggers fire; ubuntu base for prebuilts (ci: PAT-created releases so downstream triggers fire; ubuntu base for prebuilts #173)
  • feat: multi-platform releases, README refresh, CONTRIBUTING, Apache-2.0 LICENSE
  • chore: drop windows from release assets (compiler does not support it yet)
  • feat: zz setup, shell completions, auto-heal, install --path, CLI progress UI
  • feat/setup fixes (feat/setup fixes #177)
  • feat/upgrade doctor pkg tools (feat/upgrade doctor pkg tools #178)
  • native: yield tail call/return values from if/else branches (native: yield tail call/return values from if/else branches #179)
  • native: heal loop-arena strings at retaining stores
  • tests: regression fixtures for arena-string stores + early call-returns
  • fuzz: v2 shapes behind --shapes v2 (default mode byte-identical)
  • frontend: fold -9223372036854775808 to i64::MIN
  • feat: bitwise operators &, |, ^, ~, <<, >>
  • fix: review follow-ups for bitwise operators
  • native: index-set must not zz_int-wrap string values
  • docs: fuzz v2 shape families in README
  • pm: opt-in [package] zz minimum-compiler requirement
  • docs: VM value-copy design (COW + last-use move)
  • docs: v0.1.6 draft release notes (no tag)
  • fix: assert dev-build marker on stderr in build_e2e
  • bench: move-append elision harness plus pre-optimization baseline
  • analysis: shared move-on-self-reassign eligibility plus tests
  • feat: tuple indexing, len, and bare destructuring
  • fix: formatters preserve bare vs paren destructure form
  • perf: move-on-self-reassign append elision (native + VM)
  • **feat: compound assignment (+= -= *= /= %= = &= |= ^= <<= >>=)
  • feat: generic structs (Box, impl Box[T], inference + methods)
  • fix: box unboxed structs and raw scalars in array/tuple literals (AOT)
  • perf: linear appends via move-on-self-reassign takes (native + VM)
  • fix: raw-scalar fields in unboxed literals + release boxed container temps (AOT)
  • docs: AOT parity fix plan for nested [[str]] + UTF-8 measure
  • fix: AOT parity for nested [[str]] stores and UTF-8 string measure
  • fix: parser typed-decl in while cond, len(dict) native, call-result struct field
  • fix: native sqlz row cN aliases plus struct == boxing and deep equality
  • feat: trailing commas in call args, func params, closure params
  • feat(test): cargo-style grouped output, VM default + AOT engine
  • fix(test): review pass — pipe drains, honest skips, float literals
  • feat(test): kill all five limitations
  • feat(stdlib): native std.term raw-mode primitives
  • feat(stdlib): add term.flush for interactive rendering
  • fix(codegen): free-function method receivers (Bug 8)
  • chore(parity): refresh known-native-failure map
  • fix(codegen): method receivers on call/index/literal/field roots (Bug 8b)
  • Fix checker/runtime/AOT parity bugs: var collision, default args, indexed receivers
  • Default zz build to static; fix native term.flush (dual-buffer deadlock)
  • Add zz build --full (max optimization) and -o output naming
  • Fix compiler gaps: BIT bool coercion, method-shadow calls, selective native calls
  • Fix qualified-alias calls and first-class function values natively
  • feat(arena): add zz_arena bump allocator + compiler bench corpus
  • feat(arena): track zz_arena in Cargo.lock
  • feat(arena): zero-copy lexer tokens (P2a)
  • feat(arena): skip no-op decorator re-expansion (P2b)
  • feat(arena): checker key sharing + result moves (P3)
  • feat(arena): pre-join CallPath dotted names at compile time (P4)
  • docs(arena): P0-P6 record, production notes, open gates (P6)
  • feat(arena-next): pre-size lexer/parser collections (Q1)
  • feat(arena-next): multi-file bench corpus (Q0); mimalloc A/B rejected (Q2)
  • feat(arena-next): move loader seeds into checker (Q3)
  • feat(arena-next): move entry-point seeds into final check (Q4a)
  • bench: single-file 4000-func corpus for loader-overhead isolation
  • feat(arena-scale): borrow clean string literals in lexer (S3)
  • feat(arena-scale): dep-aware module check cache (S1)
  • docs(arena): arena-next record (Q0-Q4, S1, S3, de-scoped S2)
  • fix(aot): plug heap leaks in string concat, elvis, loop locals
  • feat(stdlib,frontend): pure-ZZ map/set/dec/bytes/csv/time-date + match statement arms
  • feat(lang): contextual type aliases with cross-module support
  • fix(aot): link libcurl/libsqlite3 only when reachable natives need them
  • fix(aot): register pure-ZZ map/set/dec/bytes/csv/str/time gaps in drift census
  • fix(fmt): emit import alias before selective item list
  • feat(toolchain): managed Zig backend via zz toolchain install
  • feat(aot): unboxed scalar calling convention for monomorphic functions
  • fix(aot): scope unboxed state out of closures; capture locals in _u bodies
  • test(parity): graduate frame_slots to strict parity
  • feat(aot): elide loop safepoints without concurrency; forward stack-array indices\n\n- Loop-top zz_safepoint() is a codegen barrier: clang cannot fold a\n loop around the opaque call. Programs without task/chan/spawn now\n omit it (it never suspends, only yields the OS thread, so omission\n is always correct). sum 0..5M: 21ms -> 0ms, matching Rust.\n- arr := [pure scalars]; arr[lit] reads forward to the element's raw\n C expression (SROA): no zz_index_get, arithmetic stays raw end to\n end. alloc bench: 7ms -> 1ms. Sound via eager resolution plus\n kill-on-reassign/mention/call/capture/scope-pop and a totality\n guard for trapping division.\n\n4 new tests: safepoint elided/kept, forwarding shape + value,\nstore/push kill behavior.
  • feat(aot): inline str(int/bool) in append chains; elide dead array releases\n\n- New zz_str_append_int/bool runtime helpers: str(i)/str(b) terms in\n s = s + ... chains format straight into the buffer. No cast temp,\n no arena staging, no release. str bench 2-3ms -> 1-2ms.\n- Loop-local stack-promoted arrays that are only read through\n forwarded indices skip zz_release (proven no-op): unpins the dead\n construction for DCE. alloc bench 2ms -> 0ms, matching Rust/Zig.\n- Alias soundness: x = arr kills the source entry (shared-buffer\n mutation through the alias would stale forwarding).\n\n3 new tests: chain shape + value (incl. float fallback), release\nelision, alias push agreement.
  • feat(aot): ascii fast path for str char length zz_str_char_len walked the full UTF-8 decode per call: len() on the 1MB bench string cost ~1ms where Rust does O(1) bytes. A word-at-a-time high-bit test returns byte length for pure-ASCII (~0.05ms) and falls back to the precise walk otherwise. One new test for multibyte agreement.
  • feat(lang): user-defined enums with erasure to objects
  • feat(lang): generic enums, payload chaining, match/if-let hardening
  • fix(aot): guard tuple-pattern unpack behind tag+len test
  • m0: fixture feature tags, edge corpus, parity sweep extended
  • fix(vm): write-through chained index stores
  • pre-m1: quad matrix, normalizer gates, modules parity, benches, IR spec draft
  • spec: lock closure capture semantics (by-reference, per-iteration bindings)
  • fix(aot): parity-safe C flags, explicit NaN guard for int() casts
  • fix(aot): copy-on-write struct headers (value semantics for field writes)
  • fix(vm): always-wrap integer arithmetic (+ - * neg pow loop counters)
  • fix(aot+vm): trap MIN/-1 and MIN%-1 on all legs
  • fix(aot): trap negative integer exponents natively
  • fix(aot): trap out-of-bounds index reads natively
  • WIP: detach-on-mutation for index stores (runtime side)
  • fix(aot): value semantics for all index/field stores
  • fix(aot): propagate main() Result to the exit code
  • fix(aot): fail pg.connect loudly instead of null-handle leniency
  • fix(aot): recognize bare scalar globals as already-unboxed
  • harness: mark IR-deferred lowering bugs in known-failure notes
  • docs: FEATURES edge list matches post-fix strictness
  • fix(aot): invalidate SROA forwarding on nested/field store roots
  • bench: before/after medians around the flags PR (perf-neutral within noise)
  • cleanup close-out: negative-index probe, remaining-divergences report, spec freeze
  • fix(aot): canonical float formatting via the Rust core (spec §4)\n\nC float printing (%.17g + strip, lowercase nan, exponent forms)\ndiverged from Rust Display on 1e21, 1e-7, NaN, MAX, subnormals.\nNew zz_native_rt float_fmt exposes zz_float_format_raw (exact VM\nDisplay rule, unit-tested vectors); strings.c routes all six Display\nsites through it via a weak import + fail-closed trap (never silent\ndivergence). Link gate scans typed nodes for Float (sound: no Any\ntype; JSON prints via its own path); -u force-extracts the object.\nNon-float binaries carry no libstd; --static+floats downgrades (or\nerrors explicitly) with a clear message.\n\nedge_float_format pins 16 vectors in-fixture (incl. 300+ char\nexpansions) + strict quad; edge_float_nan_display flips strict.\nSpec §4 frozen with recorded ground truth. ASan+UBSan clean;\n2000-print loop leak-free.
  • fix(frontend): parse ** on the RHS of * / and %\n\nparse_multiplicative parsed its right operand with parse_unary,\nskipping the parse_power level, so 2 * 42 stopped after the 4\nand died in the call parser. One-line fix; operators.zz gains\nmixed-precedence pins (all agree natively).**
  • fix(aot): recognize raw scalar globals in function bodies (CLASS 2 follow-up)\n\nThe scalar-global fix only covered top-level Decl: function-body\nvalues don't arrive as bare Ident (ident=None), so q8 := v4 still\nemitted (zz_global).i on a raw int64_t. emitted_is_raw_scalar now\nalso matches the emitted C id against scalar globals directly.\nCaught by the fuzz pre-run (member-reference NATFAILs); 60-case\nsample fully green after.
  • probes: evaluation-order fixtures + compound single-evaluation fix\n\nord probes: binop/call-arg order agrees (strict edge_eval_order);\ncompound index evaluates each side once (strict\nedge_compound_index_eval); plain index-store order diverges\nVM-value-first vs native-source-order (quad-split\nedge_index_store_order, spec records source order canonical).\n\nFix: AOT compound home path bound the index expression textually\ntwice (get + set) — side effects fired twice. Now bound once in\n_ci. (My feat(lsp): full LSP server with diagnostics, navigation, and refactoring #8-PR regression; VM always evaluated once.)
  • docs: evaluation order, closure probe report, OOM close-out\n\nSpec §7 records left-to-right + source-order stores (VM plain-store\ndeviation quad-split, changes post-M1); migration table updated.\nCleanup report: closure probe verdicts per rule, OOM documented\ncontract (no live fixture — hostile to CI), CLASS-2 follow-up,\nmath precision closed. FEATURES edge list extended.
  • fix(http): second Ctrl+C force-exits graceful shutdown\n\nFirst press starts the drain; a second press _exit(130)s immediately\nso a keep-alive browser tab can never wedge shutdown. Counter resets\nper listen; drain notice printed on first press. Handler stays\nasync-signal-safe (fetch_add + _exit only).
  • fuzz: v4 shapes (floats, precedence, closures, stores, casts, structs) + batch runner\n\nv4 adds 8 families with labeled output lines (real oracle values:\nfloat Display incl. NaN/INF, mixed * / ** precedence incl. the\nreported parse bug shape, agreeing closure forms, nested/aliased\nstores, strings/interp, compound stores, total casts incl.\nexponent-string parsing, plain structs). Known-open divergences\nexcluded by design (spawn, loop-capture, side-effect store order,\nstruct methods/display, rand, OOM shapes).\n\nBatch mode (--batch + run_batch.sh): K cases per clang build with\nmarker demux and manifest-driven bisect fallback into run.sh.\n100-case validation green. 3 fixed-seed v4 smoke fixtures\n(12 float, 13 precedence, 14 struct), strict in e2e + parity.\nAlso drops the never-valid floats tag (fixture_meta gate was red).
  • lint: let-else for quad natives (clippy unnecessary-unwrap)
  • docs: fuzz README covers v3/v4 shapes + batch mode
  • *fix(aot): pg FFI follows zz_index_set pointer signature + refused-trap test\n\nThe feat(lsp): full LSP server with diagnostics, navigation, and refactoring #8 value-semantics PR changed zz_index_set to take the slot by\npointer but missed the Rust declaration in zz_native_rt pg.rs: the\nstaticlib passed the dict CValue by value where C read a pointer —\nSEGV on every AOT pg.query (empty stderr, exit 139). Declaration now\ntakes mut CValue; dict_insert passes &mut. Caught via ASan+UBSan\n(misaligned 0x6 read through the bogus pointer).\n\npg_aot_leniency updated for the feat(lsp): document highlights with read/write classification #10 trap contract (refused connects\nare fatal now): bad-SQL leniency on a live db still asserted\nVM==AOT; new pg_refused_traps asserts both engines trap loudly.
  • test: tiered CI, batched parity, parallel quad, cached plugin scaffold
  • feat(strings): doubled-brace {{ }} escapes in single + triple strings
  • feat(ir): zzc-codec slice — zz_ir crate, bytecode run/dis/emit, source-order stores
  • chore: drop zz_ir debug example
  • fix: issue sweep — loader seeds, struct globals, shim order, brace-migrated test, cache genesis
  • feat(ir): populate function signatures from HIR during lowering
  • feat(aot): Chunk->C backend behind --chunk (slice-1)
  • Hygiene: fuzz CLASS 1 FIXED (Stale fuzz CLASS 1: tuple-literal boxing now works, remove entry #241) + Rust comparison docs (How is this different from Rust? #197) (Hygiene: fuzz CLASS 1 FIXED (#241) + Rust comparison docs (#197) #259)
  • Imports/loader batch: hard errors, plugin selective, re-export loud fail, docs (Imports/loader batch: hard errors, plugin selective, re-export loud fail, docs #260)
  • Checker DX + syntax helpers: Sherlock diagnostics batch (244-248) (Checker DX + syntax helpers: Sherlock diagnostics batch (244-248) #268)
  • feat(interp): single-line strings expand digit- and paren-led expressions (Semantics: expression interpolation in single-line strings + overflow docs (249, 250) #269)
  • Native correctness: short-circuit, selective consts, detailed traps (239, 240, 251) (Native correctness: short-circuit, selective consts, detailed traps (239, 240, 251) #270)
  • LSP flags, selective diagnostics, always-on completion (255-257) (LSP flags, selective diagnostics, always-on completion (255-257) #271)
  • LSP packages: dep/workspace harvest, full completion, pow census, scan fix (LSP packages: dep/workspace harvest, full completion, pow census, scan fix #272)
  • LSP colors that mean something: call classification, paren-free items (LSP colors that mean something: call classification, paren-free items #273)
  • Parity gate green: all_funcs mirror; struct-literal completion; editor colors (Parity gate green: all_funcs mirror; struct-literal completion; editor colors #275)
  • fix(lsp): advertise inlayHint and references providers (Advertise inlayHint + references providers #281)
  • feat(stdlib): positional str search (find/rfind/starts_with_at/ends_with_at/trim_span)
  • feat(stdlib): str.bytes / bytes.to_str / bytes.to_ints bridge
  • perf(stdlib): memchr scan cores, native str.count
  • fix(ci): drop str.count from KNOWN_CODEGEN_GAPS (C impl now exists)
  • fix(ci): list str_find/str_bytes fixtures in batched parity inventory
  • fix(stdlib): trim_span backward scan usize underflow on short windows
  • feat(stdlib): bounded search (find_in/rfind_in/count_in) + memchr find core
  • feat(codegen): bounded search C twins (find_in/rfind_in/count_in)
  • test+docs: bounded search fixture coverage
  • style: clippy needless_as_bytes + cargo fmt
  • fix(stdlib): rfind_in empty returns clamped end (matches wrapper semantics)
  • feat(stdlib): str.classify native classifier (Rust + registry)
  • feat(stdlib+codegen): str.classify native classifier with 6-arg AOT support
  • fix(tests): terminate unterminated import-body prints before batch markers
  • Realse v0.2.0

zaidejjo and others added 30 commits October 1, 2026 02:39
* vm: fuse N-op-y slot binop only for commutative ops

The SlotBinaryIntImm fusion always evaluates `slot op imm`, so
fusing `0 - x` silently computed `x - 0`. Restrict the
constant-left fusion to Add/Mul; Sub/Div/Rem/comparisons fall
back to the generic path. Constant-right fusion is unaffected
(operand order preserved).

* vm: bind tuple-destructure elements positionally via env

compile_destructure recorded one frame slot and reused it for
every element, assigning the first element to all variables.
Bind each element through the environment (DefineVar + Pop,
mirroring captured declarations) and scope block bindings so
they cannot leak into enclosing environments.

* checker: Never bottom type for diverging returns, verify fall-through

Stmt::Return with a value types as Never (bottom, distinct
from Error): it vanishes from if/match joins without
constraining sibling arms, while check_func_body always
verifies the fall-through type against the signature (the old
block_has_return bypass is gone). This rejects wrong-typed
value paths alongside divergent arms and missing-return paths
(e.g. no-else if in a non-unit function).

* checker: record top-level destructure bindings for native globals

bind_pattern now records top-level (env.len() == 1) bindings
into new_bindings, mirroring Stmt::Decl, so tp.bindings types
every module-level destructured name for native globals
collection.

* native: lexical scopes for branches; top-level destructure as globals

Lower tuple destructuring (previously skipped) via indexed
element extraction. push_scope/pop_scope at every branch site
(value blocks, while bodies, match arms, tail branches,
comprehensions) so NameCtx mirrors checker scoping — this also
fixes pre-existing stale references for shadowed Decl locals.
Top-level destructured names become module globals assigned in
the top-level lowering, making them visible to functions and
closures.

* tests: differential fuzzer tooling plus fixed-seed CI smoke set

tests/fuzz/ holds the generator (gen.py), the VM-vs-native
differential runner (run.sh), the runner docs (README.md) and
the explicit known-failures list for pre-existing native
classes (tuple-literal boxing, scalar-global double-unbox).
tests/fixtures/regression/fuzz_smoke_*.zz are 12 fixed seeds
passing strictly on both engines, wired into the existing
e2e and parity harnesses (no Python needed in CI).
* checker: emit diagnostics in deterministic order

Unused-variable warnings iterated defined_names HashMaps directly,
whose per-process random order made stderr differ between the VM and
native legs (separate zz processes) — flaking strict parity tests
with several warnings (e.g. fuzz_smoke_05). Sort both emitters
(pop_scope, emit_global_unused_warnings) by (span, name).

Same class in typo suggestions: suggest_all and suggest_dotted broke
ties by HashMap iteration order. Ties now break by name.

Adds unused_warnings_emit_in_source_order checker test.

* Realse v0.1.5
* ci: build once at release, publishers consume prebuilts

CI (dev/main pushes + PRs) is now the single place tests run. Merges
to main run CI; release triggers on CI success and builds the
x86_64+aarch64 zips exactly once, attaching them before publishing
the Release. docker-publish and aur-publish trigger off the Release
in parallel and consume the zips — docker no longer cargo-builds
(and drops slow QEMU arm64 emulation), AUR no longer compiles.

Also fixes the push-aur HOME skew (runner user with HOME=/root):
HOME is resolved from the passwd db with a writability pre-check,
and the zip URL uses github.repository for the pending owner move.

* ci: mirror toolchain image to Docker Hub

docker-publish pushes the same prebuilt image to ghcr.io and
docker.io/<DOCKERHUB_USERNAME>/zz with identical tags. The Hub login
and tags are skipped when DOCKERHUB_USERNAME is unset, so forks
still publish GHCR-only.
Resolve aur-publish.yml conflict in favor of dev: main pinned
HOME=/root, which breaks on ubuntu runners (user runner cannot
write /root — the exact CI failure). Dev resolves HOME from the
passwd db instead. Also persist the fixed HOME via GITHUB_ENV so
the later push step (fresh shell) expands the same paths.
… prebuilts (#173)

Two failure modes fixed:

1. docker/aur never fired after release #12: the Release was cut
with GITHUB_TOKEN, and GitHub does not fire workflow triggers off
token-authenticated API activity. The tag+Release are now created
with RELEASE_TOKEN (user PAT, Contents read+write); GITHUB_TOKEN
fallback keeps cutting the Release with an explicit warning that
downstream will not trigger.

2. docker build failed with GLIBC_2.39 not found: prebuilt zips link
the ubuntu-latest builders' glibc, but the image was debian:bookworm
(glibc 2.36). Base is now ubuntu:24.04 (same distro as both builders;
libssl3t64), with a comment pinning the coupling.

Also adds an optional version input to docker dispatch for backfills
(Cargo.toml may have moved past the tag needing an image rebuild).
Take dev's pipeline files wholesale: main's side is the same
redesign without the PAT/glibc/version-input fixes (squash merges
hid the ancestry, hence the textual conflicts). Carry forward
main's synced Cargo.lock (v0.1.6 lock-sync #172).
….0 LICENSE

- release.yml builds linux/macos/windows zips for install scripts
- README: centered logo, site links, Why ZZ, quick start with installer
- Add KISS CONTRIBUTING.md
- LICENSE replaced with canonical Apache-2.0 text
… yet)

Keeps public/install.ps1 on the website for future use.
…gress UI

- zz_pm paths: bin_dir() (~/.zz/bin, ZZ_HOME-aware) + test
- new zz_cli ui module: tty/NO_COLOR-aware colors, step headers,
  progress bars, human_bytes (progress on stderr, stdout stays clean)
- new zz_cli setup module: zz setup wires ~/.zz/bin into bash/zsh/fish
  with idempotent marker blocks, stages+installs bash/zsh/fish/powershell
  completions; zz completion [shell] prints scripts; auto_heal() creates a
  missing bin dir on every run and hints at setup when off PATH (tty only)
- zz install --path <dir|file.zz>: release-build from source like cargo,
  install binary into ~/.zz/bin (overwrite warns, chmod +x, PATH hint)
- install/add/build output goes through staged ui steps
- README: setup + install --path quick start
…path

feat: zz setup, shell completions, auto-heal, install --path, CLI progress UI
* feat: working shell completions, remote install --path, build spinner, once-only hint

Completions (setup.rs scripts are now live-tested under bash):
- bash: flags complete before files (zz build --<TAB> gives flags),
  --path/--embed complete dirs, --target/--cc/--template values
- zsh: flag + option-value branches added
- fish: stray -k dropped, flag completions added
- zsh fpath block is inserted before compinit so it actually loads;
  setup prints per-shell reload instructions

install --path (pm.rs):
- unknown local paths fall back to package names: local aliases,
  then registry metadata -> CAS -> source build -> ~/.zz/bin
  (verified: zz install --path zcc fetched, built, installed, runs)
- shared build_and_install_bin tail with spinner + sanitized names

Build UX (main.rs, ui.rs):
- ui::Spinner with elapsed time around clang (TTY animates, piped
  stays linear); build/install/fetch all show live progress
- auto_heal PATH hint now shows once via ~/.zz/.setup-hint-shown
  stamp (cleared by zz setup or when back on PATH)

* feat: harden shell completions, add setup --check diagnostics

- fish: custom __fish_complete_zz_files helper (no commandline
  dependency) + -f so only .zz + dirs complete; verified live:
  'zz run m' offers exactly main.zz next to main.py
- bash/zsh verified live too (subcommands, flags-before-files,
  .zz preference, option values)
- new 'zz setup --check': read-only per-shell report (rc wired,
  exact-script match so stale installs fail); exit 1 when incomplete
- setup prints per-shell reload command after wiring

* fix: hint stamp survives setup, no self-nag, check passes pending restart

- auto_heal takes a suppress flag; main skips the PATH hint when
  already running 'setup'/'completion'
- 'zz setup' only clears the hint stamp once ~/.zz/bin is live on
  PATH (it cannot fix the current session) — the nag showed again
  on the very next command before
- 'zz setup --check': rc-wired-but-off-PATH reports activation
  pending and exits 0 instead of counting a failure

* fix: zsh completions actually load (compdump purge, compinit bootstrap)

- 'zz setup' removes ~/.zcompdump* so the next shell rescans fpath
  instead of serving a cached set without _zz (the README.md-TAB cause)
- when no startup file runs compinit, setup adds a bootstrap line
  inside its own block (a second compinit is harmless)
- anchor matching ignores comment lines mentioning compinit
- 'zz setup --check' runs a real zsh+compinit load test with a
  throwaway dump and reports the result
* feat: upgrade, doctor, outdated, deps, audit, clean, profile, new templates+git

- zz upgrade [--check] [--version] [--keep-backup]: self-update from
  GitHub releases with atomic swap + rollback, platform-gated
- zz doctor [--fix]: binary/clang/shell/git/registry/cache audit
  with fix hints; --fix runs setup for integration issues
- zz outdated: locked vs wanted vs latest table per registry dep
- zz deps tree [--depth] / zz deps why <pkg>: lockfile + CAS graph
- zz audit: pin integrity (published? hash? licensed? content?)
- zz new: pkg:/git-URL/path templates with {{name}} substitution,
  --force/--no-git, always git init -b main, name validation
- zz profile <file> [-- args]: PGO end to end via new
  BuildMode::PgoUse (instrument -> train -> merge -> optimize)
- zz clean [--deps]: remove build outputs (+vendor/lock)
- completions list all new subcommands; README documents trio

* review: fix upgrade timeouts/rollback/version-check, git errors, leaks

- upgrade: 30s API / 300s download timeouts (bare ureq hangs forever)
- upgrade: failed zz-lsp swap restores zz (never mix versions)
- upgrade: exact version-tuple verify (0.1.60 no longer matches 0.1.6)
- new: git init failure reports detail instead of 'git not found';
  old-git fallback reports the right exit code
- new: fetched template tempdirs removed after overlay (was leaked)
- outdated: unsatisfiable req shows 'no match' + zz info hint
- deps: flag-first invocation defaults to tree; why without lock
  reports missing lock instead of 'nothing depends'
emit_tail_branch delegated the trailing statement to last_stmt_value,
whose __tail lookup only makes sense for callers that already emitted
the whole block (function/closure bodies). In a branch the last
statement was skipped, so the lookup read stale outer entries — and
trailing return was never handled at all. Both fell through to
return zz_unit(), silently dropping call results
(if c { return g(x) } else { return h(x) } printed empty lines).

The branch tail is now lowered directly: If tails recurse, call and
value tails emit once and return, return yields its value, trailing
decl yields the local. Adds branch_call_returns + branch_call_tails
regression fixtures (VM + native parity).

Co-authored-by: zaid <zaid@localhost>
zz_binop_cat_arena / zz_str_cast_arena bump-allocate strings (refs==0
sentinel) into the per-iteration loop arena, but retaining stores
aliased them past zz_arena_reset: loop-carried accumulators read back
NUL garbage, dict keys got refs++ adopted as heap (wild free later).
Every retaining store now heals arena strings to heap copies, mirroring
the zz_value_dup string path used at thread crossings: zz_assign,
array push/append/insert/dup, dict key+value, object fields, tuple
elements. Append fallbacks explicitly abandon (never release) arena
sources instead of relying on refs wraparound.
string_accum_loop (reported NUL shape), string_store_across_iter
(copies, array/dict/object slots, push, return), branch_early_return_calls
(sibling of branch_call_returns: early return-of-call + trailing call).
Each wired into e2e + strict VM-vs-native parity.
S1 return-of-call in if/else/early-return/match branches, S2 string
accumulators with calls/copies/retaining stores, S3 stringify-like
struct emits. Default seeds verified byte-identical (60/60); 12/12 v2
seeds pass the VM.
Unary minus around the exact boundary literal parses directly to MIN;
one-past-MIN still errors. Tests for MIN, underscored MIN, past-MIN.
Pure-int ops across the pipeline for Xoshiro/WyRand-class RNGs:

- lexer: Amp/Caret/Tilde/Shl/Shr tokens, << >> lexed before
  < >, line-continuation support; Pipe reused as BitOr in
  expression context (closures/patterns unaffected)
- parser/AST: BitAnd/BitOr/BitXor/Shl/Shr + BitNot with
  ~ > +- > << >> > & > ^ > | > comparison precedence
- checker: strict (int, int) -> int, int-only ~ with cast hints
- runtime/VM: wrapping & | ^ ~, shifts masked & 63 with
  runtime error on negative counts, differential VM test
- AOT: ZZOP_AND/OR/XOR/SHL/SHR + zz_bitnot; & | ^ lower to
  raw C int64, shifts always via masked boxed path (no UB)
- tests: parser/checker/eval/VM-diff units, syntax e2e
  fixture, float-operand + neg-shift error fixtures,
  strict dual-engine parity
- docs: bitwise section in syntax.md
- parser: split >> into two > closes for nested generic type
  args (Option<Option<int>> regressed to a parse error);
  pending_gt counter + Shr-aware embedded-field lookahead
- checker: suppress cascading bitwise errors on Error/Never
  operands (same convention as check_arith)
- vm: fuse commutative & | ^ in N-op-y SlotBinaryIntImm
  (shifts stay on the generic path)
- codegen: explicit int shift counts in ZZOP_SHL/SHR
- parser: tidy parse_bitor precedence doc
The Assign Index arm boxed any non-ident RHS via an AST-only scalar
check; string concats over string idents qualified and lowered to
zz_int(zz_binop(...)) (C type error). Gate the wrap on the value
actually being a raw scalar; already-zz_value RHS stores directly.
Regression fixture string_index_set_binop in e2e + strict parity.
Parsers without the field (<= 0.1.5) ignore unknown keys, so old
compilers cannot be protected by it; enforcement guards new ones.
run/build/test fail fast on the project's own requirement; install
errors on root + linked/path deps (tool installs included); add warns
for readable path deps; publish rejects malformed requirements.
Unit tests for parse/round-trip/check/publish plus CLI integration
tests for all five paths.
Progress chatter (including the (dev, size) marker) goes to
stderr by design (ui.rs); the test asserted on stdout, failing
since the CLI progress-UI change. Pre-existing dev breakage,
unrelated to bitwise work.
…ions (#269)

{1 + 2} prints 3 and {(a)} works in "..." strings, matching the
long-standing triple-quoted rule (same is_interp_start trigger set:
ident, digit, open-paren). {{, {} and JSON-like {"key" stay literal
in both forms, with escape precedence keeping {{7}} literal.

Documents the exact expansion rule plus the regexp-quantifier
consequence (^[0-9a-f]{{8}}$ like Python f-strings and Rust format!),
and migrates in-tree patterns (stdlib email regex, uuid fixtures).
Template-mode dead-brace warnings updated to the unified rule.

Documents integer wrap semantics for + - * (two's-complement on both
engines, never traps) with the toml guard-pattern pointer, alongside
the div-by-zero and MIN/-1 trap behavior. Closes #249, closes #250.
…239, 240, 251) (#270)

* fix(native): short-circuit && and || in AOT lowering

Hoisted operand statements (index traps, calls) ran unconditionally,
defeating C-level short-circuit: len(t)>=8 && t[2]==":" OOB-trapped
natively on short tokens, which is the real root cause behind #239
(decimal-int parse looked guilty; the guard before it was not).

Right operands now emit into a guarded else-branch so they evaluate
only when the left side does not decide. Verified: toml.parse
x = 1 returns OK len=1 natively, minimal && and || repros match VM,
chunk and bytecode VMs already agreed. Adds short_circuit fixture
with e2e and strict parity coverage.

Fixes #239

* fix(native): selective-import consts print values on AOT

Bare PI from import std.math(PI) lowered to unit (empty line) because
only qualified spellings hit the const table, and only on the Path
arm (bare names are Ident). Both Ident and Path arms now consult the
math const table, resolving selective aliases (PI as pi) through the
import map and module heads (m.TAU) through namespace aliases.

Checker side: is_math_const resolves both alias kinds so aliased and
ns-aliased consts type as Float values instead of falsely erroring as
printable functions. Extends math_consts fixture (selective, aliased,
ns-aliased) under existing e2e and strict parity.

Fixes #240

* test(parity): register new strict fixtures in batch inventory

destructuring and short_circuit join ELIGIBLE; generic_selective is
EXCLUDED (needs helpers/ support dir, batch sandbox is flat). Fixes
batch_inventory_complete.

* fix(native): traps carry operands and enclosing function

Index failures now report index and length plus the function
(zz error: index 5 out of bounds for length 2 in prog.boom),
mirroring the VM wording, instead of a bare message. Dict misses
report the key. Implemented as zz_index_trap in the C runtime with
operand temps bound once at all seven emission sites (reads,
writes, compound assigns), preserving evaluation order and
refcount behavior.

Integer division, remainder, and MIN/-1 traps in zz_binop include
both operands. Error-parity harness only needs failure plus the
word error, so richer native messages stay green.

Line numbers still need source plumbing (VM bisection remains for
exact lines); chunk backend keeps its own trap helper. Fixes #251
* fix(lsp): honor --version/--help instead of LSP parse error

Flags short-circuit before the serve loop so editors and checkhealth
probes can identify the spawned server build. Adds integration tests
asserting plain output with no JSON-RPC framing. Fixes #255

* fix(lsp): seed checker with document imports so selective names resolve

The server checked each file with raw stdlib_funcs (std.* keys only),
skipping the loader's namespace copies. Valid selective files then
reported both undefined-variable and unused-import on the same name,
contradicting zz check, and member completion found zero keys.

New import_seed module mirrors the loader's std-import seeding for the
funcs table (full namespaces, selective bare names incl. aliases,
wildcards); unknown modules are skipped for the checker's own errors.
GlobalState.checker_seed_for applies it per document, and the recheck
pipeline plus the test helper use it. Regression tests assert clean
diagnostics for const, fn, and aliased selective imports, and exactly
one warning for a genuinely-unused one. Fixes #256

* fix(lsp): completion never goes dark on healthy documents

Member completion needed a stored check result, so any query racing
the async recheck answered zero items at both bare and math. positions.
The handler now runs a synchronous single-file check (same seeded path
as diagnostics) when no result is stored yet. Position mapping clamps
past-end columns to the line end instead of spilling into later lines,
and context detection clamps out-of-range/unaligned offsets instead of
risking a panic slice.

Regression tests cover the issue repros end to end over the service:
bare position serves namespaces plus keywords with kinds, math. lists
FUNCTION members with math.name details, seeded selective PI completes,
and a pre-recheck query still serves (fails without the fallback).
Fixes #257
…n fix (#272)

* fix(checker): selective std calls count as import use

The loader rewrites bare selective calls to the canonical std-rooted
spelling (pow(2, 3) -> std.math.pow(2, 3)), but the unused-import census
only accepted the short alias form (math.pow). Every selectively
imported std call warned unused import falsely.

The census now also accepts the std-rooted qualified form. Genuinely
unused selective imports still warn. Regression tests at the loader
level (used call quiet, unused call warns). The stale S1 cache can
replay the old warning after this fix: clear ~/.cache/zz/check when
bisecting import diagnostics.

* feat(lsp): harvest project dependencies and workspace modules

zz add table made runtime work while the editor reported unused import
+ undefined variable and completed nothing: the server knew only stdlib
plus open buffers.

New deps module harvests signatures loader-style (entry file plus
relative siblings, pub items under the import alias, unhealthy files
skipped, self-type qualification like the loader rewrite, per-root
cache with mtime refresh, workspace files cached per (file, ns)).
checker_seed_for_path merges dep and workspace seeds (importer-relative
resolution, alias-or-stem namespaces) before the std/import-selective
seeding; the recheck pipeline, the completion fallback and the test
helper all carry document paths through. Documents also snapshot seed
bindings, since CheckResult.bindings is own-file-only by design and
hover/completion need seeded globals.

* feat(lsp): complete everything with pub on it

Member completion (lib.) now serves functions, structs, aliases, enums
and globals from any seeded namespace. Selective lists resolve the
typed path on demand (file harvest beside the document, generics
included) so the first import already suggests; bare scope offers
selectively-imported generics whose qualified signature exists.
Import statements complete std. modules, local file paths (with
exact textEdits, subdirectory descent included) and stay scope for
unresolvable locals. Dot-access inside string literals no longer
hijacks. Full audit matrix as unit tests: scope, dot, import paths,
selective lists, workspace fixtures (main + math_utils/lib with pub
funcs/consts/structs/generics), a vendored dep, and a live-shape
service test for table. members.

* fix(lsp): workspace scan never follows symlinked dirs

initialize crawled vendor/<dep> links into whole foreign trees (CAS
checkouts, linked repos), stalling startup for tens of seconds.
read_dir file types (no follow) now gate descent: real files index as
before, symlinked directories are skipped. Deps stay fully served
through the harvest path. Regression test with a linked dir.
…#273)

* fix(lsp): semantic tokens classify call callees as functions

Call expressions recursed into the callee, so every call read as a
plain variable/namespace: pow(2, 3) and table.render(..) had no color.
The collector now takes the checker's known function names; matching
bare and qualified callees (plus selectively-imported items) tokenize
as functions, value paths keep namespace color. Handler builds the set
from the stored check result. Classification tests included.

* fix(lsp): completion items are explicitly paren-free plain text

Every item now carries insertTextFormat PlainText and a test asserts no
label, insert text, filter text or textEdit across scope, member,
import-path and selective completions contains parens. Stray () on
accept comes from client autopairs confirm hooks, not the server.
…r colors (#275)

* fix(loader): std selective/wildcard seeds reach all_funcs

Bare selective names (PI, aliased ee) were seeded per-module but never
mirrored into the all-universe, which the first-success snapshot had
already taken: later modules' selectives vanished from LoadResult.
The VM never noticed (per-module checks), but the native HIR re-check
reported undefined variable while printing correct values, breaking
VM/native stderr parity in batch runs.

Mirror inserted targets into all_funcs with the local branch's generic
guard. The batched-parity assertion now prints both stderrs on mismatch
instead of a bare differs line. Regression test with two aliased
selective modules.

* feat(lsp): struct-literal field completion with scope fallback

User{id: 12, na} now offers id/name/age as FIELD items instead of
buffer words. New StructLiteral context detects ident-glued unclosed
braces (single- and multi-line); names resolve exact-first then by
unique last segment (lib.Point), unknown or ambiguous names fall back
to scope completion so ordinary blocks never go dark, and value
positions (name: typed, digit-led) stay scope. Member kinds, details
and the paren-free guarantee match dot-access. Audit matrix: partial,
all-fields, after-comma, value-position, block fallback, unknown-name
fallback, dotted dep struct.

* fix(ci): stray blank line in loader tests
The server already implements textDocument/inlayHint,
textDocument/references, and textDocument/documentHighlight, but never
advertised inlay_hint_provider or references_provider — so editors
gated on supports_method() (inlay auto-enable, gr references) never
fired. Two capability lines + capability asserts.
…ith_at/trim_span)

Byte offsets throughout: O(1) per call, O(n) streaming total, and
backend-identical on every input (no char-counting divergence).
Matches Rust str::find/rfind byte semantics; len()/slicing stay
char-oriented by design (documented in stdlib.md).

Layers: Rust natives + checker sigs (std + short spellings),
C runtime twins + headers, AOT lowering map, e2e fixture +
dual-engine parity entry, stdlib docs. find-loop line scan beats
split on the 322KB bench (13ms vs 21ms x5) with zero substrings.
feat(stdlib): positional str search (find/rfind/starts_with_at/ends_with_at/trim_span)
str.bytes(s) copies UTF-8 bytes to plain [int] (one alloc, composes
with indexing, snapshots and the bytes.* vocabulary); bytes.to_str
strict-decodes with .err on range/UTF-8 violations identically on VM
and AOT; bytes.to_ints exposes opaque buffers as plain ints.

Layers: Rust natives + checker sigs (std + short), C runtime twins +
headers, AOT lowering, e2e fixture + dual-engine parity, stdlib docs.
Parity caught and fixed two error-message/signature divergences
during development (value-carrying range message, hard-error vs
Result split on element types).
feat(stdlib): str.bytes / bytes.to_str / bytes.to_ints bridge
- Rust: str.count via memchr find_iter (no split temporaries; frozen
  edge semantics probed first: empty counts chars+1); find/rfind
  already delegate to memchr-fast std search
- Pure-ZZ split-based count removed (would conflict with native entry)
- C: zz_str_count twin + memchr-skip upgrade for contains/find/rfind
  loops (portable memchr+memcmp, same non-overlap semantics)
- Fixture: count edge cases folded into str_find_test (e2e + parity);
  stdlib.md count row added
perf(stdlib): memchr scan cores, native str.count
ws_width_back used end-N >= s guards that wrap when end-s < N,
indexing out of bounds (found via zcc offset-core parity: any
1-2 byte line ending in a high byte panicked). Rewrote guards as
end-s >= N on both backends; added nb1 regression to str_find_test.
fix(stdlib): trim_span backward scan usize underflow
feat(stdlib): bounded search (find_in/rfind_in/count_in)
fix(stdlib): rfind_in empty returns clamped end
feat(stdlib+codegen): str.classify native classifier with 6-arg AOT support
@zaidejjo
zaidejjo merged commit 02d2c88 into main Oct 7, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant