@samitouri / QOS-React / commits / 3aaf8be25c

[hir] Use a stable identity for undefined value

InferReferenceEffects uses object identity to merge states, which breaks when we create a new object to model `undefined`. Two value objects representing `undefined` are not equal due to referential equality. Instead, let's use a singleton to represent `undefined` value.

Sathya Gunasekaran committed Oct 4, 2023 at 12:11 UTC 3aaf8be25cc4db8307eb3f6ce65a0bc4101d43e8
6 files changed +97 -5
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+8 -5
@@ -12,6 +12,7 @@ import {
12 BlockId,
13 CallExpression,
14 Effect,
15 + GeneratedSource,
16 HIRFunction,
17 IdentifierId,
18 InstructionKind,
@@ -39,6 +40,12 @@ import {
40 } from "../HIR/visitors";
41 import { assertExhaustive } from "../Utils/utils";
42
43 +const UndefinedValue: InstructionValue = {
44 + kind: "Primitive",
45 + loc: GeneratedSource,
46 + value: undefined,
47 +};
48 +
49 /**
50 * For every usage of a value in the given function, infers the effect or action
51 * taken at that reference. Each reference is inferred as exactly one of:
@@ -933,11 +940,7 @@ function inferBlock(
940 continue;
941 }
942 case "DeclareLocal": {
936 - const value: InstructionValue = {
937 - kind: "Primitive",
938 - loc: instrValue.loc,
939 - value: undefined,
940 - };
943 + const value = UndefinedValue;
944 state.initialize(
945 value,
946 // Catch params may be aliased to mutable values
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-loop-let-undefined-decl.expect.md new
+46
@@ -0,0 +1,46 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function useFoo() {
6 + for (let i = 0; i <= 5; i++) {
7 + let color;
8 + if (isSelected) {
9 + color = isCurrent ? "#FFCC22" : "#FF5050";
10 + } else {
11 + color = isCurrent ? "#CCFF03" : "#CCCCCC";
12 + }
13 + console.log(color);
14 + }
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + params: [],
19 + fn: useFoo,
20 +};
21 +
22 +```
23 +
24 +## Code
25 +
26 +```javascript
27 +function useFoo() {
28 + for (let i = 0; i <= 5; i++) {
29 + let color = undefined;
30 + if (isSelected) {
31 + color = isCurrent ? "#FFCC22" : "#FF5050";
32 + } else {
33 + color = isCurrent ? "#CCFF03" : "#CCCCCC";
34 + }
35 +
36 + console.log(color);
37 + }
38 +}
39 +
40 +export const FIXTURE_ENTRYPOINT = {
41 + params: [],
42 + fn: useFoo,
43 +};
44 +
45 +```
46 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-loop-let-undefined-decl.js new
+16
@@ -0,0 +1,16 @@
1 +function useFoo() {
2 + for (let i = 0; i <= 5; i++) {
3 + let color;
4 + if (isSelected) {
5 + color = isCurrent ? "#FFCC22" : "#FF5050";
6 + } else {
7 + color = isCurrent ? "#CCFF03" : "#CCCCCC";
8 + }
9 + console.log(color);
10 + }
11 +}
12 +
13 +export const FIXTURE_ENTRYPOINT = {
14 + params: [],
15 + fn: useFoo,
16 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/loop-unused-let.expect.md new
+21
@@ -0,0 +1,21 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function useFoo() {
6 + while (1) {
7 + let foo;
8 + }
9 +}
10 +
11 +```
12 +
13 +## Code
14 +
15 +```javascript
16 +function useFoo() {
17 + while (1) {}
18 +}
19 +
20 +```
21 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/loop-unused-let.js new
+5
@@ -0,0 +1,5 @@
1 +function useFoo() {
2 + while (1) {
3 + let foo;
4 + }
5 +}
compiler/packages/sprout/src/SproutTodoFilter.ts
+1
@@ -461,6 +461,7 @@ const skipFilter = new Set([
461 "fbtparam-with-jsx-fragment-value",
462 "fbt-preserve-jsxtext",
463 "useContext-mutable-value",
464 + "loop-unused-let",
465 ]);
466
467 export default skipFilter;