Fix promotion of locals referenced outside of scopes

This commit is contained in:
Joe Savona
2024-03-06 16:19:28 -08:00
parent 854a810f05
commit 449aa70f99
3 changed files with 75 additions and 5 deletions
@@ -23,6 +23,7 @@ import {
ReactiveScopeDependency,
ReactiveTerminalStatement,
ReactiveValue,
ScopeId,
} from "../HIR/HIR";
import {
eachInstructionValueOperand,
@@ -75,18 +76,18 @@ type TemporariesUsedOutsideDefiningScope = {
* tracks all relevant temporary declarations (currently LoadLocal and PropertyLoad)
* and the scope where they are defined
*/
declarations: Map<IdentifierId, ReactiveScope>;
declarations: Map<IdentifierId, ScopeId>;
// temporaries used outside of their defining scope
usedOutsideDeclaringScope: Set<IdentifierId>;
};
class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOutsideDefiningScope> {
scopes: Array<ReactiveScope> = [];
scopes: Array<ScopeId> = [];
override visitScope(
scope: ReactiveScopeBlock,
state: TemporariesUsedOutsideDefiningScope
): void {
this.scopes.push(scope.scope);
this.scopes.push(scope.scope.id);
this.traverseScope(scope, state);
this.scopes.pop();
}
@@ -95,6 +96,9 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
instruction: ReactiveInstruction,
state: TemporariesUsedOutsideDefiningScope
): void {
// Visit all places first, then record temporaries which may need to be promoted
this.traverseInstruction(instruction, state);
const scope = this.scopes.at(-1);
if (instruction.lvalue === null || scope === undefined) {
return;
@@ -110,7 +114,6 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
break;
}
}
this.traverseInstruction(instruction, state);
}
override visitPlace(
@@ -119,7 +122,7 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
state: TemporariesUsedOutsideDefiningScope
): void {
const declaringScope = state.declarations.get(place.identifier.id);
if (this.scopes.length === 0 || declaringScope === undefined) {
if (declaringScope === undefined) {
return;
}
if (this.scopes.indexOf(declaringScope) === -1) {
@@ -0,0 +1,60 @@
## Input
```javascript
// @flow @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
function Component(listItem, thread) {
const isFoo = isFooThread(thread.threadType);
const body = useBar(listItem, [getBadgeText(listItem, isFoo)]);
return body;
}
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(listItem, thread) {
const $ = useMemoCache(7);
let t0;
let t1;
let t2;
if ($[0] !== thread.threadType || $[1] !== listItem) {
const isFoo = isFooThread(thread.threadType);
t1 = useBar;
t2 = listItem;
t0 = getBadgeText(listItem, isFoo);
$[0] = thread.threadType;
$[1] = listItem;
$[2] = t0;
$[3] = t1;
$[4] = t2;
} else {
t0 = $[2];
t1 = $[3];
t2 = $[4];
}
let t3;
if ($[5] !== t0) {
t3 = [t0];
$[5] = t0;
$[6] = t3;
} else {
t3 = $[6];
}
const body = t1(t2, t3);
return body;
}
```
### Eval output
(kind: exception) Fixture not implemented!
logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
'\n' +
' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
'\n' +
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
@@ -0,0 +1,7 @@
// @flow @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
function Component(listItem, thread) {
const isFoo = isFooThread(thread.threadType);
const body = useBar(listItem, [getBadgeText(listItem, isFoo)]);
return body;
}