From 1d3ecf000ac670a1015731e16f0e1e9a2e907ef1 Mon Sep 17 00:00:00 2001 From: Mike Vitousek Date: Tue, 13 Aug 2024 13:43:25 -0400 Subject: [PATCH] [compiler] Allow lets to be hoisted [ghstack-poisoned] --- .../src/HIR/BuildHIR.ts | 16 ++++- .../src/HIR/HIR.ts | 8 ++- .../src/HIR/PrintHIR.ts | 3 + .../ReactiveScopes/CodegenReactiveFunction.ts | 23 +++++++ .../ReactiveScopes/PruneHoistedContexts.ts | 25 +++++-- .../ValidateLocalsNotReassignedAfterRender.ts | 10 +++ ...rences-variable-its-assigned-to.expect.md} | 2 +- ...on-references-variable-its-assigned-to.js} | 0 ...r.mutate-captured-arg-separately.expect.md | 31 --------- .../error.mutate-captured-arg-separately.js | 10 --- ...ences-later-variable-declaration.expect.md | 2 +- ...oisting-nested-let-declaration-2.expect.md | 62 ++++++++++++++++++ .../hoisting-nested-let-declaration-2.js | 17 +++++ .../hoisting-nested-let-declaration.expect.md | 65 +++++++++++++++++++ .../hoisting-nested-let-declaration.js | 21 ++++++ .../hoisting-simple-let-declaration.expect.md | 51 +++++++++++++++ .../hoisting-simple-let-declaration.js | 14 ++++ .../mutate-captured-arg-separately.expect.md | 56 ++++++++++++++++ .../mutate-captured-arg-separately.js | 16 +++++ 19 files changed, 381 insertions(+), 51 deletions(-) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.todo-function-expression-references-variable-its-assigned-to.expect.md => error.function-expression-references-variable-its-assigned-to.expect.md} (59%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.todo-function-expression-references-variable-its-assigned-to.js => error.function-expression-references-variable-its-assigned-to.js} (100%) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index b9bddff6a58b5..d5c7d9e3f4193 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -439,8 +439,11 @@ function lowerStatement( loc: id.parentPath.node.loc ?? GeneratedSource, }); continue; - } else if (binding.kind !== 'const' && binding.kind !== 'var') { - // Avoid double errors on var declarations, which we do not plan to support anyways + } else if ( + binding.kind !== 'const' && + binding.kind !== 'var' && + binding.kind !== 'let' + ) { builder.errors.push({ severity: ErrorSeverity.Todo, reason: 'Handle non-const declarations for hoisting', @@ -463,10 +466,17 @@ function lowerStatement( reactive: false, loc: id.node.loc ?? GeneratedSource, }; + const kind = + // Avoid double errors on var declarations, which we do not plan to support anyways + binding.kind === 'const' || binding.kind === 'var' + ? InstructionKind.HoistedConst + : binding.kind === 'let' + ? InstructionKind.HoistedLet + : assertExhaustive(binding.kind, 'Unexpected binding kind'); lowerValueToTemporary(builder, { kind: 'DeclareContext', lvalue: { - kind: InstructionKind.HoistedConst, + kind, place, }, loc: id.node.loc ?? GeneratedSource, diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index f249329e0c9f9..1e807d6d2a5d0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -741,6 +741,9 @@ export enum InstructionKind { // hoisted const declarations HoistedConst = 'HoistedConst', + + // hoisted const declarations + HoistedLet = 'HoistedLet', } function _staticInvariantInstructionValueHasLocation( @@ -858,7 +861,10 @@ export type InstructionValue = | { kind: 'DeclareContext'; lvalue: { - kind: InstructionKind.Let | InstructionKind.HoistedConst; + kind: + | InstructionKind.Let + | InstructionKind.HoistedConst + | InstructionKind.HoistedLet; place: Place; }; loc: SourceLocation; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts index fd17822af0a7e..59f067787359f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts @@ -760,6 +760,9 @@ export function printLValue(lval: LValue): string { case InstructionKind.HoistedConst: { return `HoistedConst ${lvalue}$`; } + case InstructionKind.HoistedLet: { + return `HoistedLet ${lvalue}$`; + } default: { assertExhaustive(lval.kind, `Unexpected lvalue kind \`${lval.kind}\``); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index bd3e97f23aba7..624a4b604d66f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -994,6 +994,13 @@ function codegenTerminal( loc: iterableItem.loc, suggestions: null, }); + case InstructionKind.HoistedLet: + CompilerError.invariant(false, { + reason: 'Unexpected HoistedLet variable in for..in collection', + description: null, + loc: iterableItem.loc, + suggestions: null, + }); default: assertExhaustive( iterableItem.value.lvalue.kind, @@ -1089,6 +1096,13 @@ function codegenTerminal( loc: iterableItem.loc, suggestions: null, }); + case InstructionKind.HoistedLet: + CompilerError.invariant(false, { + reason: 'Unexpected HoistedLet variable in for..of collection', + description: null, + loc: iterableItem.loc, + suggestions: null, + }); default: assertExhaustive( iterableItem.value.lvalue.kind, @@ -1289,6 +1303,15 @@ function codegenInstructionNullable( case InstructionKind.Catch: { return t.emptyStatement(); } + case InstructionKind.HoistedLet: { + CompilerError.invariant(false, { + reason: + 'Expected HoistedLet to have been pruned in PruneHoistedContexts', + description: null, + loc: instr.loc, + suggestions: null, + }); + } case InstructionKind.HoistedConst: { CompilerError.invariant(false, { reason: diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts index 8608b32298503..1df211afc3ae4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts @@ -23,11 +23,11 @@ import { * original instruction kind. */ export function pruneHoistedContexts(fn: ReactiveFunction): void { - const hoistedIdentifiers: HoistedIdentifiers = new Set(); + const hoistedIdentifiers: HoistedIdentifiers = new Map(); visitReactiveFunction(fn, new Visitor(), hoistedIdentifiers); } -type HoistedIdentifiers = Set; +type HoistedIdentifiers = Map; class Visitor extends ReactiveFunctionTransform { override transformInstruction( @@ -39,7 +39,21 @@ class Visitor extends ReactiveFunctionTransform { instruction.value.kind === 'DeclareContext' && instruction.value.lvalue.kind === 'HoistedConst' ) { - state.add(instruction.value.lvalue.place.identifier.declarationId); + state.set( + instruction.value.lvalue.place.identifier.declarationId, + InstructionKind.Const, + ); + return {kind: 'remove'}; + } + + if ( + instruction.value.kind === 'DeclareContext' && + instruction.value.lvalue.kind === 'HoistedLet' + ) { + state.set( + instruction.value.lvalue.place.identifier.declarationId, + InstructionKind.Let, + ); return {kind: 'remove'}; } @@ -47,6 +61,9 @@ class Visitor extends ReactiveFunctionTransform { instruction.value.kind === 'StoreContext' && state.has(instruction.value.lvalue.place.identifier.declarationId) ) { + const kind = state.get( + instruction.value.lvalue.place.identifier.declarationId, + )!; return { kind: 'replace', value: { @@ -57,7 +74,7 @@ class Visitor extends ReactiveFunctionTransform { ...instruction.value, lvalue: { ...instruction.value.lvalue, - kind: InstructionKind.Const, + kind, }, type: null, kind: 'StoreLocal', diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts index 2dab01f86e838..0ea1814349f7f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts @@ -130,6 +130,16 @@ function getContextReassignment( */ contextVariables.add(value.lvalue.place.identifier.id); } + const reassignment = reassigningFunctions.get( + value.value.identifier.id, + ); + if (reassignment !== undefined) { + reassigningFunctions.set( + value.lvalue.place.identifier.id, + reassignment, + ); + reassigningFunctions.set(lvalue.identifier.id, reassignment); + } break; } default: { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-variable-its-assigned-to.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md similarity index 59% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-variable-its-assigned-to.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md index c06ecc085f8e7..76ac6d77a2774 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-variable-its-assigned-to.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md @@ -18,7 +18,7 @@ function Component() { 1 | function Component() { 2 | let callback = () => { > 3 | callback = null; - | ^^^^^^^^^^^^^^^ Todo: Handle non-const declarations for hoisting. variable "callback" declared with let (3:3) + | ^^^^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `callback` cannot be reassigned after render (3:3) 4 | }; 5 | return
; 6 | } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-variable-its-assigned-to.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-variable-its-assigned-to.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.expect.md deleted file mode 100644 index ecfbdf6495255..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.expect.md +++ /dev/null @@ -1,31 +0,0 @@ - -## Input - -```javascript -// Let's not support identifiers defined after use for now. -function component(a) { - let y = function () { - m(x); - }; - - let x = {a}; - m(x); - return y; -} - -``` - - -## Error - -``` - 2 | function component(a) { - 3 | let y = function () { -> 4 | m(x); - | ^^^^ Todo: Handle non-const declarations for hoisting. variable "x" declared with let (4:4) - 5 | }; - 6 | - 7 | let x = {a}; -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.js deleted file mode 100644 index 22ea3e823317d..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.js +++ /dev/null @@ -1,10 +0,0 @@ -// Let's not support identifiers defined after use for now. -function component(a) { - let y = function () { - m(x); - }; - - let x = {a}; - m(x); - return y; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md index 174139eaae9e3..d53d6b9ea4f33 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md @@ -20,7 +20,7 @@ function Component() { 1 | function Component() { 2 | let callback = () => { > 3 | onClick = () => {}; - | ^^^^^^^^^^^^^^^^^^ Todo: Handle non-const declarations for hoisting. variable "onClick" declared with let (3:3) + | ^^^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `onClick` cannot be reassigned after render (3:3) 4 | }; 5 | let onClick; 6 | diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md new file mode 100644 index 0000000000000..fe0c6618f51ad --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +function hoisting(cond) { + let items = []; + if (cond) { + let foo = () => { + items.push(bar()); + }; + let bar = () => true; + foo(); + } + return items; +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [true], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function hoisting(cond) { + const $ = _c(2); + let items; + if ($[0] !== cond) { + items = []; + if (cond) { + const foo = () => { + items.push(bar()); + }; + + let bar = _temp; + foo(); + } + $[0] = cond; + $[1] = items; + } else { + items = $[1]; + } + return items; +} +function _temp() { + return true; +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [true], + isComponent: false, +}; + +``` + +### Eval output +(kind: ok) [true] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.js new file mode 100644 index 0000000000000..27b8a8fff6010 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.js @@ -0,0 +1,17 @@ +function hoisting(cond) { + let items = []; + if (cond) { + let foo = () => { + items.push(bar()); + }; + let bar = () => true; + foo(); + } + return items; +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [true], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md new file mode 100644 index 0000000000000..faf7a064d748f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md @@ -0,0 +1,65 @@ + +## Input + +```javascript +function hoisting() { + let qux = () => { + let result; + { + result = foo(); + } + return result; + }; + let foo = () => { + return bar + baz; + }; + let bar = 3; + const baz = 2; + return qux(); // OK: called outside of TDZ +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function hoisting() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const qux = () => { + let result; + + result = foo(); + return result; + }; + + let foo = () => bar + baz; + + let bar = 3; + const baz = 2; + t0 = qux(); + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [], + isComponent: false, +}; + +``` + +### Eval output +(kind: ok) 5 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.js new file mode 100644 index 0000000000000..c4c8f2aa4c293 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.js @@ -0,0 +1,21 @@ +function hoisting() { + let qux = () => { + let result; + { + result = foo(); + } + return result; + }; + let foo = () => { + return bar + baz; + }; + let bar = 3; + const baz = 2; + return qux(); // OK: called outside of TDZ +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md new file mode 100644 index 0000000000000..8974664b0515f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md @@ -0,0 +1,51 @@ + +## Input + +```javascript +function hoisting() { + let foo = () => { + return bar + baz; + }; + let bar = 3; + let baz = 2; + return foo(); // OK: called outside of TDZ for bar/baz +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function hoisting() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const foo = () => bar + baz; + + let bar = 3; + let baz = 2; + t0 = foo(); + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [], + isComponent: false, +}; + +``` + +### Eval output +(kind: ok) 5 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.js new file mode 100644 index 0000000000000..b9a02619f5c9a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.js @@ -0,0 +1,14 @@ +function hoisting() { + let foo = () => { + return bar + baz; + }; + let bar = 3; + let baz = 2; + return foo(); // OK: called outside of TDZ for bar/baz +} + +export const FIXTURE_ENTRYPOINT = { + fn: hoisting, + params: [], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md new file mode 100644 index 0000000000000..f3f21f8f6222b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md @@ -0,0 +1,56 @@ + +## Input + +```javascript +function component(a) { + let y = function () { + m(x); + }; + + let x = {a}; + m(x); + return y; +} + +function m(x) {} + +export const FIXTURE_ENTRYPOINT = { + fn: component, + params: [{name: 'Jason'}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function component(a) { + const $ = _c(2); + let y; + if ($[0] !== a) { + y = function () { + m(x); + }; + + let x = { a }; + m(x); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + return y; +} + +function m(x) {} + +export const FIXTURE_ENTRYPOINT = { + fn: component, + params: [{ name: "Jason" }], +}; + +``` + +### Eval output +(kind: ok) "[[ function params=0 ]]" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.js new file mode 100644 index 0000000000000..9985aea7f2fc5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.js @@ -0,0 +1,16 @@ +function component(a) { + let y = function () { + m(x); + }; + + let x = {a}; + m(x); + return y; +} + +function m(x) {} + +export const FIXTURE_ENTRYPOINT = { + fn: component, + params: [{name: 'Jason'}], +};