Skip to content

fix(hir): recognize the Bun platform global in diagnostics - #9754

Closed
proggeramlug wants to merge 2 commits into
PerryTS:mainfrom
proggeramlug:fix/9745-bun-global-diagnostics
Closed

fix(hir): recognize the Bun platform global in diagnostics#9754
proggeramlug wants to merge 2 commits into
PerryTS:mainfrom
proggeramlug:fix/9745-bun-global-diagnostics

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

In --platform bun mode, ordinary reads of Bun emitted unknown-identifier warnings even though the compiler installs globalThis.Bun before module initialization. Pass the selected platform's supplied global names into HIR lowering and suppress that warning for an unshadowed supplied name, including the constructor-reference diagnostic.

Reads keep the existing by-name runtime lookup, so replacing globalThis.Bun remains observable. Lexical bindings still resolve first. Default-platform diagnostics and unrelated unknown names keep their existing behavior. The existing public lowering entry point retains its signature and uses an empty supplied-global set.

Validation:

  • The end-to-end regression suite failed on both Bun warning assertions before the change, while the default-platform feature-detection test passed.
  • All 3 end-to-end tests pass against a freshly built compiler and coherent runtime/stdlib/network archives, including dependency-module reads, namespace replacement, computed access, destructuring, typeof, constructor failure, and lexical shadowing in both modes.
  • HIR unit suite: 382 passed, 1 ignored. Compiler unit suite: 1,083 passed; the sole failure is the pre-existing PERRY_CONCAT_SITE_CACHE registration gap, fixed separately by fix(cache): register concat switch and explain codegen inputs #9748.
  • Formatting, file-size, test-registration and diff checks pass.

Other baseline CI failures are addressed separately: raw TLS policy by #9750, and Linux pthread declaration warnings by #9752.

Closes #9745. No version bump.

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 6 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 8 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 1e8f917c-ec9f-4735-9e7c-c92bddec428b

📥 Commits

Reviewing files that changed from the base of the PR and between 12efed1 and fc40d95.

📒 Files selected for processing (10)
  • changelog.d/9754-bun-global-diagnostics.md
  • crates/perry-hir/src/lib.rs
  • crates/perry-hir/src/lower/context.rs
  • crates/perry-hir/src/lower/expr_new.rs
  • crates/perry-hir/src/lower/lower_expr/arm_ident.rs
  • crates/perry-hir/src/lower/lower_module_fn.rs
  • crates/perry-hir/src/lower/lowering_context.rs
  • crates/perry-hir/src/lower/mod.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry/tests/issue_9599_bun_platform.rs

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.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Landed on main via merge train #9798 (rebase-merged, so your commits keep their authorship). Thanks!

proggeramlug pushed a commit to proggeramlug/perry that referenced this pull request Sep 5, 2026
… % more

The five tables PerryTS#9754 converted were valued individually with a
measurement-only `PERRY_YOUNG_LOG=0` gate on
`RuntimeRootVisitor::young_scope()` (all five scanners fall back to their full
walk together inside one binary), plus a third arm — `cc_base_new`, main
`1d63fa91f`, no logs at all. Three interleaved rounds, `stream_scale` len 3300,
identical collection schedule in every arm (minors 196/194/196, budgeted steps
60/59/59), so these are scan costs:

| scanner, ms per turn         | main            | log, full walk  | log, minor walk |
|------------------------------|-----------------|-----------------|-----------------|
| all 95 scanners              | 14761/14105/19411 | 23368/23286/26974 | 2667/2852/3674 |
| scan_shape_table_rekey_mut   | 10884/11077/14458 | 18893/18655/22125 | 1426/1567/1947 |
| scan_descriptor_roots_mut    |   1807/1192/2223  |  2257/2467/2548   |  127/136/145   |
| scan_closure_dynamic_props   |    1014/985/1347  |    890/902/959    |  224/236/209   |
| transition_cache scanner     |     121/123/159   |    254/221/246    |   85/94/145    |
| shape_cache scanner          |      89/89/113    |    159/153/167    |  121/122/151   |

The shape cache is the one table where the log loses to the walk it replaced:
+34 ms (+35 %) against main, having skipped **0.0 % of 3.85 M entry visits in
every one of 107 collections**. The cause was already documented — the
canonical keys arrays are allocated in the LONGLIVED arena, which
`addr_is_minor_relevant` must answer `true` for because a longlived parent is
not write-barriered, and a longlived object is never promoted, so no entry ever
leaves the log.

So it goes back to the plain `values_mut()` walk: the arm helper, its
production and test-seam call sites, the thread-local log, the name constant
and the `debug_assert_logged` re-derivation are all deleted. An inert log is
not free — it is a permanent arming obligation on every future writer of that
cache plus a suppression audit that has to keep proving each site — and it
should not land on the promise of a longlived remembered set that does not
exist yet. When that set exists and makes this table skip something, the log
can come back with a measurement.

The test is kept as a scanner test (a young entry reachable only through the
cache still moves and is re-keyed in both the inline slot and the overflow map)
and now asserts that NO `[gc-young-log]` row exists for the table, so re-adding
a log here without re-measuring is a red test.

Note for anyone repeating this on another table: the two-arm version of this
experiment gives the wrong answer. With the log merely disabled, the full-walk
arm still pays its upkeep — a `take_sorted()` whose sorted result is discarded
and an `addr_is_minor_relevant` probe per entry to rebuild `kept` — so every
"off" row above is worse than main, by +7.8 s on the shapes table alone. Only
the third arm says whether a log should exist at all.

Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m
proggeramlug pushed a commit to proggeramlug/perry that referenced this pull request Sep 5, 2026
… % more

The five tables PerryTS#9754 converted were valued individually with a
measurement-only `PERRY_YOUNG_LOG=0` gate on
`RuntimeRootVisitor::young_scope()` (all five scanners fall back to their full
walk together inside one binary), plus a third arm — `cc_base_new`, main
`1d63fa91f`, no logs at all. Three interleaved rounds, `stream_scale` len 3300,
identical collection schedule in every arm (minors 196/194/196, budgeted steps
60/59/59), so these are scan costs:

| scanner, ms per turn         | main            | log, full walk  | log, minor walk |
|------------------------------|-----------------|-----------------|-----------------|
| all 95 scanners              | 14761/14105/19411 | 23368/23286/26974 | 2667/2852/3674 |
| scan_shape_table_rekey_mut   | 10884/11077/14458 | 18893/18655/22125 | 1426/1567/1947 |
| scan_descriptor_roots_mut    |   1807/1192/2223  |  2257/2467/2548   |  127/136/145   |
| scan_closure_dynamic_props   |    1014/985/1347  |    890/902/959    |  224/236/209   |
| transition_cache scanner     |     121/123/159   |    254/221/246    |   85/94/145    |
| shape_cache scanner          |      89/89/113    |    159/153/167    |  121/122/151   |

The shape cache is the one table where the log loses to the walk it replaced:
+34 ms (+35 %) against main, having skipped **0.0 % of 3.85 M entry visits in
every one of 107 collections**. The cause was already documented — the
canonical keys arrays are allocated in the LONGLIVED arena, which
`addr_is_minor_relevant` must answer `true` for because a longlived parent is
not write-barriered, and a longlived object is never promoted, so no entry ever
leaves the log.

So it goes back to the plain `values_mut()` walk: the arm helper, its
production and test-seam call sites, the thread-local log, the name constant
and the `debug_assert_logged` re-derivation are all deleted. An inert log is
not free — it is a permanent arming obligation on every future writer of that
cache plus a suppression audit that has to keep proving each site — and it
should not land on the promise of a longlived remembered set that does not
exist yet. When that set exists and makes this table skip something, the log
can come back with a measurement.

The test is kept as a scanner test (a young entry reachable only through the
cache still moves and is re-keyed in both the inline slot and the overflow map)
and now asserts that NO `[gc-young-log]` row exists for the table, so re-adding
a log here without re-measuring is a red test.

Note for anyone repeating this on another table: the two-arm version of this
experiment gives the wrong answer. With the log merely disabled, the full-walk
arm still pays its upkeep — a `take_sorted()` whose sorted result is discarded
and an `addr_is_minor_relevant` probe per entry to rebuild `kept` — so every
"off" row above is worse than main, by +7.8 s on the shapes table alone. Only
the third arm says whether a log should exist at all.

Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m
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.

compiler(bun): suppress unknown-identifier warnings for the provided Bun global

1 participant