Repository navigation
Effect 4 in the popular suite, class field and optional chain fixes (v0.6.1) - #24
Merged
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 B extends A { x = 1 }andclass C extends B {},new C().xwasundefined: a class with fields and an implicit constructor never initialized them when reached as a subclass'ssuper(). Effect'sData.TaggedErrorlost its_tag, socatchTagnever matched. Present since at least v0.4.2.const v = a?.bstored an internal short-circuit sentinel, sov === undefinedwasfalsein code compiled by the bytecode VM.(a?.b).c,(a?.b)()anddelete (a?.b).cnow throw like V8;(a?.b).c = 1,(a?.b).c++and(a?.b)`t`parse; optional chains are rejected as destructuring targets.EstreeSerializeremitsChainExpressionexactly like@babel/parser's estree output (new parse-oracle fixture).null/undefineduse V8's "Cannot read properties of undefined (reading 'x')".Added
effect4.0.5 intests/Popularas an esbuild IIFE bundle, byte-equal with Node (205 libraries).Verified locally
./bin/verify-all./bin/wpt(with ext-brotli)New Node-oracle scenarios pin each fix; each fails on v0.6.0.
🤖 Generated with Claude Code