diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts index 8bf6f6634d..181e585093 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts @@ -1053,10 +1053,35 @@ export function isMutableEffect( export type ReactiveScope = { id: ScopeId; range: MutableRange; + + /** + * The inputs to this reactive scope + */ dependencies: ReactiveScopeDependencies; + + /** + * The set of values produced by this scope. This may be empty + * for scopes that produce reassignments only. + */ declarations: Map; + + /** + * A mutable range may sometimes include a reassignment of some variable. + * This is the set of identifiers which are reassigned by this scope. + */ reassignments: Set; + /** + * Reactive scopes may contain a return statement, which needs to be replayed + * whenever the inputs to the scope have not changed since the previous execution. + * If the reactive scope has an early return, this variable stores the temporary + * identifier to which the return value will be assigned. See PropagateEarlyReturns + * for more about how early returns in reactive scopes are compiled and represented. + * + * This value is null for scopes that do not contain early returns. + */ + earlyReturnValue: { value: IdentifierId; loc: SourceLocation } | null; + /* * Some passes may merge scopes together. The merged set contains the * ids of scopes that were merged into this one, for passes that need diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts index c62e2336a7..8c69ecfdf3 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -451,11 +451,20 @@ function codegenReactiveScope( const computationBlock = codegenBlock(cx, block); computationBlock.body.push(...cacheStoreStatements); const memoBlock = t.blockStatement(cacheLoadStatements); - const memoStatement = t.ifStatement( - testCondition, - computationBlock, - memoBlock - ); + + let memoStatement; + if (scope.earlyReturnValue !== null) { + // Has early return + CompilerError.throwTodo({ + reason: `Codegen support for reactive scopes with early return`, + loc: scope.earlyReturnValue.loc, + description: null, + suggestions: null, + }); + } else { + memoStatement = t.ifStatement(testCondition, computationBlock, memoBlock); + } + if (cx.env.config.enableMemoizationComments) { if (changeExpressionComments.length) { t.addComment( diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/InferReactiveScopeVariables.ts index 0e38cbf251..79b550bc42 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -199,6 +199,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void { dependencies: new Set(), declarations: new Map(), reassignments: new Set(), + earlyReturnValue: null, merged: new Set(), }; scopes.set(groupIdentifier, scope); diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts index 7dbd5753c6..13b8be8267 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts @@ -42,17 +42,32 @@ export function printReactiveFunction(fn: ReactiveFunction): string { } export function printReactiveScopeSummary(scope: ReactiveScope): string { - return `scope @${scope.id} [${scope.range.start}:${ - scope.range.end - }] dependencies=[${Array.from(scope.dependencies) - .map((dep) => printDependency(dep)) - .join(", ")}] declarations=[${Array.from(scope.declarations) - .map(([, decl]) => - printIdentifier({ ...decl.identifier, scope: decl.scope }) - ) - .join(", ")}] reassignments=[${Array.from(scope.reassignments).map( - (reassign) => printIdentifier(reassign) - )}]`; + const items = []; + // If the scope has a return value it needs a label + items.push("scope"); + items.push(`@${scope.id}`); + items.push(`[${scope.range.start}:${scope.range.end}]`); + items.push( + `dependencies=[${Array.from(scope.dependencies) + .map((dep) => printDependency(dep)) + .join(", ")}]` + ); + items.push( + `declarations=[${Array.from(scope.declarations) + .map(([, decl]) => + printIdentifier({ ...decl.identifier, scope: decl.scope }) + ) + .join(", ")}]` + ); + items.push( + `reassignments=[${Array.from(scope.reassignments).map((reassign) => + printIdentifier(reassign) + )}]` + ); + if (scope.earlyReturnValue !== null) { + items.push(`earlyReturn=${scope.earlyReturnValue.value}`); + } + return items.join(" "); } export function writeReactiveBlock(