Skip to content

[compiler] Fix incorrect closure outlining for factory-function components#36538

Closed
tejasupmanyu wants to merge 1 commit into
facebook:mainfrom
tejasupmanyu:fix/outline-functions-factory-arrow
Closed

[compiler] Fix incorrect closure outlining for factory-function components#36538
tejasupmanyu wants to merge 1 commit into
facebook:mainfrom
tejasupmanyu:fix/outline-functions-factory-arrow

Conversation

@tejasupmanyu
Copy link
Copy Markdown

@tejasupmanyu tejasupmanyu commented May 26, 2026

Summary

When the outermost function being compiled is nested inside another function (factory/HOC, infer mode), HIRBuilder resolves identifiers against parentFunction.scope.parent — the enclosing function's scope, not the program scope. The enclosing scope's locals are mis-tagged as ModuleLocal, and outlineFunctions sees context.length === 0 and hoists the inner closure to module scope, where the captured name is undefined at runtime.

This PR adds a guard in outlineFunctions: a function is not outlined if its body contains a LoadGlobal(ModuleLocal) whose name does not resolve at the program scope.

Fixes #34901

Alternative considered

A more principled fix is to correct the misclassification in HIRBuilder.resolveIdentifier / isContextIdentifier by using scope.getProgramParent() instead of scope.parent. That change alone is semantically correct but exposes a deeper structural gap: the compiler's capture machinery (gatherCapturedContext, captureScopes) only walks scopes up to parentFunction.scope, so factory-scope variables get no value-kind initialization and InferMutationAliasingEffects invariant-fails. Closing that gap requires extending capture collection up to (but excluding) the program scope and gathering captures for the outermost function as well — a substantially larger change with wide effects on how nested functions are lowered. Worth doing in a follow-up; out of scope for this fix.

Test plan

  • Added regression fixture factory-function-component-captures-outer-scope.js. Eval output renders <div>42</div>, confirming getCount stays inline.
  • Full snap suite: 1720/1720 pass.
  • yarn workspace babel-plugin-react-compiler lint clean.

…nents

When a React component is defined inside a factory function (e.g.
`makeCounter`), the compiler's `infer` compilation mode classifies
factory-scope variables (e.g. `counter`) as `ModuleLocal` globals
because `parentFunction.scope.parent` coincidentally matches the
factory function's scope rather than the true module scope.

As a result, inner callbacks that reference those variables (e.g.
`getCount = () => counter.value`) end up with an empty `context`
array. The `outlineFunctions` pass saw `context.length === 0` and
incorrectly hoisted `getCount` to a top-level `_temp` function at
module scope, where `counter` is undefined at runtime.

Fix: before outlining a FunctionExpression, check whether its body
contains a `LoadGlobal(ModuleLocal)` instruction for a name that does
not actually exist at the true program/module scope (via
`scope.getProgramParent().getBinding(name)`). If such a reference is
found the function is left inline — outlined, it would capture an
undefined variable.

Fixes facebook#34901
@meta-cla
Copy link
Copy Markdown

meta-cla Bot commented May 26, 2026

Hi @tejasupmanyu!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

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 Bug]: Incorrect closure hoisting causes runtime error

1 participant