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 8b9eb80216c61..a6f6c606e118d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts @@ -844,7 +844,7 @@ export function printPlace(place: Place): string { } export function printIdentifier(id: Identifier): string { - return `${printName(id.name)}\$${id.id}#${id.declarationId}${printScope(id.scope)}`; + return `${printName(id.name)}\$${id.id}${printScope(id.scope)}`; } function printName(name: IdentifierName | null): string { diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts index bf3ba01ddecc2..80a34ea3e0593 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts @@ -5,18 +5,27 @@ * LICENSE file in the root directory of this source tree. */ +import prettyFormat from 'pretty-format'; import {CompilerError, SourceLocation} from '..'; import { BlockId, DeclarationId, + DoWhileTerminal, + ForInTerminal, + ForOfTerminal, + ForTerminal, GotoVariant, HIRFunction, Identifier, IdentifierId, + IfTerminal, Instruction, + LabelTerminal, Place, ReactiveScope, ScopeId, + SwitchTerminal, + WhileTerminal, } from '../HIR'; import {printIdentifier, printInstruction} from '../HIR/PrintHIR'; import { @@ -26,7 +35,6 @@ import { } from '../HIR/visitors'; import {assertExhaustive, getOrInsertWith} from '../Utils/utils'; import { - BranchNode, GotoNode, ControlNode, EntryNode, @@ -40,12 +48,16 @@ import { ReactiveGraph, ReactiveId, ReactiveNode, - ReturnNode, reversePostorderReactiveGraph, StoreNode, eachNodeDependency, FallthroughNode, printReactiveGraph, + IfNode, + PhiNode, + ThrowNode, + ReturnNode, + LabelNode, } from './ReactiveIR'; export function buildReactiveGraph(fn: HIRFunction): ReactiveGraph { @@ -131,6 +143,7 @@ class Builder { type Fallthrough = | {kind: 'Function'} + | {kind: 'Label'; block: BlockId; fallthrough: ReactiveId} | {kind: 'If'; block: BlockId; fallthrough: ReactiveId}; class ControlContext { @@ -143,6 +156,7 @@ class ControlContext { > = new Map(), private scopes: Map = new Map(), private nodes: Set = new Set(), + private breakMapping: Map = new Map(), private parent: ControlContext | null = null, ) {} @@ -157,11 +171,44 @@ class ControlContext { */ new Map(), new Map(), + // Track not-yet-depended on nodes in this context so the terminal node can depend on them new Set(), + this.breakMapping, this, ); } + forkValue(): ControlContext { + return new ControlContext( + this.builder, + this.fallthrough, + new Map(), + new Map(), + new Set(), + this.breakMapping, + this, + ); + } + + putBreakMapping(block: BlockId, node: ReactiveId): void { + this.breakMapping.set(block, node); + } + + getBreakMapping(block: BlockId, loc: SourceLocation): ReactiveId { + const node = this.breakMapping.get(block); + if (node == null) { + console.log( + `No break mapping for bb${block}`, + prettyFormat(this.breakMapping), + ); + } + CompilerError.invariant(node !== undefined, { + reason: `Unset break mapping for bb${block}`, + loc, + }); + return node; + } + controlNode(control: ReactiveId, loc: SourceLocation): ReactiveId { const node: ControlNode = { kind: 'Control', @@ -169,7 +216,6 @@ class ControlContext { loc, outputs: [], control, - dependencies: [], }; this.putNode(node); return node.id; @@ -204,7 +250,10 @@ class ControlContext { } loadBreakTarget(target: BlockId, loc: SourceLocation): ReactiveId { - if (this.fallthrough.kind === 'If' && this.fallthrough.block === target) { + if ( + (this.fallthrough.kind === 'If' || this.fallthrough.kind === 'Label') && + this.fallthrough.block === target + ) { return this.fallthrough.fallthrough; } if (this.parent != null) { @@ -489,169 +538,32 @@ function buildBlockScope( // handle the terminal const terminal = block.terminal; switch (terminal.kind) { - case 'if': { - const testDep = context.lookupTemporary( - terminal.test.identifier, - terminal.test.loc, - ); - const test: NodeReference = { - node: testDep, - from: {...terminal.test}, - as: {...terminal.test}, - }; - const branchNodeId = context.nextReactiveId; - const fallthroughNodeId = context.nextReactiveId; - const joinFallthrough = { - kind: 'If', - block: terminal.fallthrough, - fallthrough: fallthroughNodeId, - } as const; - const consequentContext = context.fork(joinFallthrough); - const consequentControl = consequentContext.controlNode( - branchNodeId, - terminal.loc, - ); - const consequent = buildBlockScope( - fn, - consequentContext, - terminal.consequent, - consequentControl, - ); - const alternateContext = context.fork(joinFallthrough); - const alternateControl = alternateContext.controlNode( - branchNodeId, - terminal.loc, + case 'return': { + const valueDep = context.lookupTemporary( + terminal.value.identifier, + terminal.value.loc, ); - const alternate = - terminal.alternate !== terminal.fallthrough - ? buildBlockScope( - fn, - alternateContext, - terminal.alternate, - alternateControl, - ) - : alternateControl; - - const branch: BranchNode = { - kind: 'Branch', - control, - dependencies: [], - id: branchNodeId, - loc: terminal.loc, - outputs: [], - fallthrough: fallthroughNodeId, - terminal: { - kind: 'If', - test, - consequent: { - entry: consequentControl, - exit: consequent, - }, - alternate: { - entry: alternateControl, - exit: alternate, - }, - }, + const value: NodeReference = { + node: valueDep, + from: {...terminal.value}, + as: {...terminal.value}, }; - context.putNode(branch); - const ifNode: FallthroughNode = { - kind: 'Fallthrough', - control: branch.id, - id: fallthroughNodeId, + const returnNode: ReturnNode = { + kind: 'Return', + id: context.nextReactiveId, loc: terminal.loc, outputs: [], - branches: [consequent, alternate], + value, + dependencies: context + .uncontolledNodes() + .filter(id => id !== valueDep), + control, }; - - const predecessors: Array<{ - enter: ReactiveId; - exit: ReactiveId; - context: ControlContext; - }> = [ - { - enter: consequentControl, - exit: consequent, - context: consequentContext, - }, - { - enter: alternateControl, - exit: alternate, - context: alternateContext, - }, - ]; - - const controlDependencies: Set = new Set(); - const joinedDeclarations: Map = - new Map(); - const joinedScopes: Set = new Set(); - for (const predecessorBlock of predecessors) { - /* - * track scopes that were mutated in any of the branches so that we can - * establish control depends to order the branch/join relative to previous - * subsequent mutations of those scopes - */ - for (const [scope] of predecessorBlock.context.eachScope()) { - joinedScopes.add(scope); - } - /* - * Track variables that were read/reassigned in any of the predecessors - * so that subsequent writes/reads can take the join node as a control - */ - for (const [ - declarationId, - {write, read}, - ] of predecessorBlock.context.eachDeclaration()) { - if (write) { - joinedDeclarations.set(declarationId, 'write'); - } else if (read && !joinedDeclarations.has(declarationId)) { - joinedDeclarations.set(declarationId, 'read'); - } - } - } - for (const scope of joinedScopes) { - const scopeControl = context.loadScopeControl(scope); - if (scopeControl != null) { - controlDependencies.add(scopeControl); - } - context.recordScopeMutation(scope, ifNode.id); - } - // Add control dependencies and record reads/writes accordingly. - for (const [declarationId, declType] of joinedDeclarations) { - if (declType === 'write') { - /* - * If there was a write in any of the branches, we take a write - * dependency (on the last read/write) and record the if as the - * last write - */ - const writeControl = context.loadDeclarationControlForWrite( - declarationId, - terminal.loc, - ); - if (writeControl != null) { - controlDependencies.add(writeControl); - } - context.recordDeclarationWrite(declarationId, ifNode.id); - } else { - /* - * If there were only reads in the branches, we take a read - * dependency (on the last write) and record the if as the - * last read - */ - const readControl = - context.loadDeclarationControlForRead(declarationId); - if (readControl != null) { - controlDependencies.add(readControl); - } - context.recordDeclarationRead(declarationId, ifNode.id); - } - } - - branch.dependencies = Array.from(controlDependencies); - context.putNode(ifNode); - lastNode = ifNode.id; + context.putNode(returnNode); + lastNode = returnNode.id; break; } - case 'return': { + case 'throw': { const valueDep = context.lookupTemporary( terminal.value.identifier, terminal.value.loc, @@ -661,8 +573,8 @@ function buildBlockScope( from: {...terminal.value}, as: {...terminal.value}, }; - const returnNode: ReturnNode = { - kind: 'Return', + const throwNode: ThrowNode = { + kind: 'Throw', id: context.nextReactiveId, loc: terminal.loc, outputs: [], @@ -672,8 +584,8 @@ function buildBlockScope( .filter(id => id !== valueDep), control, }; - context.putNode(returnNode); - lastNode = returnNode.id; + context.putNode(throwNode); + lastNode = throwNode.id; break; } case 'goto': { @@ -711,15 +623,50 @@ function buildBlockScope( control, }; context.putNode(node); + context.putBreakMapping(block.id, node.id); lastNode = node.id; break; } - default: { + case 'unreachable': { + CompilerError.invariant(false, { + reason: `Found unreachable code`, + loc: terminal.loc, + }); + } + case 'unsupported': { + CompilerError.invariant(false, { + reason: `Found unsupported terminal`, + loc: terminal.loc, + }); + } + case 'scope': + case 'pruned-scope': { CompilerError.throwTodo({ - reason: `Support ${terminal.kind} nodes`, + reason: `Support scopes`, loc: terminal.loc, }); } + case 'branch': + case 'logical': + case 'ternary': + case 'sequence': + case 'optional': { + CompilerError.throwTodo({ + reason: `Support value blocks`, + loc: terminal.loc, + }); + } + case 'try': + case 'maybe-throw': { + CompilerError.throwTodo({ + reason: `Support try/catch`, + loc: terminal.loc, + }); + } + default: { + lastNode = buildTerminal(fn, terminal, context, control); + break; + } } // Continue iteration in the fallthrough @@ -733,6 +680,231 @@ function buildBlockScope( return lastNode; } +function buildTerminal( + fn: HIRFunction, + terminal: + | DoWhileTerminal + | ForInTerminal + | ForOfTerminal + | ForTerminal + | IfTerminal + | LabelTerminal + | SwitchTerminal + | WhileTerminal, + context: ControlContext, + control: ReactiveId, +): ReactiveId { + let branches: Array<{ + enter: ReactiveId; + exit: ReactiveId; + context: ControlContext; + }>; + const fallthroughNodeId = context.nextReactiveId; + let terminalNode: ReactiveNode; + + switch (terminal.kind) { + case 'if': { + const testDep = context.lookupTemporary( + terminal.test.identifier, + terminal.test.loc, + ); + const test: NodeReference = { + node: testDep, + from: {...terminal.test}, + as: {...terminal.test}, + }; + const ifNodeId = context.nextReactiveId; + const joinFallthrough = { + kind: 'If', + block: terminal.fallthrough, + fallthrough: fallthroughNodeId, + } as const; + const consequentContext = context.fork(joinFallthrough); + const consequentControl = consequentContext.controlNode( + ifNodeId, + terminal.loc, + ); + const consequent = buildBlockScope( + fn, + consequentContext, + terminal.consequent, + consequentControl, + ); + const alternateContext = context.fork(joinFallthrough); + const alternateControl = alternateContext.controlNode( + ifNodeId, + terminal.loc, + ); + const alternate = + terminal.alternate !== terminal.fallthrough + ? buildBlockScope( + fn, + alternateContext, + terminal.alternate, + alternateControl, + ) + : alternateControl; + + const node: IfNode = { + kind: 'If', + control, + dependencies: [], + id: ifNodeId, + loc: terminal.loc, + outputs: [], + fallthrough: fallthroughNodeId, + test, + consequent: { + entry: consequentControl, + exit: consequent, + }, + alternate: { + entry: alternateControl, + exit: alternate, + }, + }; + context.putNode(node); + branches = [ + { + enter: consequentControl, + exit: consequent, + context: consequentContext, + }, + { + enter: alternateControl, + exit: alternate, + context: alternateContext, + }, + ]; + terminalNode = node; + break; + } + case 'label': { + const blockContext = context.fork({ + kind: 'Label', + block: terminal.fallthrough, + fallthrough: fallthroughNodeId, + }); + const labelNodeId = context.nextReactiveId; + const blockControl = blockContext.controlNode(labelNodeId, terminal.loc); + const block = buildBlockScope( + fn, + blockContext, + terminal.block, + blockControl, + ); + const node: LabelNode = { + kind: 'Label', + block: {entry: blockControl, exit: block}, + control, + id: labelNodeId, + loc: terminal.loc, + outputs: [], + dependencies: [], + }; + context.putNode(node); + branches = [{enter: blockControl, exit: block, context: blockContext}]; + terminalNode = node; + break; + } + default: { + assertExhaustive( + terminal /* TODO */ as never, + `Unexpected terminal kind '${(terminal as any).kind}'`, + ); + } + } + const fallthrough = fn.body.blocks.get(terminal.fallthrough)!; + const phis: Array = Array.from(fallthrough.phis).map(phi => { + return { + kind: 'Phi', + place: phi.place, + operands: new Map( + Array.from(phi.operands, ([blockId, operand]) => { + return [context.getBreakMapping(blockId, phi.place.loc), operand]; + }), + ), + }; + }); + const fallthroughNode: FallthroughNode = { + kind: 'Fallthrough', + control: terminalNode.id, + id: fallthroughNodeId, + loc: terminal.loc, + outputs: [], + branches: branches.map(pred => pred.exit), + phis, + }; + context.putNode(fallthroughNode); + + const controlDependencies: Set = new Set(); + const joinedDeclarations: Map = new Map(); + const joinedScopes: Set = new Set(); + for (const predecessorBlock of branches) { + /* + * track scopes that were mutated in any of the branches so that we can + * establish control depends to order the branch/join relative to previous + * subsequent mutations of those scopes + */ + for (const [scope] of predecessorBlock.context.eachScope()) { + joinedScopes.add(scope); + } + /* + * Track variables that were read/reassigned in any of the predecessors + * so that subsequent writes/reads can take the join node as a control + */ + for (const [ + declarationId, + {write, read}, + ] of predecessorBlock.context.eachDeclaration()) { + if (write) { + joinedDeclarations.set(declarationId, 'write'); + } else if (read && !joinedDeclarations.has(declarationId)) { + joinedDeclarations.set(declarationId, 'read'); + } + } + } + for (const scope of joinedScopes) { + const scopeControl = context.loadScopeControl(scope); + if (scopeControl != null) { + controlDependencies.add(scopeControl); + } + context.recordScopeMutation(scope, fallthroughNode.id); + } + // Add control dependencies and record reads/writes accordingly. + for (const [declarationId, declType] of joinedDeclarations) { + if (declType === 'write') { + /* + * If there was a write in any of the branches, we take a write + * dependency (on the last read/write) and record the if as the + * last write + */ + const writeControl = context.loadDeclarationControlForWrite( + declarationId, + terminal.loc, + ); + if (writeControl != null) { + controlDependencies.add(writeControl); + } + context.recordDeclarationWrite(declarationId, fallthroughNode.id); + } else { + /* + * If there were only reads in the branches, we take a read + * dependency (on the last write) and record the if as the + * last read + */ + const readControl = context.loadDeclarationControlForRead(declarationId); + if (readControl != null) { + controlDependencies.add(readControl); + } + context.recordDeclarationRead(declarationId, fallthroughNode.id); + } + } + + terminalNode.dependencies = Array.from(controlDependencies); + return fallthroughNodeId; +} + function getScopeForInstruction(instr: Instruction): ReactiveScope | null { let scope: ReactiveScope | null = null; for (const operand of eachInstructionValueOperand(instr.value)) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts index 4bf766c068d24..c9b6810646006 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts @@ -52,16 +52,19 @@ export function makeReactiveId(id: number): ReactiveId { } export type ReactiveNode = + | ControlNode | EntryNode - | LoadNode - | StoreNode - | LoadArgumentNode - | InstructionNode - | BranchNode | FallthroughNode - | ControlNode + | GotoNode + | IfNode + | InstructionNode + | LabelNode + | LoadArgumentNode + | LoadNode + | OptionalNode | ReturnNode - | GotoNode; + | StoreNode + | ThrowNode; export type NodeReference = { node: ReactiveId; @@ -129,6 +132,16 @@ export type ReturnNode = { control: ReactiveId; }; +export type ThrowNode = { + kind: 'Throw'; + id: ReactiveId; + loc: SourceLocation; + value: NodeReference; + outputs: Array; + dependencies: Array; + control: ReactiveId; +}; + export type GotoNode = { kind: 'Goto'; id: ReactiveId; @@ -140,24 +153,27 @@ export type GotoNode = { variant: GotoVariant; }; -export type BranchNode = { - kind: 'Branch'; +export type LabelNode = { + kind: 'Label'; id: ReactiveId; loc: SourceLocation; outputs: Array; - dependencies: Array; // values/scopes depended on by more than one branch, or by the terminal + dependencies: Array; control: ReactiveId; - fallthrough: ReactiveId; - terminal: BranchTerminal; + block: {entry: ReactiveId; exit: ReactiveId}; }; -export type BranchTerminal = IfBranch; - -export type IfBranch = { +export type IfNode = { kind: 'If'; + id: ReactiveId; + loc: SourceLocation; + outputs: Array; + dependencies: Array; // values/scopes depended on by more than one branch, or by the terminal + control: ReactiveId; test: NodeReference; consequent: {entry: ReactiveId; exit: ReactiveId}; alternate: {entry: ReactiveId; exit: ReactiveId}; + fallthrough: ReactiveId; }; export type FallthroughNode = { @@ -167,6 +183,13 @@ export type FallthroughNode = { outputs: Array; control: ReactiveId; // always the corresponding branch node branches: Array; // the other control-flow paths that reach the fallthrough + phis: Array; +}; + +export type PhiNode = { + kind: 'Phi'; + place: Place; + operands: Map; }; export type ControlNode = { @@ -174,7 +197,17 @@ export type ControlNode = { id: ReactiveId; loc: SourceLocation; outputs: Array; - dependencies: Array; + control: ReactiveId; +}; + +export type OptionalNode = { + kind: 'Optional'; + id: ReactiveId; + loc: SourceLocation; + outputs: Array; + object: NodeReference; + continuation: NodeReference; + optional: boolean; control: ReactiveId; }; @@ -241,30 +274,21 @@ export function reversePostorderReactiveGraph(graph: ReactiveGraph): void { graph.nodes = nodes; } -export function* eachBranchTerminalDependency( - terminal: BranchTerminal, -): Iterable { - switch (terminal.kind) { - case 'If': { - yield terminal.test.node; - } - } -} - export function* eachNodeDependency(node: ReactiveNode): Iterable { switch (node.kind) { + case 'Control': case 'Entry': case 'LoadArgument': { break; } - case 'Goto': - case 'Control': { + case 'Label': + case 'Goto': { yield* node.dependencies; break; } - case 'Branch': { + case 'If': { yield* node.dependencies; - yield* eachBranchTerminalDependency(node.terminal); + yield node.test.node; break; } case 'Fallthrough': { @@ -279,13 +303,19 @@ export function* eachNodeDependency(node: ReactiveNode): Iterable { yield node.value.node; break; } + case 'Throw': case 'Return': { yield* node.dependencies; yield node.value.node; break; } case 'Value': { - yield* [...node.dependencies.keys()]; + yield* node.dependencies.keys(); + break; + } + case 'Optional': { + yield node.object.node; + yield node.continuation.node; break; } default: { @@ -297,21 +327,11 @@ export function* eachNodeDependency(node: ReactiveNode): Iterable { } } -export function* eachBranchTerminalReference( - terminal: BranchTerminal, -): Iterable { - switch (terminal.kind) { - case 'If': { - yield terminal.test; - break; - } - } -} - export function* eachNodeReference( node: ReactiveNode, ): Iterable { switch (node.kind) { + case 'Label': case 'Goto': case 'Entry': case 'Control': @@ -326,12 +346,13 @@ export function* eachNodeReference( yield node.value; break; } + case 'Throw': case 'Return': { yield node.value; break; } - case 'Branch': { - yield* eachBranchTerminalReference(node.terminal); + case 'If': { + yield node.test; break; } case 'Fallthrough': { @@ -345,6 +366,11 @@ export function* eachNodeReference( })); break; } + case 'Optional': { + yield node.object; + yield node.continuation; + break; + } default: { assertExhaustive(node, `Unexpected node kind '${(node as any).kind}'`); } @@ -416,9 +442,7 @@ function writeReactiveNodes( break; } case 'Control': { - buffer.push( - `£${id} Control${control} deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]`, - ); + buffer.push(`£${id} Control${control}`); break; } case 'Load': { @@ -431,35 +455,48 @@ function writeReactiveNodes( ); break; } + case 'Throw': { + buffer.push( + `£${id} Throw ${printNodeReference(node.value)} deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]${control}`, + ); + break; + } case 'Return': { buffer.push( `£${id} Return ${printNodeReference(node.value)} deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]${control}`, ); break; } - case 'Branch': { + case 'Label': { buffer.push( - `£${id} Branch deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]${control}`, + `£${id} Label block=£${node.block.entry}:${node.block.exit} deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]${control}`, + ); + break; + } + case 'If': { + buffer.push(`£${id} If test=${printNodeReference(node.test)}`); + buffer.push( + ` consequent=£${node.consequent.entry}:${node.consequent.exit} `, + ); + buffer.push( + ` alternate=£${node.alternate.entry}:${node.alternate.exit} `, + ); + buffer.push( + ` deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]${control}`, ); - switch (node.terminal.kind) { - case 'If': { - buffer.push( - ` If test=${printNodeReference(node.terminal.test)} ` + - `consequent=£${node.terminal.consequent.entry}:${node.terminal.consequent.exit} ` + - `alternate=£${node.terminal.alternate.entry}:${node.terminal.alternate.exit}`, - ); - break; - } - default: { - // assertExhaustive(node.terminal, `Unsupported terminal kind ${(node.terminal as any).kind}`); - } - } break; } case 'Fallthrough': { buffer.push( `£${id} Fallthrough${control} branches=[${node.branches.map(id => `£${id}`).join(', ')}]`, ); + for (const phi of node.phis) { + const operands = []; + for (const [pred, operand] of phi.operands) { + operands.push(`£${pred} => ${printPlace(operand)}`); + } + buffer.push(` ${printPlace(phi.place)} => [${operands.join(', ')}]`); + } break; } case 'Value': { @@ -470,6 +507,13 @@ function writeReactiveNodes( buffer.push(' ' + printInstruction(node.value)); break; } + case 'Optional': { + buffer.push( + `£${id} Optional ${printNodeReference(node.object)} ` + + `${node.optional ? '?.' : '.'} ${printNodeReference(node.continuation)} ${control}`, + ); + break; + } default: { assertExhaustive(node, `Unexpected node kind ${(node as any).kind}`); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.expect.md index 82e39840ea332..ed991c902e4f8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.expect.md @@ -6,8 +6,12 @@ function Component(props) { let element = props.default; let other = element; - if (props.cond) { - element =
; + label: if (props.cond) { + if (props.ret) { + break label; + } else { + element =
; + } } else { element = ; } @@ -24,15 +28,19 @@ function Component(props) { const $ = _c(5); let element = props.default; const other = element; - if (props.cond) { - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 =
; - $[0] = t0; + bb0: if (props.cond) { + if (props.ret) { + break bb0; } else { - t0 = $[0]; + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
; + $[0] = t0; + } else { + t0 = $[0]; + } + element = t0; } - element = t0; } else { let t0; if ($[1] === Symbol.for("react.memo_cache_sentinel")) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.js index 0153c889229c3..24a861370d7c0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-graph-conditional-let.js @@ -2,8 +2,12 @@ function Component(props) { let element = props.default; let other = element; - if (props.cond) { - element =
; + label: if (props.cond) { + if (props.ret) { + break label; + } else { + element =
; + } } else { element = ; }