fix(hir): suppress Annex B.3.3 legacy var across for-head/destructuring-catch lexicals (#5346)#5384
fix(hir): suppress Annex B.3.3 legacy var across for-head/destructuring-catch lexicals (#5346)#5384proggeramlug wants to merge 1 commit into
Conversation
…ng-catch lexicals (#5346) B.3.3 gives a sloppy block-nested `function f(){}` an enclosing-scope `var f` ONLY when replacing it with `var f` would not be an early error. The collector's `forbidden` set already accounted for the body's own top-level lexicals and each descended block's `let`/`const`/`class`, but missed lexical bindings introduced by intermediate scopes between the body and the nested declaration: - a `let`/`const` for-head — `for (let f; ;) { { function f(){} } }`, `for (let f in/of …) { … }` — lexically scopes the loop body, so an enclosing `var f` collides and is an early error; - a *destructuring* catch parameter — `catch ({ f }) { { function f(){} } }` — lexically binds its names (a *simple* `catch (f)` is exempt: Annex B.3.4 lets `var f` alias it, so it does not gate B.3.3). Perry created the legacy `var` regardless, so `f` was observable (and a function after the block ran) where the spec keeps it unbound — `f` must throw ReferenceError and `typeof f` stay `"undefined"`. Seed the for-head and destructuring-catch lexical names into the forbidden set as the traversal descends into each of those bodies. test262 `annexB/language` (node v26 differential, `--all-features`): all 112 `function-code`/`global-code` `*skip-early-err-{for,for-in,for-of,switch,try}` cases now pass (the 94× "An initialized binding is not created" cluster); overall annexB parity 645/970 -> 776/1026 (75.6%). The eval-code variants of the same shape still need eval enablement (separate issue). Pure addition to the B.3.3 forbidden-set computation — no other lowering path is affected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughA new internal helper ChangesAnnex B B.3.3 forbidden set refinements
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Comment |
|
Closing as a duplicate — superseded by #5356 (commit 6964193), already merged to |
Summary
Closes the dominant cluster from #5346 (follow-up to #5297/#5319): 94×
An initialized binding is not createdin test262annexB/language.These are the B.3.3 skip cases. A sloppy block-nested
function f(){}gets an enclosing-scope legacyvar fonly when replacing it withvar fwould not be an early error. #5319's collector seeded theforbiddenset with the body's own top-level lexicals and each descended block'slet/const/class, but missed lexicals introduced by intermediate scopes between the body and the nested declaration:let/constfor-head —for (let f; ;) { { function f(){} } },for (let f in/of …) { … }— lexically scopes the loop body, so an enclosingvar fcollides → early error → var suppressed;catch ({ f }) { { function f(){} } }— lexically binds its names. A simplecatch (f)is exempt: Annex B.3.4 letsvar falias it, so it does not gate B.3.3.Perry created the legacy
varregardless, sofwas observable (and a function after the block ran) where the spec keeps it unbound — readingfmust throwReferenceErrorandtypeof fstay"undefined".Fix
Seed the for-head (
let/constonly) and destructuring-catch lexical names into theforbiddenset as the B.3.3 traversal descends into each of those bodies (annexb_nested_stmtinlower_decl/block.rs). Pure addition to the forbidden-set computation — no other lowering path is touched. Theswitchskip variant already worked (case lexicals were collected).Validation
node v26exactly for the for / for-in / for-of / switch / try shapes (fthrowsReferenceError,typeof f === "undefined"before and after the block).annexB/language(--all-features, node v26 differential): all 112function-code/global-code*skip-early-err-{for,for-in,for-of,switch,try}cases now pass; overall annexB parity 645/970 → 776/1026 (75.6%).eval-codevariants of the same shape still fail — they need eval enablement (separate item in test262 annexB/language tail — 303 fails after #5319 (B.3.3 residual + missed-negatives + dynamic eval) #5346).cargo test -p perry-hirgreen.Remaining #5346 clusters (out of scope here): 72× dynamic
eval(AOT limit), 73× Annex B missed-negatives, and theexisting-fn-no-inithoisting-precedence SameValue cluster.🤖 Generated with Claude Code
Summary by CodeRabbit