Skip to content

fix(es/compat): Preserve array rest when lowering nested object rest - #12352

Draft
magic-akari wants to merge 1 commit into
mainfrom
fix/issue-12339
Draft

fix(es/compat): Preserve array rest when lowering nested object rest#12352
magic-akari wants to merge 1 commit into
mainfrom
fix/issue-12339

Conversation

@magic-akari

Copy link
Copy Markdown
Member

Description:

Fix a panic when lowering patterns such as const [...{ ...y }] = [1, 2] to ES5.

Preserve the array rest element when splitting the pattern, replacing only its argument with a temporary binding. This ensures remaining elements are collected before lowering the nested object rest and prevents emitting an invalid standalone rest declaration.

Add fixture and execution coverage for direct and nested patterns, assignments, function parameters, for-of bindings, and iterator consumption.

Related issue (if exists):

@changeset-bot

changeset-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9bcf515

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codspeed-hq

codspeed-hq Bot commented Sep 12, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 9.62%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 3 improved benchmarks
✅ 203 untouched benchmarks
⏩ 61 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
script[1024] 244.4 ns 215.3 ns +13.55%
script[16] 244.4 ns 215.3 ns +13.55%
es/large/minify/libraries/d3 484.5 ms 474.2 ms +2.15%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/issue-12339 (9bcf515) with main (ff6a3c3)

Open in CodSpeed

Footnotes

  1. 61 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@magic-akari
magic-akari marked this pull request as ready for review September 12, 2026 10:31
@magic-akari
magic-akari requested review from a team as code owners September 12, 2026 10:31
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-12T10:41:36.827223Z 9bcf515 Draft marked ready
🔒 Security Review Completed 2026-09-12T10:34:45.091211Z 9bcf515 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bcf51551f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment on lines +860 to +863
Some(Pat::Rest(rest)) => {
let nested = rest.arg.take();
*rest.arg = split_ref.clone().into();
*nested

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the original RHS value for assignment results

When the nested rest pattern assigns to the identifier used as the RHS, this new branch returns the overwritten binding rather than the original assignment value. For example, const original = source; const result = ([...{ 0: source, ...tail }] = source); should leave result === original, but the transform emits the equivalent of ([..._ref] = source, { 0: source } = _ref, ..., source), so the final expression evaluates to the newly assigned element. Capture the RHS in a temporary for this assignment path so the sequence returns its original value.

Useful? React with 👍 / 👎.

Comment on lines +860 to +863
Some(Pat::Rest(rest)) => {
let nested = rest.arg.take();
*rest.arg = split_ref.clone().into();
*nested

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the lexical loop-head TDZ

For a lexical loop head such as let x = [[1]]; for (let [...{ ...x }] of x) {}, the iterable expression must resolve to the new loop binding and throw due to its TDZ. This newly handled pattern is subsequently processed by transform_for_loop, which replaces the head binding with a temporary and declares x inside the body, leaving the iterable expression outside x's lexical scope; the generated loop can therefore read the outer/global x and execute instead. Keep the original head-binding environment around evaluation of the iterable expression or otherwise preserve the TDZ.

Useful? React with 👍 / 👎.

Comment on lines +860 to +863
Some(Pat::Rest(rest)) => {
let nested = rest.arg.take();
*rest.arg = split_ref.clone().into();
*nested

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep transformed function parameters non-simple

In sloppy functions that have an earlier simple parameter, lowering this newly supported pattern to a plain temporary makes the entire parameter list simple and creates a mapped arguments object. For example, function f(a, [...{ ...x }]) { a = 2; return arguments[0]; } must return the original first argument, but after ParamCollector replaces the pattern with _1, assigning to a also updates arguments[0], so the transformed function returns 2. Preserve a non-simple parameter form while extracting the nested pattern so the function's arguments remain unmapped.

Useful? React with 👍 / 👎.

@magic-akari
magic-akari marked this pull request as draft September 12, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

panic on object rest nested inside an array rest binding

1 participant