Skip to content

fix: #5735 — declaration completion-value (5) + typed-array symbol-key writes#5762

Merged
proggeramlug merged 2 commits into
mainfrom
worktree-fix+5735-test262-clusters
Jun 28, 2026
Merged

fix: #5735 — declaration completion-value (5) + typed-array symbol-key writes#5762
proggeramlug merged 2 commits into
mainfrom
worktree-fix+5735-test262-clusters

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Fixes the two net-new test262 clusters from issue #5735 (2026-06-27 sweep).

Cluster 2 — declaration completion-value (5 cases ✅ all fixed)

The completion value of a statement list whose last evaluated statement is a function / async function / generator / var declaration must be empty, falling through to the prior statement:

eval body expected
function f(){} undefined
1; function f(){} 1
var x = 10; undefined
9; var x = 10; 9
function fn(){}{} undefined

In global-script mode the eval-fold hoist published these declarations to the global environment with statements the completion tracker mis-counted:

  • Top-level function is published via Object.defineProperty(globalThis, …), which returns globalThis. The tracker rewrote that into __perry_cv = Object.defineProperty(…), so a declaration-only body yielded the global object. → void-wrap the defineProperty calls (the publish block is prepended, so an empty completion suffices).
  • Top-level var x = init was rewritten in place to void (x = init) — still an expression statement, so the tracker wrote __perry_cv = undefined and clobbered a preceding value (9; var x = 10 wrongly became undefined). → emit a completion-inert lexical declaration { let __perry_eval_void = (x = init); }; a declaration's completion is empty, so the prior value shows through.

Closes test262 language/statements/{function,async-function,generators,variable}/cptn-* and language/statementList/eval-fn-block.

Cluster 1 — typed-array symbol-key writes

A Symbol write on a statically-typed multi-byte typed array (const t = new Float64Array(2); t[sym] = v) lowered through the width-tracked native store, which fptosi-coerced the NaN-boxed Symbol to index 0 and clobbered element 0 instead of storing a symbol property — corrupting the array's data and dropping the symbol.

  • Route non-numeric keys on the width-tracked store path to the symbol-aware runtime dispatcher (mirroring the symmetric IndexGet guard already in index_get.rs).
  • Make js_typed_array_index_{get,set}_dynamic triage Symbol keys into the symbol side table.

Remaining (separate follow-up): the two built-ins/TypedArrayConstructors/internals/OwnPropertyKeys/.../integer-indexes-and-string-and-symbol-keys cases still fail. They exercise a multi-byte view over a buffer from a discarded temporary (new TA(new TA([…]).buffer)), whose receiver is mis-inferred onto a typed-feedback array store path — a deeper type-inference issue, not fixable safely in this change.

Testing

  • crates/perry/tests/issue_5579_indirect_eval_global_completion.rs — extended with the declaration / var-init completion cases.
  • crates/perry/tests/issue_5735_typed_array_symbol_keys.rs — new; pins the symbol write lands in the side table, leaves element data intact, and survives reverse() / Reflect.ownKeys ordering.
  • perry-hir, perry-codegen, perry-runtime suites green; test262 subset for the affected subtrees re-measured (cluster 2 fully closed, reverse/HasProperty/not-enumerable remain 100%).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed typed array property access for non-numeric keys, including correct handling of Symbol reads/writes via the typed array’s symbol-key storage (avoids clobbering element indices).
    • Corrected eval/global hoisting completion behavior so completion is properly empty or preserved for declaration-ending eval bodies and related global binding publishes.
  • Tests
    • Added regression coverage for Symbol-keyed typed array properties.
    • Added regression coverage for indirect eval completion semantics in global scripts.

…d-array symbol-key writes

Two coherent test262 clusters from the 2026-06-27 sweep.

## Cluster 2 — declaration completion-value (5 cases, fully fixed)

The completion value of a statement list whose last evaluated statement is a
`function`/`async function`/`generator`/`var` *declaration* must be empty, so it
falls through to the prior statement (`eval("1; function f(){}")` is `1`;
`eval("function f(){}")` is `undefined`; `eval("9; var x = 10")` is `9`).

In global-script mode the eval-fold hoist published these declarations to the
global environment with statements the completion tracker mis-counted:

- A top-level `function` is published via `Object.defineProperty(globalThis, …)`,
  which *returns* `globalThis`. The tracker rewrote that to `__perry_cv = define…`,
  so a declaration-only body yielded the global object. Fix: `void`-wrap the
  defineProperty calls (the publish block is prepended, so an empty completion is
  enough).
- A top-level `var x = init` was rewritten in place to `void (x = init)`, still an
  expression statement, so the tracker wrote `__perry_cv = undefined` and
  *clobbered* a preceding value (`9; var x = 10` wrongly became `undefined`). Fix:
  emit a completion-inert lexical declaration `{ let __perry_eval_void = (x = init); }`
  — a declaration's completion is empty, so the prior value shows through.

Fixes test262 `language/statements/{function,async-function,generators,variable}/cptn-*`
and `language/statementList/eval-fn-block` (all pass).

## Cluster 1 — typed-array symbol-key writes

A Symbol write on a *statically-typed* multi-byte typed array
(`const t = new Float64Array(2); t[sym] = v`) lowered through the width-tracked
native store, which `fptosi`-coerced the NaN-boxed Symbol to index 0 and
*clobbered element 0* instead of storing a symbol property. Fix: route
non-numeric keys on the width-tracked store path to the symbol-aware runtime
dispatcher (mirroring the symmetric IndexGet guard), and make
`js_typed_array_index_{get,set}_dynamic` triage Symbol keys into the symbol side
table.

Note: the two `OwnPropertyKeys/integer-indexes-and-string-and-symbol-keys` cases
remain — they exercise a multi-byte view over a buffer from a discarded temporary
(`new TA(new TA([…]).buffer)`) whose receiver is mis-inferred onto a typed-feedback
array path; that is a separate type-inference issue tracked for follow-up.

Tests: extends issue_5579 completion test with the declaration cases; adds
issue_5735_typed_array_symbol_keys for the symbol-write fix. perry-hir,
perry-codegen, perry-runtime suites green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Typed-array Symbol-key access now uses the symbol side table in runtime get/set paths and in codegen for non-numeric IndexSet lowering. Global eval hoisting now emits completion-inert publish statements, wraps defineProperty calls in void, and adds regression coverage for declaration-ending eval bodies.

Changes

TypedArray Symbol Key Handling

Layer / File(s) Summary
Runtime Symbol-key get/set
crates/perry-runtime/src/typedarray_props.rs
typed_array_index_get_dynamic and js_typed_array_index_set_dynamic detect Symbol keys and use symbol-property storage instead of numeric coercion paths.
Codegen routes non-numeric IndexSet
crates/perry-codegen/src/expr/index_set.rs
Width-tracked typed-array IndexSet lowering adds a non-numeric branch that calls js_typed_array_index_set_dynamic.
TypedArray Symbol-key regression test
crates/perry/tests/issue_5735_typed_array_symbol_keys.rs
A new test compiles and runs an inline TypeScript program that checks Symbol writes, Reflect.ownKeys, reverse(), and Symbol reads on fresh typed arrays.

Global Eval Hoist Completion Semantics

Layer / File(s) Summary
Completion-inert publish form
crates/perry-hir/src/lower/global_eval_hoist.rs
synth_inert_assign_stmt replaces void-wrapped assignment publication, and generated global function binding emission wraps Object.defineProperty calls in void(...).
Updated hoist test helpers
crates/perry-hir/src/lower/global_eval_hoist.rs
Test helpers now scan the inert publish block form, and the defineProperty walker descends through unary void wrappers.
Eval declaration completion regression test
crates/perry/tests/issue_5579_indirect_eval_global_completion.rs
A new global-script regression payload and test assert that declaration-ending eval bodies produce empty completion values while earlier expression results still propagate.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • PerryTS/perry#5544: Touches typed-array index access and Symbol-key handling in the same runtime/codegen area as the new typed-array changes here.
  • PerryTS/perry#5636: Modifies the same global_eval_hoist machinery for hoisted publish behavior and eval-body rewriting.
  • PerryTS/perry#5725: Also changes global_eval_hoist.rs publish emission for top-level var/function completion behavior.

Poem

🐇 I hop where symbols softly glow,
And typed arrays keep their side-table flow.
Eval ends quiet, declarations serene,
With completions as empty as moonlight's sheen.
A carrot-cheer for code precise and bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific and accurately summarizes the two main fixes in the change set.
Description check ✅ Passed The description covers summary, concrete changes, related issue, and testing, even if it doesn't match the template headings exactly.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-fix+5735-test262-clusters

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry-hir/src/lower/global_eval_hoist.rs`:
- Around line 95-109: The synthetic inert binding in synth_inert_assign_stmt
currently uses a fixed name that user code can reference, causing name
collisions and possible TDZ issues during initializer resolution. Update this
helper to generate a collision-free hidden identifier, preferably by reusing
fresh_hidden() or an equivalent temp-name generator, and wire that fresh name
into the parsed inert block before attaching the initializer from
synth_assign_stmt.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 65ad7b41-38df-4d7b-ae73-c85e69a5503f

📥 Commits

Reviewing files that changed from the base of the PR and between 771735f and 97ad1ea.

📒 Files selected for processing (5)
  • crates/perry-codegen/src/expr/index_set.rs
  • crates/perry-hir/src/lower/global_eval_hoist.rs
  • crates/perry-runtime/src/typedarray_props.rs
  • crates/perry/tests/issue_5579_indirect_eval_global_completion.rs
  • crates/perry/tests/issue_5735_typed_array_symbol_keys.rs

Comment thread crates/perry-hir/src/lower/global_eval_hoist.rs Outdated
Addresses CodeRabbit review: the completion-inert `var x = init` publish wrapped
the assignment in `{ let __perry_eval_void = (x = init); }` with a *fixed* sink
name. The lexical sink is in TDZ while its own initializer (`x = init`)
evaluates, so eval source whose `init` referenced that exact name
(`eval("var x = __perry_eval_void")`) would throw a spurious ReferenceError and
diverge from the pre-rewrite resolution. Generate a fresh per-publish hidden name
via `fresh_hidden()` instead, matching the existing renamed-binding convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry-hir/src/lower/global_eval_hoist.rs`:
- Around line 760-764: The inert sink in `global_eval_hoist.rs` is only made
fresh with `fresh_hidden()`, but it can still collide with identifiers from the
eval source and shadow them during initialization. Update the sink selection
used before `synth_inert_assign_stmt` so it chooses a name that is
collision-free against all source identifiers, not just previously generated
publishes, and keep the hidden sink logic within the publish path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e2bb6ccf-bf4b-4c58-a4e2-fe5054880495

📥 Commits

Reviewing files that changed from the base of the PR and between 97ad1ea and 773052d.

📒 Files selected for processing (1)
  • crates/perry-hir/src/lower/global_eval_hoist.rs

Comment thread crates/perry-hir/src/lower/global_eval_hoist.rs
@proggeramlug proggeramlug merged commit 69eaa23 into main Jun 28, 2026
15 checks passed
@proggeramlug proggeramlug deleted the worktree-fix+5735-test262-clusters branch June 28, 2026 15:50
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