@samitouri / QOS-React / commits / 0322e4dbd4

InferReferenceEffects uses function types for CallExpression

Updates the InferReferenceEffects logic for CallExpression to work similarly to MethodCall, where we take into account the function signature (if present) when inferring the effects and return kind.

Joe Savona committed May 18, 2023 at 15:52 UTC 0322e4dbd41769f335cf77d4fda0cf102c684a8b
5 files changed +87 -40
compiler/forget/src/HIR/HIR.ts
+8 -6
@@ -538,6 +538,13 @@ export type MethodCall = {
538 loc: SourceLocation;
539 };
540
541 +export type CallExpression = {
542 + kind: "CallExpression";
543 + callee: Place;
544 + args: Array<Place | SpreadPattern>;
545 + loc: SourceLocation;
546 +};
547 +
548 /**
549 * The value of a given instruction. Note that values are not recursive: complex
550 * values such as objects or arrays are always defined by instructions to define
@@ -605,12 +612,7 @@ export type InstructionValue =
612 args: Array<Place | SpreadPattern>;
613 loc: SourceLocation;
614 }
608 - | {
609 - kind: "CallExpression";
610 - callee: Place;
611 - args: Array<Place | SpreadPattern>;
612 - loc: SourceLocation;
613 - }
615 + | CallExpression
616 | MethodCall
617 | {
618 kind: "UnaryExpression";
compiler/forget/src/Inference/InferReferenceEffects.ts
+37 -4
@@ -11,6 +11,7 @@ import { Environment } from "../HIR";
11 import {
12 BasicBlock,
13 BlockId,
14 + CallExpression,
15 Effect,
16 HIRFunction,
17 IdentifierId,
@@ -707,8 +708,6 @@ function inferBlock(
708 continue;
709 }
710 case "CallExpression": {
710 - valueKind = ValueKind.Mutable;
711 - effectKind = Effect.Mutate;
711 const hook =
712 instrValue.callee.identifier.type.kind === "Hook"
713 ? instrValue.callee.identifier.type.definition
@@ -716,8 +715,42 @@ function inferBlock(
715 if (hook !== null) {
716 effectKind = hook.effectKind;
717 valueKind = hook.valueKind;
718 + break;
719 }
720 - break;
720 +
721 + const signature = getFunctionCallSignature(
722 + env,
723 + instrValue.callee.identifier.type
724 + );
725 +
726 + const effects =
727 + signature !== null
728 + ? getMethodCallEffects(instrValue, signature)
729 + : null;
730 + for (let i = 0; i < instrValue.args.length; i++) {
731 + const arg = instrValue.args[i];
732 + const place = arg.kind === "Identifier" ? arg : arg.place;
733 + if (effects !== null) {
734 + // If effects are inferred for an argument, we should fail invalid
735 + // mutating effects
736 + state.referenceAndCheckError(place, effects[i]);
737 + } else {
738 + state.reference(place, Effect.Mutate);
739 + }
740 + }
741 + if (signature !== null) {
742 + state.referenceAndCheckError(
743 + instrValue.callee,
744 + signature.calleeEffect
745 + );
746 + } else {
747 + state.reference(instrValue.callee, Effect.Mutate);
748 + }
749 +
750 + state.initialize(instrValue, ValueKind.Mutable);
751 + state.define(instr.lvalue, instrValue);
752 + instr.lvalue.effect = Effect.Mutate;
753 + continue;
754 }
755 case "MethodCall": {
756 invariant(
@@ -1003,7 +1036,7 @@ function getFunctionCallSignature(
1036 * @returns Inferred effects of function arguments, or null if inference fails.
1037 */
1038 function getMethodCallEffects(
1006 - fn: MethodCall,
1039 + fn: MethodCall | CallExpression,
1040 sig: FunctionSignature
1041 ): Array<Effect> | null {
1042 const results = [];
compiler/forget/src/__tests__/fixtures/compiler/globals-Boolean.expect.md
+14 -10
@@ -17,25 +17,29 @@ import { unstable_useMemoCache as useMemoCache } from "react";
17 function Component(props) {
18 const $ = useMemoCache(3);
19 let t0;
20 - let x;
20 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - x = {};
23 - t0 = Boolean(x);
21 + t0 = {};
22 $[0] = t0;
25 - $[1] = x;
23 } else {
24 t0 = $[0];
28 - x = $[1];
25 }
30 - const y = t0;
26 + const x = t0;
27 let t1;
28 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
29 + t1 = Boolean(x);
30 + $[1] = t1;
31 + } else {
32 + t1 = $[1];
33 + }
34 + const y = t1;
35 + let t2;
36 if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
33 - t1 = [x, y];
34 - $[2] = t1;
37 + t2 = [x, y];
38 + $[2] = t2;
39 } else {
36 - t1 = $[2];
40 + t2 = $[2];
41 }
38 - return t1;
42 + return t2;
43 }
44
45 ```
compiler/forget/src/__tests__/fixtures/compiler/globals-Number.expect.md
+14 -10
@@ -17,25 +17,29 @@ import { unstable_useMemoCache as useMemoCache } from "react";
17 function Component(props) {
18 const $ = useMemoCache(3);
19 let t0;
20 - let x;
20 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - x = {};
23 - t0 = Number(x);
21 + t0 = {};
22 $[0] = t0;
25 - $[1] = x;
23 } else {
24 t0 = $[0];
28 - x = $[1];
25 }
30 - const y = t0;
26 + const x = t0;
27 let t1;
28 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
29 + t1 = Number(x);
30 + $[1] = t1;
31 + } else {
32 + t1 = $[1];
33 + }
34 + const y = t1;
35 + let t2;
36 if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
33 - t1 = [x, y];
34 - $[2] = t1;
37 + t2 = [x, y];
38 + $[2] = t2;
39 } else {
36 - t1 = $[2];
40 + t2 = $[2];
41 }
38 - return t1;
42 + return t2;
43 }
44
45 ```
compiler/forget/src/__tests__/fixtures/compiler/globals-String.expect.md
+14 -10
@@ -17,25 +17,29 @@ import { unstable_useMemoCache as useMemoCache } from "react";
17 function Component(props) {
18 const $ = useMemoCache(3);
19 let t0;
20 - let x;
20 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - x = {};
23 - t0 = String(x);
21 + t0 = {};
22 $[0] = t0;
25 - $[1] = x;
23 } else {
24 t0 = $[0];
28 - x = $[1];
25 }
30 - const y = t0;
26 + const x = t0;
27 let t1;
28 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
29 + t1 = String(x);
30 + $[1] = t1;
31 + } else {
32 + t1 = $[1];
33 + }
34 + const y = t1;
35 + let t2;
36 if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
33 - t1 = [x, y];
34 - $[2] = t1;
37 + t2 = [x, y];
38 + $[2] = t2;
39 } else {
36 - t1 = $[2];
40 + t2 = $[2];
41 }
38 - return t1;
42 + return t2;
43 }
44
45 ```