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
```