ReviewOS

zig-utils/zig-js

Compiler: lower optional chaining across bytecode tiers

#712
Closed chris opened this 9 days ago ·
9 days ago

Parents: #465, #498 Related: #491, #711

Problem

Program, generator, async, and plain-function admission still rejects the optional_chain AST wrapper and optional member/call nodes even though bytecode already provides nullish peek jumps, exact property lookup, receiver-aware calls, spread calls, and ordinary branch merging. This forces otherwise eligible code to the tree-walker and leaves a known tier-divergence surface around computed keys, argument suppression, and receiver identity.

Scope

  • Lower optional property reads and optional calls in program, plain-function, generator, and async bytecode.
  • Preserve one-shot base/key/getter/callee evaluation, receiver identity, and the rule that computed keys and arguments are skipped after a nullish short circuit.
  • Carry a chain short circuit across later non-optional member/call segments while respecting parentheses that terminate the chain.
  • Support mixed named/computed segments, direct/member calls, spread arguments, generator/async suspension, proxy/accessor effects, and nested chains.
  • Preserve optional eval?.(...) as indirect eval and keep strict tail-position behavior exact; reject only shapes lacking an existing causal tail operation.
  • Use compile-owned jump patching and verified operand cleanup over the existing VM operation family.

No-workaround rules

  • No source/AST rewriting, repeated observable evaluation, hidden bindings, global/environment scratch, heap wrapper per reference, tree-walker callback, disabled proper-tail-call behavior, or new runtime primitive.
  • No runtime stack-container, queue, job, Promise/microtask, Worker, or Map/Set iterator changes.

Acceptance

  • Program/plain/generator/async optional reads and calls compile through existing operations with exact short-circuit cleanup.
  • Witnesses cover chain continuation and parentheses, computed key/argument suppression, getter/proxy counts, receiver identity, spread, indirect eval, suspension, exceptions, and strict tail positions.
  • Forced tree-walker and required bytecode agree; unsupported barriers remain causal and audited.
  • Focused compiler/frontend, full units, applicable TSan, and exact optional-chaining Test262 before → after accounting pass with zero regressions.
  • Parent/related issues and landing commit are cross-linked with quantified evidence.
9 days ago

Landed on main in ef067c10 (feat(compiler): lower optional chains across tiers (#712)).

Evidence:

  • program/plain/generator/async admission covered; strict tail-spread remains the audited causal rejection because no tail-spread opcode exists
  • forced tree-walker vs required bytecode: 1/1
  • generator/async suspension: 1/1
  • focused compiler: 1/1; #711 spread regression: 1/1
  • frontend: 12/12
  • focused TSan: 3/3
  • full units: 1,819 passed, 1 skipped, 0 failed, 0 leaked (1,820 total; 296.1 s; 6 shards)
  • exact test/language/expressions/optional-chaining: 38/38 → 38/38, byte-identical diagnostics, 0 flips, 0 regressions

The implementation uses compile-owned nullish-exit patching with shared one-operand/two-operand cleanup blocks over existing VM operations. It also corrects the parenthesized tree-walker boundary so (null?.method)(arg()) evaluates arg() before throwing TypeError, while successful member calls retain the receiver.

9 days ago

Follow-on #713 owns optional-chain deletion, whose nullish completion is true rather than the undefined value produced by ordinary optional reads/calls.

9 days ago

Follow-up #713 is complete in 7a1932cf: optional property deletion now reuses the compiler-owned chain exits while synthesizing the spec-required true result and preserving parenthesized chain boundaries. Optional-chaining Test262 remained 38/38 before and after.

9 days ago

Follow-up #719 landed in 2388fd77: the spread and optional-reference lowering from this work now extends to proper tail calls in strict ordinary functions, including receiver-aware member/super forms. Exact-parent call/optional/super/return Test262 remained byte-identical at 240/240 with 0 regressions.

Sign in to comment on this issue.