Skip to content

Effect 4 in the popular suite, class field and optional chain fixes (v0.6.1) - #24

Merged
dnnsjsk merged 6 commits into
mainfrom
test/popular-effect-4
Oct 15, 2026
Merged

dnnsjsk merged 6 commits into
mainfrom
test/popular-effect-4

Conversation

@dnnsjsk

@dnnsjsk dnnsjsk commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Releases as v0.6.1. Adds Effect 4 to the popular-packages suite and fixes the engine bugs it exposed, plus the parenthesized optional chain gap.

Fixed

  • Class fields through implicit constructors. With class B extends A { x = 1 } and class C extends B {}, new C().x was undefined: a class with fields and an implicit constructor never initialized them when reached as a subclass's super(). Effect's Data.TaggedError lost its _tag, so catchTag never matched. Present since at least v0.4.2.
  • Optional chain results. const v = a?.b stored an internal short-circuit sentinel, so v === undefined was false in code compiled by the bytecode VM.
  • Parentheses end optional chains. (a?.b).c, (a?.b)() and delete (a?.b).c now throw like V8; (a?.b).c = 1, (a?.b).c++ and (a?.b)`t` parse; optional chains are rejected as destructuring targets.
  • EstreeSerializer emits ChainExpression exactly like @babel/parser's estree output (new parse-oracle fixture).
  • Tree-walker method calls on null/undefined use V8's "Cannot read properties of undefined (reading 'x')".

Added

  • effect 4.0.5 in tests/Popular as an esbuild IIFE bundle, byte-equal with Node (205 libraries).

Verified locally

Gate Result
./bin/verify-all all checks pass (1,593 tests, 16/16 oracle scenarios)
test262 50,506 / 50,506
./bin/wpt (with ext-brotli) 15,577 / 15,577
microbench (median of 3) 1,040.1 ms vs v0.6.0's 1,054.7 ms

New Node-oracle scenarios pin each fix; each fails on v0.6.0.

🤖 Generated with Claude Code

dnnsjsk and others added 6 commits October 15, 2026 10:24
Class fields through implicit constructors: an implicit derived
constructor is a native callable with no super() site of its own, so
`new` initialized the fields of the class it constructed and an explicit
super() those of the class whose body ran, but a class with an implicit
constructor reached as someone's super never got its fields:
`class B extends A { x = 1 }`, `class C extends B {}`, `new C().x` was
undefined. Both super paths now initialize an implicit derived super's
fields and private methods after it returns. Effect's Data.TaggedError
classes lost their _tag, so Effect.catchTag never matched.

Optional chain results: a short-circuited chain yields an internal
sentinel so the rest of the chain skips itself, and evaluate() only
turned it into undefined for non-member, non-call nodes. `const v =
a?.b` stored the sentinel, and code compiled by the VM compared it by
identity, so `v === undefined` was false (Effect's optional
AsyncResource detection then called `new undefined`). evaluate() now
always returns undefined; only the object or callee of a member or call
keeps the sentinel, through evaluateChainLink().

Both bugs predate the JSX Pack (v0.4.2 has them). New oracle scenarios
pin them against Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
effect 4.0.5 ships ES modules only, so it is vendored as an esbuild
0.28.2 IIFE bundle (--bundle --minify --format=iife --global-name=effect
--platform=neutral --target=es2022), the same treatment as zod and fuse.
The runner covers pipe, Effect.gen, typed errors and catchTag, Exit and
Cause, Option and Result, Schema decode/encode and classes, services and
layers, Ref, concurrency, Match, Data equality, HashMap, Duration, and an
async program through runPromise: sleep, fibers, Queue, Stream,
Schedule retries, race and timeouts. Output is byte-equal with the
committed Node oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full local ./bin/compat-report run after the class-field and optional-chain
fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parentheses close an optional chain, but the parser dropped them, so a
short-circuit of `a?.b` also skipped the rest of `(a?.b).c`, and
`(a?.b)`t``, `(a?.b).c = 1` and `(a?.b).c++` were rejected as syntax
errors. MemberExpression and CallExpression now record `parenthesized`,
set where the parser closes the parentheses, and:

- the interpreter stops a short-circuit at a parenthesized link, so
  `(a?.b).c`, `(a?.b).c()` and `delete (a?.b).c` throw a TypeError and
  `(a?.b)()` calls undefined after evaluating its arguments, as V8 does;
  `(o?.g)()` still calls with `this === o`;
- the parser's optional-chain checks stop at parentheses, so those forms
  parse, while `(a?.b) = 1` and `(a?.b)++` stay early errors;
- optional chains are rejected as destructuring targets, parenthesized
  or not (`[a?.b] = []` used to fail only at runtime);
- EstreeSerializer emits ChainExpression around each chain, ending at
  parentheses, matching @babel/parser's estree output (new parse-oracle
  fixture).

The tree-walker's method call on null/undefined now uses V8's "Cannot
read properties of undefined (reading 'x')", which the VM already did.
A Node-oracle scenario covers the runtime cases, assignment through a
parenthesized chain and the early errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full local ./bin/compat-report run after ending optional chains at
parentheses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dnnsjsk
dnnsjsk merged commit 62592c6 into main Oct 15, 2026
7 checks passed
@dnnsjsk
dnnsjsk deleted the test/popular-effect-4 branch October 15, 2026 12:36
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