Skip to content

fix(hir): restore function constructors after block class shadowing - #11221

Merged
proggeramlug merged 2 commits into
mainfrom
fix/11160-function-constructor-shadow
Sep 24, 2026
Merged

proggeramlug merged 2 commits into
mainfrom
fix/11160-function-constructor-shadow

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11160.

After { const C = class { k = 2 }; } shadows a module-level function C(), new C() kept constructing the expired class. Confirmed on pristine main: the original reproducer prints 2, while Node 26.5.1 prints 1. Ordinary reads and calls already resolve the outer function correctly.

Snapshot the visible function constructor before argument lowering and dispatch it before consulting the unscoped class/alias metadata. Preserve the existing precedence for nearer locals, class declarations, class self-bindings, and explicit global constructors.

Validation:

  • The new HIR regression fails before the fix; all 492 HIR unit tests pass after rebasing onto main ba9adf1 (one existing ignored test).
  • Fresh native compiler plus runtime/stdlib static wrappers rebuilt after the rebase: all seven fixtures match Node 26.5.1 byte for byte (new regression, local-over-class shadowing, function/class overrides of globals, explicit globalThis constructors, cast constructor shadowing, and function-constructor prototype inheritance). PERRY_NO_AUTO_OPTIMIZE=1 with PERRY_RUNTIME_DIR pinned to the fresh wrappers.
  • Formatting, file-size, Node-version consistency, test registration, and diff checks pass.

Checked GitHub for an existing PR and audited local worktrees/session history plus repository state and sessions on root@perrymaster.skelpo.net before starting. No competing work found. No version bump.

CI note: the public benchmark evidence freshness gate already fails on unchanged main inputs (#10030).

Summary by CodeRabbit

  • Bug Fixes
    • Fixed constructor lookup so new C() uses the visible outer function after a block-scoped class binding ends, while preserving nearer local and class self-bindings.
  • Tests
    • Added coverage for constructor resolution across nested bindings and argument expressions, including a native parity case.

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 8a629fa2-5979-4b6d-b0ed-bbab27a96556

📥 Commits

Reviewing files that changed from the base of the PR and between ba9adf1 and 899e99b.

📒 Files selected for processing (5)
  • changelog.d/11221-function-constructor-shadow.md
  • crates/perry-hir/src/lower/expr_new.rs
  • crates/perry-hir/src/lower/tests.rs
  • crates/perry-hir/src/lower/tests/function_constructor_shadow.rs
  • test-files/test_gap_11160_function_constructor_shadow.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

Constructor lowering captures an eligible function binding before lowering arguments and uses it for dynamic construction. New HIR and native regression coverage exercises block-scoped class shadows and related constructor bindings.

Changes

Function constructor shadow resolution

Layer / File(s) Summary
Capture and use the function binding
crates/perry-hir/src/lower/expr_new.rs
Constructor lowering snapshots an eligible function binding before argument lowering and uses it to create a dynamic function constructor reference. The later function-lookup reroute is removed.
Register and add shadowing regression coverage
crates/perry-hir/src/lower/tests.rs, crates/perry-hir/src/lower/tests/function_constructor_shadow.rs, test-files/test_gap_11160_function_constructor_shadow.ts, changelog.d/11221-function-constructor-shadow.md
The HIR test checks constructor resolution after a block-scoped class expression for both const and let. The native fixture logs results for nested bindings, named-class self-reference, and argument shadowing. The changelog records the fix and coverage.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 899e9

No actionable constructor-resolution issue remains; the change is mergeable after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The change addresses #11160. lower_new snapshots the visible function binding before argument lowering and emits NewDynamic with FuncRef before the unscoped class fallback. The new HIR test cove…
Out of Scope Changes check ✅ Passed The changed lowering code, HIR regression test, native parity fixture, test registration, and changelog fragment all support the #11160 constructor-resolution fix. No unrelated change is demonstrated.
Title check ✅ Passed The title clearly and concisely describes the main fix: restoring function constructor resolution after block-scoped class shadowing.
Description check ✅ Passed The description explains the bug, expected behavior, implementation approach, related issue, validation results, and CI context. It does not use all template headings or checklist items, but it provid…
Full details: Docstring Coverage

Explanation

Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 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

Copy link
Copy Markdown
Contributor Author

Merge queue: this now conflicts with main in crates/perry-hir/src/lower/tests.rs (main moved: #11188/#11190/#11206/#11219 landed class-evaluation changes nearby). Please rebase onto current main and push; CI is otherwise the gate. Owner: please reply here so the queue knows who has this.

@proggeramlug
proggeramlug force-pushed the fix/11160-function-constructor-shadow branch from 2d02128 to 899e99b Compare September 24, 2026 14:36
@proggeramlug
proggeramlug merged commit fa1fcd5 into main Sep 24, 2026
53 of 55 checks passed
@proggeramlug
proggeramlug deleted the fix/11160-function-constructor-shadow branch September 24, 2026 16:43
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.

Block-scoped const shadowing a module-level function leaks: outer new C() resolves to the block binding (unconfirmed on main)

1 participant