Skip to content

fix: initialize sloppy block functions at block entry - #10232

Closed
proggeramlug wants to merge 2 commits into
mainfrom
codex/10079-script-block-function-hoisting
Closed

proggeramlug wants to merge 2 commits into
mainfrom
codex/10079-script-block-function-hoisting

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Sloppy-script block functions used before their declarations were still undefined. Initialize their lexical bindings at block entry, preserving the separate Annex B outer-variable copy at the textual declaration. The reported retained-callback loop now prints 2,2,2, matching Node in both script and ES module packages.

Changes

  • Share block-entry function hoisting between sloppy and strict code, and associate each declaration with its fresh block-local binding so it correctly shadows outer variables and parameters.
  • Keep Annex B copies in source order and preallocate boxes needed by hoisted closures.
  • Add HIR regressions plus a package-pinned native Bun matrix covering early reads, repeated entry, shared var, fresh lexical captures, parameter shadowing, outer-binding updates, and mutual recursion.
  • Build the Bun startup networking archive for this integration suite in CI; add a setup regression for that dependency.

Related issue

Fixes #10079.

Test plan

Validated on macOS arm64, LLVM 22, Node 26.5.1 (the repository pin), with a compiler and runtime/stdlib/net archives built together from this checkout. Local builds use the development profile, as required by the repository instructions.

  • cargo build -p perry -p perry-runtime-static -p perry-stdlib-static -p perry-ext-net -j3 — passed.
  • Both new HIR tests failed before the implementation; afterward cargo test -p perry-hir --lib passed 413 tests, with 1 ignored.
  • cargo test -p perry --test issue_10079_script_block_function_hoisting — passed all 6 native comparisons: commonjs/module × O0/Os/Oz, --platform bun --no-cache --no-auto-optimize --no-codegen. Each package context has its own successful Node oracle; the script fixture has no strict directive or ESM marker.
  • PERRY_NO_AUTO_OPTIMIZE=1 RUST_TEST_THREADS=1 cargo test -p perry --test issue_nested_block_forward_capture --test issue_5848_annexb_global_init_reflection --test issue_5869_labeled_block_capture_boxing10 passed.
  • node --test scripts/test-require-runtime.test.mjs8 passed.
  • scripts/pre-tag-check.sh --quick — all checks passed except the existing public benchmark evidence-freshness failure. The artifact, verifier, and every source/harness fingerprint input are unchanged from base 6000a00dfe.
  • actionlint .github/workflows/test.yml reports the same three ShellCheck findings as the base workflow (SC2086, SC2012, SC2129), all outside the modified setup.
  • git diff --check — passed.

Full workspace, release, and platform-cross checks were not run locally. The initial native test attempt exposed the missing Bun startup archive; the recorded passing run followed a coherent rebuild including perry-ext-net.

Checklist

  • No workspace version bump; Cargo.toml, Cargo.lock, CLAUDE.md, and CHANGELOG.md unchanged.
  • Added user-facing regression coverage and a changelog fragment.
  • Read the contribution instructions; commits use the fix: prefix.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed block-scoped function declarations in scripts so they are initialized correctly at block entry.
    • Forward references, closures, loop iterations, parameter shadowing, and mutual recursion now resolve to the correct function binding.
    • Preserved expected Annex B behavior for outer variable updates.
  • Tests

    • Added coverage across strict and sloppy scripts, modules, loop scopes, and native optimization levels.

CI follow-up

The zizmor audit fails on the existing workflow_run trigger in .github/workflows/gate-failure-watch.yml (dangerous-triggers); that workflow is unchanged by this PR. The TLS checker also fails on the existing raw thread-local in runtime/regex/perex_owner.rs. Neither failure comes from the scoped integration dependency setup. Other jobs are still running.

CI follow-up: the completed run is red. All eight gap failures also occur on the independent #10229 and #10230 branches, which do not contain this hoisting change: iterator_prototype_next_patch (Node oracle exit 1), 2899_2779_2777_static_helpers, disposablestack_2875, gc_http2_pending_event_callback_rooting (port 443 permission denial), 2159_defineproperty_class_prototype (Perry exit 13), json_lazy_defineproperty_index (descriptor-read assertion), perfhooks_3088_3008_3010_3011, and v8_2_3680plus (Perry exit 1). The HTTP/2 fixture also fails all seven GC-stress arms. This supports a shared base/environment cause; it is not an exact-main A/B rerun.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: f8f81014-cb90-46c4-a717-6f9026f42f57

📥 Commits

Reviewing files that changed from the base of the PR and between 6000a00 and 8b5593a.

📒 Files selected for processing (10)
  • .github/workflows/test.yml
  • changelog.d/10232-script-block-function-hoisting.md
  • crates/perry-hir/src/lower/context.rs
  • crates/perry-hir/src/lower/lowering_context.rs
  • crates/perry-hir/src/lower_decl/block.rs
  • crates/perry-hir/src/lower_decl/block/hoisting_tests.rs
  • crates/perry-hir/src/lower_decl/body_stmt/nested_fn_decl.rs
  • crates/perry/tests/issue_10079_script_block_function_hoisting.rs
  • scripts/test-require-runtime.test.mjs
  • test-files/test_gap_10079_block_function_hoisting.ts

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


📝 Walkthrough

Walkthrough

Block-function lowering now initializes and reuses block-local bindings in sloppy and strict code. Regression tests cover script and module contexts, loop captures, Annex B updates, parameter shadowing, and multiple optimization levels. CI builds the required runtime package for the suite.

Changes

Block Function Hoisting

Layer / File(s) Summary
Block-function binding lowering
crates/perry-hir/src/lower/...
The lowering context records block-function bindings. Block declarations use shared local IDs, shadow enclosing bindings, preserve Annex B outer-variable positions, and restore temporary state after lowering.
Hoisting behavior validation
crates/perry-hir/src/lower_decl/block/hoisting_tests.rs, test-files/test_gap_10079_block_function_hoisting.ts, crates/perry/tests/issue_10079_script_block_function_hoisting.rs, changelog.d/10232-script-block-function-hoisting.md
HIR and integration tests cover early reads, loop-local captures, mutual recursion, Annex B updates, parameter shadowing, CommonJS and module contexts, and native O0/Os/Oz output comparisons.
Scoped suite runtime setup
.github/workflows/test.yml, scripts/test-require-runtime.test.mjs
The issue 10079 suite adds perry-ext-net to its runtime build. The setup test checks the combined Cargo invocation and runtime flags.

Priority: ➖ Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant TestSuite
  participant Node
  participant Perry
  participant Executable
  TestSuite->>Node: run fixture in CommonJS and module contexts
  TestSuite->>Perry: compile at O0, Os, and Oz
  Perry->>Executable: generate executable
  TestSuite->>Executable: run compiled fixture
  TestSuite->>TestSuite: compare output with Node
Loading

Merge Risk: ⚪ Minimal · up to 8b559

The implementation and regression coverage are consistent with the intended block-function behavior and are ready to merge after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 8 files. (2 skipped: … 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 description references and fixes issue #10079. The implementation and regression tests directly address the stated issue objectives.
Out of Scope Changes check ✅ Passed The changes remain within scope. They implement the block-function fix, add related regression coverage, and update CI setup required by the integration test.
Title check ✅ Passed The title clearly and concisely describes the primary change: initializing sloppy block functions at block entry.
Description check ✅ Passed The description includes the required Summary, Changes, Related issue, Test plan, and Checklist sections. It explains the implementation, regression coverage, test results, and known CI failures. The …
Full details: Docstring Coverage

Explanation

Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 8 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/10079-script-block-function-hoisting

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 via merge train 185r (#10242) at 9fda98d on main.

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.

Sloppy script block function used before its declaration in a loop is undefined

1 participant