Skip to content

fix(rolldown): keep enum declaration for optional-chain access#9229

Merged
Dunqing merged 2 commits intomainfrom
04-25-fix_rolldown_keep_enum_decl_for_optional_chain
Apr 28, 2026
Merged

fix(rolldown): keep enum declaration for optional-chain access#9229
Dunqing merged 2 commits intomainfrom
04-25-fix_rolldown_keep_enum_decl_for_optional_chain

Conversation

@Dunqing
Copy link
Copy Markdown
Contributor

@Dunqing Dunqing commented Apr 25, 2026

Summary

Fixes the bug where optional-chained accesses to a cross-module enum (E?.x, E?.['x']) referenced an undeclared variable at runtime because the enum's declaration had been tree-shaken away.

Cause

The tree-shaker in include_statement was bypassing inclusion of an enum's declaration for any single-property member access on it, assuming the finalizer would inline the access to a literal. But the finalizer's try_inline_enum_access only matches bare StaticMemberExpression / ComputedMemberExpression — not the ChainExpression produced by optional chaining. The two phases disagreed: the tree-shaker dropped the IIFE, the finalizer left the ?.x reference intact, and the output crashed with ReferenceError.

Fix

Tighten the bypass condition in crates/rolldown/src/stages/link_stage/tree_shaking/include_statements.rs so it only fires when the access is actually inlinable:

The existing esbuild conformance test ts/ts_enum_cross_module_inlining_access already covers both inlined (E.x, E['x']) and not-inlined (E?.x, E?.['x'], bare E) accesses; its snapshot is updated to reflect the corrected output and diff.md is regenerated.

Closes #9181

Test plan

  • cargo test -p rolldown --test integration ts_enum_cross_module_inlining_access
  • Regenerated scripts/.../snap-diff output; diff.md now shows only stylistic differences vs esbuild (var vs let, arrow vs function IIFE form)

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 25, 2026

Deploy Preview for rolldown-rs ready!

Name Link
🔨 Latest commit 677e6df
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/69f00fb3e751c100088f9fbb
😎 Deploy Preview https://deploy-preview-9229--rolldown-rs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 25, 2026

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing 04-25-fix_rolldown_keep_enum_decl_for_optional_chain (677e6df) with main (d295f42)

Open in CodSpeed

Footnotes

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

@Dunqing Dunqing requested review from IWANABETHATGUY and sapphi-red and removed request for IWANABETHATGUY April 26, 2026 14:45
Comment thread crates/rolldown/tests/esbuild/ts/ts_enum_cross_module_inlining_access/diff.md Outdated
@Dunqing Dunqing requested a review from sapphi-red April 27, 2026 05:51
@IWANABETHATGUY
Copy link
Copy Markdown
Member

Sorry, I did not notice that the request review has been canceled. Wait for @sapphi-red to approve the pull request.

@Dunqing
Copy link
Copy Markdown
Contributor Author

Dunqing commented Apr 27, 2026

Sorry, I did not notice that the request review has been canceled. Wait for @sapphi-red to approve the pull request.

Thank you for reviewing! I think that's an accident.

Dunqing added 2 commits April 28, 2026 09:38
The tree-shaker was bypassing inclusion of enum declarations for any
single-property member access (`E.x`, `E?.x`), assuming the finalizer
would inline it to a literal. But the finalizer's `try_inline_enum_access`
only matches `StaticMemberExpression`/`ComputedMemberExpression` — not
the `ChainExpression` produced by `?.`. Result: optional-chained references
survived in the output while their enum declarations were tree-shaken,
producing `ReferenceError` at runtime.

Tighten the bypass to only fire when the access is actually inlinable:
non-optional and not a write target.

Closes #9181
The test is no longer failing meaningfully — only stylistic differences
remain (`var` vs `let`, arrow vs `function` IIFE). Removing the entry and
re-running `just ued` deletes the stale diff.md and updates stats/summary.
@Dunqing Dunqing force-pushed the 04-25-fix_rolldown_keep_enum_decl_for_optional_chain branch from 3c7bdd2 to 677e6df Compare April 28, 2026 01:38
@Dunqing Dunqing disabled auto-merge April 28, 2026 01:39
@Dunqing Dunqing enabled auto-merge (squash) April 28, 2026 01:39
@Dunqing Dunqing merged commit 563a9d4 into main Apr 28, 2026
31 of 32 checks passed
@Dunqing Dunqing deleted the 04-25-fix_rolldown_keep_enum_decl_for_optional_chain branch April 28, 2026 01:42
This was referenced Apr 29, 2026
shulaoda added a commit that referenced this pull request Apr 29, 2026
## [1.0.0-rc.18] - 2026-04-29

### 💥 BREAKING CHANGES

- optimization: default unspecified inlineConst.mode to smart (#9248) by @IWANABETHATGUY

### 🐛 Bug Fixes

- rolldown_plugin_vite_import_glob: return error instead of panicking when virtual module uses a relative glob (#9241) by @shulaoda
- binding: treat empty inlineConst object as omitted (#9247) by @IWANABETHATGUY
- rolldown: keep enum declaration for optional-chain access (#9229) by @Dunqing
- link_stage: restore inline let-else in exports-kind filter (#9237) by @IWANABETHATGUY
- dev/lazy: avoid module reinitialization in lazy compilation patches (#9179) by @h-a-n-a
- dev: visit identifier references for runtime rewrites in HMR finalizer (#9191) by @h-a-n-a
- chunk-optimizer: pick dominator for runtime placement to avoid cycles (#9164) by @IWANABETHATGUY
- make `this.emitFile` chunk path synchronous to avoid deadlock (#9031) by @lazarv
- use sentinel id for `browser: false` ignored modules (#9192) by @shulaoda
- prevent chunk optimizer from creating import cycles (#9228) by @IWANABETHATGUY

### 🚜 Refactor

- replace tokio::sync::Mutex with std::sync::Mutex for non-IO data (#9176) by @shulaoda
- rolldown_plugin_vite_import_glob: do not rewrite import path for absolute base (#9195) by @shulaoda
- runtime_helper: wrap DependedRuntimeHelperMap in a struct (#9215) by @IWANABETHATGUY
- drop redundant clear() in determine_safely_merge_cjs_ns (#9206) by @IWANABETHATGUY
- clean up generate_lazy_export (#9208) by @IWANABETHATGUY
- bitset: return bool from set_bit to fuse guard-and-set (#9207) by @IWANABETHATGUY
- link_stage: simplify exports-kind filter and clarify safety comments (#9205) by @IWANABETHATGUY

### 📚 Documentation

- determine_module_exports_kind (#9252) by @IWANABETHATGUY
- fix dead link to esbuild ESM/CJS interop tests (#9230) by @Copilot
- remove CSS bundling references (#9234) by @shulaoda
- correct IncrementalFullBuild row in BundleMode table (#9214) by @IWANABETHATGUY
- design: add bundler data lifecycle design doc (#9212) by @hyf0
- remove minifier alpha status notices (#9202) by @sapphi-red

### ⚙️ Miscellaneous Tasks

- upgrade oxc to 0.128.0 (#9260) by @shulaoda
- deps: bump rolldown-ariadne to 0.6.0 (#9254) by @IWANABETHATGUY
- deps: update github actions (#9259) by @renovate[bot]
- deps: update github actions (#9258) by @renovate[bot]
- remove renovate overrides (#9257) by @Boshen
- use ubuntu-latest for security workflow (#9256) by @Boshen
- notify Discord around release publish (#9251) by @Boshen
- add release environment to npm publish workflow (#9250) by @Boshen
- justfile: drop the `--` separator before forwarded args in `vp run` (#9246) by @shulaoda
- deps: update test262 submodule for tests (#9243) by @sapphi-red
- add more tracing instrumentations (#9220) by @sapphi-red
- rolldown_plugin_vite_import_glob: remove outdated sourcemap doc comment (#9213) by @shulaoda
- update security workflow (#9201) by @Boshen

### ❤️ New Contributors

* @lazarv made their first contribution in [#9031](#9031)

Co-authored-by: shulaoda <165626830+shulaoda@users.noreply.github.com>
@rolldown-guard rolldown-guard Bot mentioned this pull request Apr 29, 2026
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.

[Bug]: optional chain access to an enum declared in a different file compiles wrongly

3 participants