ReviewOS

zig-utils/zig-js

Compiler: lower member update expression references

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

Parents: #465, #498 Depends on: #706 for top-level program expressions Related: #707, #491

Problem

The bytecode compiler lowers identifier ++/-- but rejects ordinary member targets. obj.x++, --obj[key], and suspendable computed forms therefore fall back even though #707 now provides an activation-safe one-reference contract for function bytecode.

Program-mode bytecode still lacks activation-local scratch, and super writes need receiver-aware lowering. Those boundaries must remain explicit.

Scope

  • Lower ordinary named and computed member prefix/postfix increment and decrement in plain, generator, and async function activations.
  • Resolve the base/key and observable ToPropertyKey once, perform GetValue and ToNumeric once, and PutValue on the same reference.
  • Preserve Number/BigInt behavior, prefix-new versus postfix-old results, coercion/getter/setter/proxy order, strict errors, abrupt completion, recursion, and suspension while resolving computed keys.
  • Keep top-level program and super-property targets causally unsupported behind #706 and receiver-aware super writes.

No-workaround rules

  • No repeated base/key/coercion, old + 1 substitution, AST rewrite, tree-walker callback, shared/global scratch, GIL-only serialization, skipped proxies/accessors, or changed stack/runtime primitive.

Acceptance

  • Named/computed prefix/postfix updates compile across plain, generator, and async admission; program mode retains a causal #706 rejection.
  • Exact witnesses cover one-shot base/key/get/ToNumeric/set order, Number/BigInt/string coercion, Symbol keys, recursion, proxies/accessors, strict failures, and abrupt completion.
  • Generator/async suspension while resolving a computed key preserves the exact base; prefix/postfix results remain exact.
  • Forced tree-walker and required-bytecode differentials match; program and super barriers stay causal.
  • Focused compiler/frontend, full units, applicable TSan, and exact relevant Test262 before → after accounting pass with no regressions.
  • Parent/dependency issues and landing commit are cross-linked with quantified evidence.

Result

Landed in feat(compiler): lower member update expressions (#708).

  • Ordinary named/computed ++/-- now uses one activation-local Reference across plain, generator, and async bytecode, including suspension during computed-key resolution.
  • The tree-walker now captures named member bases once too, and the VM to_numeric operation returns the required Number/BigInt postfix value instead of preserving a string primitive.
  • Test262 before → after: postfix increment 38/38 → 38/38, postfix decrement 37/37 → 37/37, prefix increment 33/33 → 33/33, prefix decrement 34/34 → 34/34; total 142/142 → 142/142, flips 0, regressions 0.
  • Validation: member-update units 3/3; ToNumeric/native optimizer units 7/7; admission/barrier/inventory/async-template units 8/8; TSan member-update units 3/3; focused frontend 12/12; full units 1,808 passed, 1 skipped, 0 failed, 0 leaked.
  • Program mode remains causally rejected behind #706; super remains behind receiver-aware write lowering.
9 days ago

Scope clarification from tier review: the tree-walker captured computed update references but re-evaluated a non-computed base during write-back, so base().value++ could call base() twice and write to a different object. The candidate fixes that semantic baseline and tests one-shot named and computed bases before enabling the same contract in plain/generator/async bytecode. This is a correctness prerequisite, not a bytecode-only workaround.

9 days ago

Completed by 1f925020 with the quantified evidence recorded in the issue body.

Sign in to comment on this issue.