@samitouri / QOS-React-1 / commits / 48ecaf92d5

Reset scopes from AnalyzeFunctions in InferReactiveScopeVariables

Updates InferReactiveScopeVariables to first prune scopes attached during AnalyzeFunctions. This ensures that after this pass the only scopes that exist on identifiers in the outer program are those that the pass explicitly inferred, and not accidentally leftover.

Joe Savona committed Mar 25, 2024 at 15:31 UTC 48ecaf92d51813a1b1f1bcba1cdcbf48a501f2c6
2 files changed +47 -55
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+24 -34
@@ -9,7 +9,6 @@ import { Environment } from "../HIR";
9 import {
10 HIRFunction,
11 Identifier,
12 - IdentifierId,
12 Instruction,
13 makeInstructionId,
14 Place,
@@ -17,8 +16,10 @@ import {
16 } from "../HIR/HIR";
17 import {
18 doesPatternContainSpreadElement,
19 + eachInstructionLValue,
20 eachInstructionOperand,
21 eachPatternOperand,
22 + eachTerminalOperand,
23 } from "../HIR/visitors";
24 import DisjointSet from "../Utils/DisjointSet";
25 import { assertExhaustive } from "../Utils/utils";
@@ -80,6 +81,27 @@ import { assertExhaustive } from "../Utils/utils";
81 * ```
82 */
83 export function inferReactiveScopeVariables(fn: HIRFunction): void {
84 + // First reset any scopes that may have been created from inner functions
85 + for (const [, block] of fn.body.blocks) {
86 + for (const phi of block.phis) {
87 + phi.id.scope = null;
88 + for (const [, operand] of phi.operands) {
89 + operand.scope = null;
90 + }
91 + }
92 + for (const instr of block.instructions) {
93 + for (const lvalue of eachInstructionLValue(instr)) {
94 + lvalue.identifier.scope = null;
95 + }
96 + for (const operand of eachInstructionOperand(instr)) {
97 + operand.identifier.scope = null;
98 + }
99 + }
100 + for (const operand of eachTerminalOperand(block.terminal)) {
101 + operand.identifier.scope = null;
102 + }
103 + }
104 +
105 /*
106 * Represents the set of reactive scopes as disjoint sets of identifiers
107 * that mutate together.
@@ -189,10 +211,6 @@ export function findDisjointMutableValues(
211 fn: HIRFunction
212 ): DisjointSet<Identifier> {
213 const scopeIdentifiers = new DisjointSet<Identifier>();
192 - const declarations: Map<IdentifierId, Place> | null = fn.env.config
193 - .enableForest
194 - ? new Map()
195 - : null;
214 for (const [_, block] of fn.body.blocks) {
215 /*
216 * If a phi is mutated after creation, then we need to alias all of its operands such that they
@@ -221,14 +239,7 @@ export function findDisjointMutableValues(
239 if (range.end > range.start + 1 || mayAllocate(fn.env, instr)) {
240 operands.push(instr.lvalue!.identifier);
241 }
224 - if (instr.value.kind === "DeclareLocal") {
225 - if (declarations !== null) {
226 - declarations.set(
227 - instr.value.lvalue.place.identifier.id,
228 - instr.value.lvalue.place
229 - );
230 - }
231 - } else if (
242 + if (
243 instr.value.kind === "StoreLocal" ||
244 instr.value.kind === "StoreContext"
245 ) {
@@ -244,27 +255,6 @@ export function findDisjointMutableValues(
255 ) {
256 operands.push(instr.value.value.identifier);
257 }
247 - if (declarations !== null) {
248 - const declaration = declarations.get(
249 - instr.value.lvalue.place.identifier.id
250 - );
251 - if (declaration !== undefined) {
252 - declaration.identifier.mutableRange.end = makeInstructionId(
253 - Math.max(
254 - declaration.identifier.mutableRange.end,
255 - instr.value.lvalue.place.identifier.mutableRange.end
256 - )
257 - );
258 - instr.value.lvalue.place.identifier.mutableRange.start =
259 - makeInstructionId(
260 - Math.min(
261 - declaration.identifier.mutableRange.start,
262 - instr.value.lvalue.place.identifier.mutableRange.start
263 - )
264 - );
265 - operands.push(declaration.identifier);
266 - }
267 - }
258 } else if (instr.value.kind === "Destructure") {
259 for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
260 if (
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-mutable-range-shared-inner-outer-function.expect.md
+23 -21
@@ -34,34 +34,36 @@ export const FIXTURE_ENTRYPOINT = {
34 import { unstable_useMemoCache as useMemoCache } from "react"; // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
35 let cond = true;
36 function Component(props) {
37 - const $ = useMemoCache(1);
37 + const $ = useMemoCache(2);
38 + let a;
39 + let b;
40 let t0;
41 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
40 - t0 = Symbol.for("react.early_return_sentinel");
41 - bb8: {
42 - let a;
43 - let b;
44 - const f = () => {
45 - if (cond) {
46 - a = {};
47 - b = [];
48 - } else {
49 - a = {};
50 - b = [];
51 - }
52 - a.property = true;
53 - b.push(false);
54 - };
55 - t0 = <div onClick={f} />;
56 - break bb8;
57 - }
42 + t0 = () => {
43 + if (cond) {
44 + a = {};
45 + b = [];
46 + } else {
47 + a = {};
48 + b = [];
49 + }
50 +
51 + a.property = true;
52 + b.push(false);
53 + };
54 $[0] = t0;
55 } else {
56 t0 = $[0];
57 }
62 - if (t0 !== Symbol.for("react.early_return_sentinel")) {
63 - return t0;
58 + const f = t0;
59 + let t1;
60 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
61 + t1 = <div onClick={f} />;
62 + $[1] = t1;
63 + } else {
64 + t1 = $[1];
65 }
66 + return t1;
67 }
68
69 export const FIXTURE_ENTRYPOINT = {