diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PromoteUsedTemporaries.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PromoteUsedTemporaries.ts index ce3ac13a9fe..02f1c1f3f6f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PromoteUsedTemporaries.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PromoteUsedTemporaries.ts @@ -31,6 +31,39 @@ import {eachInstructionValueLValue, eachPatternOperand} from '../HIR/visitors'; * Phase 2: Promote identifiers which are used in a place that requires a named variable. */ class PromoteTemporaries extends ReactiveFunctionVisitor { + /* + * Only promote LoadGlobal temps when the JSX tag identifier's name differs + * from the referenced module-local binding. This preserves direct uses like + * while still emitting stable temps for aliases such as + * const Tag = StaticText1; return ;. + */ + override visitInstruction( + instruction: ReactiveInstruction, + state: State, + ): void { + const binding = + instruction.value.kind === 'LoadGlobal' + ? instruction.value.binding + : null; + const lvalue = instruction.lvalue; + const tagName = + lvalue != null + ? (state.tagNames.get(lvalue.identifier.declarationId) ?? null) + : null; + if ( + binding != null && + binding.kind === 'ModuleLocal' && + lvalue != null && + lvalue.identifier.name == null && + tagName !== null && + tagName !== binding.name && + state.tags.has(lvalue.identifier.declarationId) + ) { + promoteIdentifier(lvalue.identifier, state); + } + this.traverseInstruction(instruction, state); + } + override visitScope(scopeBlock: ReactiveScopeBlock, state: State): void { for (const dep of scopeBlock.scope.dependencies) { const {identifier} = dep; @@ -172,6 +205,7 @@ class PromoteAllInstancedOfPromotedTemporaries extends ReactiveFunctionVisitor; type State = { tags: JsxExpressionTags; + tagNames: Map; promoted: Set; pruned: Map< DeclarationId, @@ -205,7 +239,10 @@ class CollectPromotableTemporaries extends ReactiveFunctionVisitor { ): void { this.traverseValue(id, value, state); if (value.kind === 'JsxExpression' && value.tag.kind === 'Identifier') { - state.tags.add(value.tag.identifier.declarationId); + const identifier = value.tag.identifier; + state.tags.add(identifier.declarationId); + const name = identifier.name != null ? identifier.name.value : null; + state.tagNames.set(identifier.declarationId, name); } } @@ -432,6 +469,7 @@ class PromoteInterposedTemporaries extends ReactiveFunctionVisitor { export function promoteUsedTemporaries(fn: ReactiveFunction): void { const state: State = { tags: new Set(), + tagNames: new Map(), promoted: new Set(), pruned: new Map(), }; 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..865180a9b36 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-jsx-dynamic-tag-alias.expect.md @@ -0,0 +1,55 @@ + +## 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); + 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 ; +}