fix(hir): report typeof gc as "function" so the optional-gc capability guard runs - #5711
Merged
Merged
Conversation
…ity guard runs Programs gate the optional `gc()` builtin behind the idiomatic capability guard `if (typeof gc === "function") gc()` — written that way because Node only exposes `gc` under `--expose-gc` (where `typeof gc === "undefined"` otherwise) and Bun behind `--expose-gc` too. See the Node CLI docs, which show `if (globalThis.gc) globalThis.gc()`. Perry's `gc()` is ALWAYS a real, callable builtin (a call-intrinsic routed to `js_gc_collect`), yet the HIR typeof fold deliberately EXCLUDED `gc` (arm_unary.rs: "gc is excluded — it's undefined in Node without --expose-gc"), folding `typeof gc` to a non-"function" value. So the guard was always false: an allocation-heavy program that gates collection on it never collected and RSS grew unbounded (to ~physical RAM), while an identical Node run with --expose-gc stayed flat. Include `gc` in the typeof-"function" fold (matching `setTimeout`, `queueMicrotask`, etc.). `typeof gc` now reports "function", so the guard runs the collector and memory stays bounded. Direct `gc()` calls are unchanged.
8 tasks
📝 WalkthroughWalkthroughThe unary Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
An allocation-heavy program that gates collection on the idiomatic optional-
gccapability guard never collected on Perry — RSS grew unbounded (to ~physical
RAM) while an identical Node run (with
--expose-gc) stayed flat.The guard
if (typeof gc === "function") gc()is standard precisely becausegcis optional: Node only exposes it under--expose-gc(wheretypeof gcis otherwise
"undefined"), and Bun likewise behind--expose-gc. TheNode CLI docs for
--expose-gcshow the same capability guard (
if (globalThis.gc) globalThis.gc()).Perry's
gc()is always a real, callable builtin (a call-intrinsic routedto
js_gc_collect). But the HIRtypeoffold deliberately excludedgc(
arm_unary.rs: "gc is excluded — it's undefined in Node without--expose-gc"), so
typeof gcreported a non-"function"value whilegc()was callable. The guard was therefore always false, and programs that use it
to bound memory silently ran with no manual collection.
--expose-gcCLI flag docs: https://nodejs.org/api/cli.html#--expose-gc (which itself shows the capability-guard idiomif (globalThis.gc) globalThis.gc()).--expose-gcexposinggc()on the global object, and theglobal.gc?.()form).Changes
crates/perry-hir/src/lower/lower_expr/arm_unary.rs— includegcin thetypeof→"function"fold (alongsidesetTimeout,queueMicrotask,structuredClone, …). Since Perry'sgcis genuinely available,typeof gcmust be
"function"so the capability guard runs the collector. Directgc()calls are unchanged.The complementary value-form guards (
if (globalThis.gc) gc(),global.gc?.()) readgcas a property ofglobalThis; those are addressedin a follow-up that installs
gcas a real callable global value.Related issue
n/a —
gc()capability-guard compatibility (the--expose-gcfamily ofidioms).
Test plan
typeof gcnow reports"function"; the guard runsgc()each round and RSSstays bounded. No regression to the other builtins (
typeof setTimeout/parseInt/fetchremain"function"; timers still fire).cargo build --releaseclean — built-p perry; the full-workspace build needs the GTK/gdk-pixbuflibs forperry-ui-*, which CI provides.cargo test --workspace --exclude perry-ui-ios --exclude perry-ui-tvos --exclude perry-ui-watchos --exclude perry-ui-gtk4 --exclude perry-ui-android --exclude perry-ui-windowspasses — ran the affected crate (cargo test -p perry-hir typeof, 5/5) plus the end-to-end repro; full workspace deferred to CI (perry-uineedsgdk-pixbuf).test-files/or a#[test]— thetypeoffold is covered byperry-hirtypeof tests; behavior verified end to end above.docs/src/— n/a.-p perry-ui-<backend>— n/a.Screenshots / output
n/a — see the
reproRSS plateau in the test plan.Checklist
feat:/fix:/docs:/chore:prefix convention —fix(hir): …Summary by CodeRabbit
typeof gcnow returns"function"for bare identifier checks, matching expected runtime behavior.gcis treated as always available and callable.