Skip to content

Comments

fix(mangler): use retain instead of truncate to remove empty frequency slots#18225

Merged
graphite-app[bot] merged 1 commit intomainfrom
01-19-fix_mangler_use_retain_instead_of_truncate_to_remove_empty_frequency_slots
Jan 19, 2026
Merged

fix(mangler): use retain instead of truncate to remove empty frequency slots#18225
graphite-app[bot] merged 1 commit intomainfrom
01-19-fix_mangler_use_retain_instead_of_truncate_to_remove_empty_frequency_slots

Conversation

@Dunqing
Copy link
Member

@Dunqing Dunqing commented Jan 19, 2026

#18183 introduced a bug when removing empty frequency slots. The code used truncate after sorting:

frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency));
if let Some(idx) = frequencies.iter().position(|x| x.symbol_ids.is_empty()) {
    frequencies.truncate(idx);
}

The problem is that sort_unstable_by_key doesn't guarantee order for items with equal keys. When multiple slots have frequency: 0, some have symbols (that need renaming but have no references), and some are empty. Since the sort order is unstable, an empty slot could appear before a non-empty slot, and truncate would incorrectly remove valid slots.

This fix uses retain instead, and moves it before the sort for better efficiency (sorting fewer elements):

frequencies.retain(|x| !x.symbol_ids.is_empty());
frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency));

Note for reviewer: I've locally tested in the monitor-oxc; there is no naming collision after this.

@github-actions github-actions bot added A-minifier Area - Minifier C-bug Category - Bug labels Jan 19, 2026
Copy link
Member Author

Dunqing commented Jan 19, 2026


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@Dunqing Dunqing requested review from Boshen and sapphi-red January 19, 2026 11:06
@Dunqing Dunqing marked this pull request as ready for review January 19, 2026 11:06
Copilot AI review requested due to automatic review settings January 19, 2026 11:06
@codspeed-hq
Copy link

codspeed-hq bot commented Jan 19, 2026

Merging this PR will not alter performance

✅ 38 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing 01-19-fix_mangler_use_retain_instead_of_truncate_to_remove_empty_frequency_slots (7d9fc4f) with main (8fe1e8e)2

Open in CodSpeed

Footnotes

  1. 7 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.

  2. No successful run was found on main (9a5e3e2) during the generation of this report, so 8fe1e8e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Dunqing Dunqing force-pushed the 01-19-fix_mangler_use_retain_instead_of_truncate_to_remove_empty_frequency_slots branch from 35c4846 to 7d9fc4f Compare January 19, 2026 11:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug in the mangler where empty frequency slots were incorrectly removed using truncate after an unstable sort. The issue was that sort_unstable_by_key doesn't guarantee order for items with equal keys (like frequency=0), so empty slots could appear before non-empty slots containing symbols that need renaming, causing those valid slots to be incorrectly truncated.

Changes:

  • Replaced truncate logic with retain to filter out empty slots
  • Moved the filtering before sorting for better efficiency
  • Updated snapshot test to reflect correct parameter renaming behavior

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
crates/oxc_mangler/src/lib.rs Replaced truncate with retain and moved filtering before sort to fix bug and improve performance
crates/oxc_minifier/tests/mangler/snapshots/mangler.snap Updated snapshot showing parameter a now correctly renamed to e

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Dunqing
Copy link
Member Author

Dunqing commented Jan 19, 2026

I thought frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency)); would place all the empty slots at the end, but that is incorrect. x.frequency is 0, which means no references, but it doesn't mean it doesn't have symbols (symbols can be defined without use).

// Initial:
[{ slot: 0, freq: 0, [] }, { slot: 1, freq: 0, [a] }]

// After sort_unstable_by_key - could be EITHER:
[{ slot: 0, freq: 0, [] }, { slot: 1, freq: 0, [a] }]  // same order
// OR
[{ slot: 1, freq: 0, [a] }, { slot: 0, freq: 0, [] }]  // swapped

@Dunqing
Copy link
Member Author

Dunqing commented Jan 19, 2026

And the performance is actually better than truncate.

@sapphi-red
Copy link
Member

Ah, my bad, I was thinking of frequencies.sort_unstable_by_key(|x| (std::cmp::Reverse(x.frequency), x.symbol_ids.len()));. Did you try this one?

Copy link
Member

@sapphi-red sapphi-red left a comment

Choose a reason for hiding this comment

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

(the comment above is non-blocking)

@overlookmotel
Copy link
Member

I'm going to merge this as is because it's a blocker to doing a release. @Dunqing You may want to look into Sapphi's suggestion later.

@overlookmotel overlookmotel added the 0-merge Merge with Graphite Merge Queue label Jan 19, 2026
Copy link
Member

overlookmotel commented Jan 19, 2026

Merge activity

…uency slots (#18225)

#18183 introduced a bug when removing empty frequency slots. The code used `truncate` after sorting:

```rust
frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency));
if let Some(idx) = frequencies.iter().position(|x| x.symbol_ids.is_empty()) {
    frequencies.truncate(idx);
}
```

The problem is that `sort_unstable_by_key` doesn't guarantee order for items with equal keys. When multiple slots have `frequency: 0`, some have symbols (that need renaming but have no references), and some are empty. Since the sort order is unstable, an empty slot could appear **before** a non-empty slot, and `truncate` would incorrectly remove valid slots.

This fix uses `retain` instead, and moves it before the sort for better efficiency (sorting fewer elements):

```rust
frequencies.retain(|x| !x.symbol_ids.is_empty());
frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency));
```

Note for reviewer: I've locally tested in the `monitor-oxc`; there is no naming collision after this.
@graphite-app graphite-app bot force-pushed the 01-19-fix_mangler_use_retain_instead_of_truncate_to_remove_empty_frequency_slots branch from 7d9fc4f to ee9f6a4 Compare January 19, 2026 11:44
@graphite-app graphite-app bot merged commit ee9f6a4 into main Jan 19, 2026
21 checks passed
@graphite-app graphite-app bot deleted the 01-19-fix_mangler_use_retain_instead_of_truncate_to_remove_empty_frequency_slots branch January 19, 2026 11:51
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Jan 19, 2026
@Dunqing
Copy link
Member Author

Dunqing commented Jan 19, 2026

Ah, my bad, I was thinking of frequencies.sort_unstable_by_key(|x| (std::cmp::Reverse(x.frequency), x.symbol_ids.len()));. Did you try this one?

I tried it locally, and it fixed the issue as well. Considering that retain makes Mangler 1% faster than truncate, I won't make a change for this.

image

overlookmotel added a commit that referenced this pull request Jan 19, 2026
### 🐛 Bug Fixes

- f1e2dc0 semantic: No error in `check_function_redeclaration` for
CommonJS files (#18231) (overlookmotel)
- 645c3f0 transformer: Use `require` not `import` in CommonJS files
(#18226) (overlookmotel)
- ee9f6a4 mangler: Use `retain` instead of `truncate` to remove empty
frequency slots (#18225) (Dunqing)

### ⚡ Performance

- 52073d9 semantic: Use cheaper test for source type (#18235)
(overlookmotel)

Co-authored-by: overlookmotel <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-minifier Area - Minifier C-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants