Skip to content

fix(codegen): own method beats a Date/Array builtin on unproven receivers (#11493) - #11529

Merged
proggeramlug merged 2 commits into
mainfrom
claude/brave-lovelace-kizfge
Sep 27, 2026
Merged

proggeramlug merged 2 commits into
mainfrom
claude/brave-lovelace-kizfge

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

const d: any = new Date(0); d.getTime = () => 42; d.getTime() printed 0; Node prints 42. #10943 added the own-override test for receivers whose kind the compiler proves. It did not cover the #10476 receiver-kind guard, which handles unproven receivers. That guard checks at runtime that the value is a Date (or a plain array) and then called the builtin directly, but a matching kind says nothing about own properties. This PR adds the #10943 test to that path. It also closes the gaps I found next to it on the proven path.

Changes

  • lower_call/property_get/builtin_kind_guard.rs (the bug in the issue)
    • A heap receiver that passes the Date or plain-array check now goes through emit_own_override_branch before the builtin. So does the Date arm of the toLocaleString() guard. A receiver that may own the name takes the existing universal-dispatch arm.
    • Numbers and Symbols are primitives, so they skip the test and their IR is unchanged.
    • The predicate may allocate. When the test is emitted, the receiver and every argument are therefore rooted across it, and each arm re-reads them from their roots. time (the Date's time value) is a Number, so it is reused.
  • lower_call/property_get/own_override_guard.rs
    • The Date half of shadowable_builtin_name now comes from the kind guard's own date_builtin table (is_direct_date_builtin_name) instead of a hand-kept list. The old list stopped short, so a proven Date's getUTCHours, setTime, toUTCString, … reached the direct builtin with no diamond.
    • It also adds the array names the kind guard lowers directly (toReversed, toSorted, toSpliced, reduceRight, copyWithin). Without them, the same call answered differently depending on what the compiler proved.
  • expr/folded_builtin_override.rs
  • test-files/test_gap_11493_own_method_beats_date_builtin.ts (89 rows) covers:
    • the issue's shape, plus parameter, property-read, array-element and call-result receivers (the call result is evaluated once);
    • every Date method the kind guard lowers;
    • this/arguments, an own accessor (run once), a non-callable own value (TypeError), delete-restores-builtin, a borrowed builtin, and an own method installed mid-loop;
    • the new proven folds, class-field Dates, and unproven and proven arrays.
    • All native rows are TZ- and locale-independent.
  • Seven codegen IR tests in builtin_kind_guard_tests.rs and two table tests in folded_builtin_override.rs. The IR tests assert the test is present on the Date, array and locale arms and absent for Numbers, and use assert_rooted_across to check that the receiver reaches both the predicate and the dispatcher through its root.

No runtime change and no new side table. Version not bumped. The changelog fragment follows in a second commit, keyed to this PR number.

Related issue

Fixes #11493

Test plan

Local, Linux x86-64, --profile perry-dev, LLVM 22.1.8. main = 63e89977 built the same way.

  • New gap test: 71 of 89 lines differ from Node on main; 0 differ with this change, under TZ=UTC, America/New_York and Asia/Kolkata.
  • cargo test -p perry-codegen (RUST_TEST_THREADS=1): all 41 test binaries pass, including 1764 lib tests and the 9 new ones.
  • A/B over 133 existing tests (every test-files/ name matching date/own/override/kind/expando/locale/toSorted/reduce/flat/dayjs/…): 120 byte-identical to Node. The other 13 behave the same on main: identical output, a missing npm package (date-fns, dayjs) or UI library at link time, Date.now() output, or a feature Node 22 lacks. 0 regressions.
  • gc_root_dominance_check.py --moving-only on the IR of the new test plus 4 Date/own-override tests: 0 violations on both builds, with root stores up from 595 to 675. --stale-registers drops from 286 to 233 on the same sources. --unrooted-allocas: 0 on both.
  • scripts/run_lint_gates.sh (SKIP_COMPILE_GATES=1): 89 of 92 pass. The 3 failures are environmental or already red on main: no cargo xwin here; the benchmark-artifact gate sees zero RSS samples in this sandbox and no Bun; public-benchmark freshness.
  • Not run locally: the full gap sweep, cargo test --workspace, the auto-optimize path. CI covers these.

Cost

Instruction counts from valgrind --tool=callgrind, two-N differential, two Date calls per iteration, unproven receiver:

workload main this PR
flag clear (nothing has ever put a named property on an exotic cell) 259 280
flag armed by a Map expando 259 1891
flag armed by a property on a function 258 1823
proven Date, flag armed (the #10943 path, unchanged) 1853 1853
  • With the flag clear, the cost is a flag load plus the rooting, about +10 per call.
  • With the flag armed, each call runs the full predicate, about +800 per call. The flag is armed in most real programs, since a property on any function arms it.
  • That is exactly what a proven Date call already pays on main, so this makes the unproven path consistent rather than adding a new cost. Most of it is the predicate's key allocation plus hasOwn, which perf(runtime): memoize %Function.prototype% per realm in the dispatcher's own-override check (#10497) #11491 is shrinking for both paths.

Not covered

  • toUTCString/toGMTString on a Date that HIR proves (the shared fold above).
  • Proven-array findLast/findLastIndex, which the kind guard never handled either.

Generated by Claude Code

…vers (#11493)

`const d: any = new Date(0); d.getTime = () => 42; d.getTime()` printed 0.
#10943 added the own-override test for receivers whose kind is proven, but
#10476's receiver-kind guard, which serves UNPROVEN receivers, still went
straight to the builtin once the runtime check said "this is a Date" (or a
plain array). A matching kind says nothing about own properties.

- builtin_kind_guard: a heap receiver that passes the Date / plain-array
  check (including the Date arm of the toLocaleString guard) now takes the
  #10943 own-override test before the builtin, and falls through to the
  universal dispatcher when it may own the name. Numbers and Symbols are
  primitives and skip it. The test may allocate, so the receiver and every
  argument are rooted across it and re-read in each arm.
- own_override_guard: the proven-receiver name list now comes from the
  kind guard's own Date table, so a proven Date's getUTCHours, setTime,
  toUTCString, ... get the diamond too, and it covers the array names the
  kind guard handles (toReversed, toSorted, toSpliced, reduceRight,
  copyWithin).
- folded_builtin_override: add the zero-argument Date folds that were missing
  from the table (getTimezoneOffset, toJSON, toDateString, toTimeString,
  toLocaleDateString, toLocaleTimeString, toLocaleString). DateToUTCString
  stays out: HIR folds toUTCString and toGMTString into it, so the node does
  not know which name to test.

test_gap_11493_own_method_beats_date_builtin.ts: 71 of 89 lines differ from
node on main, 0 with this change (TZ=UTC, America/New_York, Asia/Kolkata).
@coderabbitai

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 82412f26-528c-4f77-84d4-52ddd8a349c7

📥 Commits

Reviewing files that changed from the base of the PR and between c66f7c4 and f72ebea.

📒 Files selected for processing (6)
  • changelog.d/11529-own-method-beats-date-builtin.md
  • crates/perry-codegen/src/expr/folded_builtin_override.rs
  • crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard.rs
  • crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard_tests.rs
  • crates/perry-codegen/src/lower_call/property_get/own_override_guard.rs
  • test-files/test_gap_11493_own_method_beats_date_builtin.ts
 ______________________________________________________________
< Ad Astra Per Codicem Fixis. To the stars through code fixes. >
 --------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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 pushed a commit that referenced this pull request Sep 27, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WV7PJ81Uf8F8YQMvhKKSyf
@proggeramlug
proggeramlug force-pushed the claude/brave-lovelace-kizfge branch from fd2789e to f72ebea Compare September 27, 2026 13:08
@proggeramlug
proggeramlug merged commit f5679f4 into main Sep 27, 2026
16 of 17 checks passed
@proggeramlug
proggeramlug deleted the claude/brave-lovelace-kizfge branch September 27, 2026 13:08
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.

Own getTime on a Date instance is ignored (built-in wins over own property)

2 participants