diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts index 7330b63ddce..35a8bc21ce7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts @@ -23,6 +23,7 @@ import { markInstructionIds, markPredecessors, mergeConsecutiveBlocks, + promoteTemporaryJsxTag, reversePostorderBlocks, } from '../HIR'; import { @@ -62,7 +63,12 @@ export function constantPropagation(fn: HIRFunction): void { function constantPropagationImpl(fn: HIRFunction, constants: Constants): void { while (true) { - const haveTerminalsChanged = applyConstantPropagation(fn, constants); + const jsxTagIdentifiers = collectJsxTagIdentifiers(fn); + const haveTerminalsChanged = applyConstantPropagation( + fn, + constants, + jsxTagIdentifiers, + ); if (!haveTerminalsChanged) { break; } @@ -106,6 +112,7 @@ function constantPropagationImpl(fn: HIRFunction, constants: Constants): void { function applyConstantPropagation( fn: HIRFunction, constants: Constants, + jsxTagIdentifiers: Set, ): boolean { let hasChanges = false; for (const [, block] of fn.body.blocks) { @@ -130,7 +137,7 @@ function applyConstantPropagation( continue; } const instr = block.instructions[i]!; - const value = evaluateInstruction(constants, instr); + const value = evaluateInstruction(constants, instr, jsxTagIdentifiers); if (value !== null) { constants.set(instr.lvalue.identifier.id, value); } @@ -239,6 +246,7 @@ function evaluatePhi(phi: Phi, constants: Constants): Constant | null { function evaluateInstruction( constants: Constants, instr: Instruction, + jsxTagIdentifiers: Set, ): Constant | null { const value = instr.value; switch (value.kind) { @@ -593,6 +601,14 @@ function evaluateInstruction( case 'LoadLocal': { const placeValue = read(constants, value.place); if (placeValue !== null) { + if ( + instr.lvalue != null && + jsxTagIdentifiers.has(instr.lvalue.identifier.id) && + placeValue.kind === 'LoadGlobal' && + instr.lvalue.identifier.name == null + ) { + promoteTemporaryJsxTag(instr.lvalue.identifier); + } instr.value = placeValue; } return placeValue; @@ -639,3 +655,19 @@ function read(constants: Constants, place: Place): Constant | null { type Constant = Primitive | LoadGlobal; type Constants = Map; + +function collectJsxTagIdentifiers(fn: HIRFunction): Set { + const identifiers = new Set(); + + for (const [, block] of fn.body.blocks) { + for (const instr of block.instructions) { + if ( + instr.value.kind === 'JsxExpression' && + instr.value.tag.kind === 'Identifier' + ) { + identifiers.add(instr.value.tag.identifier.id); + } + } + } + return identifiers; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md index a7d27bc3819..d6da6da2c18 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md @@ -37,7 +37,8 @@ function useFoo() { return t0; } function _temp() { - return ; + const T0 = Stringify; + return ; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-tag-evaluation-order.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-tag-evaluation-order.expect.md index b59db512231..259fdc19947 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-tag-evaluation-order.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-tag-evaluation-order.expect.md @@ -31,10 +31,12 @@ import { StaticText1, StaticText2 } from "shared-runtime"; function Component(props) { const $ = _c(3); + const T0 = StaticText1; const t0 = props.value; + const T1 = StaticText2; let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = ; + t1 = ; $[0] = t1; } else { t1 = $[0]; @@ -42,10 +44,10 @@ function Component(props) { let t2; if ($[1] !== t0) { t2 = ( - + {t0} {t1} - + ); $[1] = t0; $[2] = t2; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.expect.md new file mode 100644 index 00000000000..cc8790b2bde --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +import React from 'react'; + +const base = 'div'; + +const TestComponent: React.FC = () => { + const Comp = base; + return ; +}; + +export default function Home() { + return ; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import React from "react"; + +const base = "div"; + +const TestComponent: React.FC = () => { + const $ = _c(1); + + const T0 = base; + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +}; + +export default function Home() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.tsx new file mode 100644 index 00000000000..c4d2604293e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +const base = 'div'; + +const TestComponent: React.FC = () => { + const Comp = base; + return ; +}; + +export default function Home() { + return ; +}