Quartz v5.25

Next Session — WASM Backend + Remaining Spec Failures

Baseline: b984ab71 (post impl recursion fix, trunk) Session shape: WASM backend is multi-session. Remaining spec failures are quick individual fixes. Tree state: Clean, guard stamp valid, fixpoint 1991 functions.


What this session accomplished (Apr 16, 2026)

10 commits. Major fixes:

  1. SEND-RECV-SHADOW — deleted dead extern "C" send/recv from std/ffi/socket.qz. Unblocked 6+ specs.
  2. generic_ufcs_dispatch_spec — not a compiler bug, fixed test string concat (triple-quote newline).
  3. Collection stubs — added reversed() and sorted() to prelude. 21/21 green.
  4. Async $poll extern collisioncg_extern_var_index $-strip matched __Future_*$poll → libc poll(). Fixed by skipping __-prefixed names.
  5. .size wart — typechecker auto-rewrites .size on Int-typed values to vec_size().
  6. impl Option/Result infinite recursion — method bodies delegated to same-named free functions, causing infinite recursion. Inlined the match logic.
  7. ROADMAP cleanup — 10+ stale entries verified green on retest.

Impact: ~100+ tests unblocked across result_helpers, csv, option_result, concurrency, collection stubs, pattern matching, and more.


Item 1: WASM Backend (multi-session)

Current state

  • wasm_encode.qz (14K) — LEB128, opcodes, section encoding. Works. wasm_encode_spec 15/15 green.
  • codegen_wasm.qz (82K) — Main WASM backend.
  • wasm_runtime.qz (300K) — WASM runtime synthesis.
  • Feature-gated behind @cfg(feature: "wasm"). Current compiler binary doesn’t include it.
  • Building with --feature wasm requires ~30 GB RSS and 15+ minutes.
  • wasmtime is installed at /opt/homebrew/bin/wasmtime.

To test the WASM backend

# 1. Build compiler with WASM feature (30 GB RSS, 15+ min)
./self-hosted/bin/quartz --feature wasm \
  -I self-hosted/frontend -I self-hosted/middle -I self-hosted/backend \
  -I self-hosted/shared -I std -I tools --no-cache self-hosted/quartz.qz \
  > /tmp/quartz_wasm.ll

# 2. Build the WASM-enabled binary
PATH="/opt/homebrew/opt/llvm/bin:$PATH"
llc -filetype=obj /tmp/quartz_wasm.ll -o /tmp/quartz_wasm.o
clang /tmp/quartz_wasm.o -o /tmp/quartz_wasm -lm -lpthread

# 3. Use it to compile a test to WASM
/tmp/quartz_wasm --backend wasm --no-cache test.qz -o test.wasm
wasmtime test.wasm

WASM specs

  • wasm_encode_spec — 15/15 green (encoding layer)
  • wasm_core_spec — 27 tests (exit codes, variables, functions, control flow, recursion, stdout)
  • wasm_data_spec — closures, bitwise, string/vec ops
  • wasm_extended_spec — char predicates, str_trim, str_repeat, str_cmp

All core/data/extended specs require the WASM-enabled binary.

Architecture reference

Memory file: project_wasm_direct_backend.md. 10-phase plan at .claude/plans/indexed-enchanting-dusk.md.


Item 2: Remaining Spec Failures

Known failures after this session

SpecIssuePriority
json_specSIGSEGV (exit 139) — runtime crash in JSON parserP1
semaphore_specTimeout — scheduler-based test hangs in PTYP2 (test infra)
http2_frame_specLink error — missing libraryP2
separate_compilation_spec5/6 FAIL:link — separate compilation linker gapsP2
impl_trait_specNeeds -I spec/qspec/fixturesP3 (test infra)

Specs that need QUARTZ_COMPILER / QUARTZ_FIXTURES

All subprocess-based specs require:

  • QUARTZ_COMPILER="$(pwd)/self-hosted/bin/quartz"
  • QUARTZ_FIXTURES="$(pwd)/spec/qspec/fixtures" (for specs importing fixtures)

impl recursion pattern — more instances?

The impl Option/impl Result recursion bug (method def foo() = foo(self) where foo shadows the free function) may exist in other impl blocks. Grep for return.*\(self\) inside impl blocks to audit.


Item 3: Other observations

Triple-quoted string newline behavior

Quartz triple-quoted strings strip trailing newlines. Any helper() + """...""" concatenation needs explicit "\n" between to avoid enddef-style concatenation. This bit generic_ufcs_dispatch_spec.

cg_extern_var_index $-stripping

The fallback at codegen_util.qz:426-440 strips module prefixes via last-$ lookup. Now guarded against __-prefixed names, but could still match user module names containing extern function names (e.g. my_module$send → matches libc send). A more robust fix would be to track extern names in a separate namespace.

Memory stats on stdout

The [mem] debug output goes to stderr (via eputs), not stdout. IR output is clean. When testing, use 2>/dev/null to suppress, or grep -v '^\[mem\]' if mixing stderr into stdout.