@samitouri / QOS-React / commits / 8be5423e3b

Type Array.prototype.map/filter

We can now type `Array.prototype.{map,filter}`: * The callee is ConditionallyMutable because, although the array itself is not modified, its items flow into the lambda and may be modified there. * The argument is ConditionallyMutable because it accepts both mutable and immutable lambdas. Mutate would disallow immutable lambdas (wrong), while Read would be incorrect for mutable lambdas since calling them triggers mutation.

Joe Savona committed May 25, 2023 at 14:50 UTC 8be5423e3bf61f88545cf90ce170aed5647b2c3c
5 files changed +149
compiler/forget/src/HIR/ObjectShape.ts
+26
@@ -204,6 +204,32 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
204 returnValueKind: ValueKind.Immutable,
205 }),
206 ],
207 + [
208 + "map",
209 + addFunction(BUILTIN_SHAPES, [], {
210 + positionalParams: [],
211 + restParam: Effect.ConditionallyMutate,
212 + returnType: { kind: "Object", shapeId: BuiltInArrayId },
213 + // callee is ConditionallyMutate because items of the array
214 + // flow into the lambda and may be mutated there, even though
215 + // the array object itself is not modified
216 + calleeEffect: Effect.ConditionallyMutate,
217 + returnValueKind: ValueKind.Mutable,
218 + }),
219 + ],
220 + [
221 + "filter",
222 + addFunction(BUILTIN_SHAPES, [], {
223 + positionalParams: [],
224 + restParam: Effect.ConditionallyMutate,
225 + returnType: { kind: "Object", shapeId: BuiltInArrayId },
226 + // callee is ConditionallyMutate because items of the array
227 + // flow into the lambda and may be mutated there, even though
228 + // the array object itself is not modified
229 + calleeEffect: Effect.ConditionallyMutate,
230 + returnValueKind: ValueKind.Mutable,
231 + }),
232 + ],
233 // TODO: rest of Array properties
234 ]);
235
compiler/forget/src/__tests__/fixtures/compiler/array-map-frozen-array.expect.md new
+54
@@ -0,0 +1,54 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = [];
7 + <dif>{x}</dif>;
8 + const y = x.map((item) => item);
9 + return [x, y];
10 +}
11 +
12 +```
13 +
14 +## Code
15 +
16 +```javascript
17 +import { unstable_useMemoCache as useMemoCache } from "react";
18 +function Component(props) {
19 + const $ = useMemoCache(4);
20 + let t0;
21 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 + t0 = [];
23 + $[0] = t0;
24 + } else {
25 + t0 = $[0];
26 + }
27 + const x = t0;
28 + let t2;
29 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
30 + let t1;
31 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
32 + t1 = (item) => item;
33 + $[2] = t1;
34 + } else {
35 + t1 = $[2];
36 + }
37 + t2 = x.map(t1);
38 + $[1] = t2;
39 + } else {
40 + t2 = $[1];
41 + }
42 + const y = t2;
43 + let t3;
44 + if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
45 + t3 = [x, y];
46 + $[3] = t3;
47 + } else {
48 + t3 = $[3];
49 + }
50 + return t3;
51 +}
52 +
53 +```
54 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/array-map-frozen-array.js new
+6
@@ -0,0 +1,6 @@
1 +function Component(props) {
2 + const x = [];
3 + <dif>{x}</dif>;
4 + const y = x.map((item) => item);
5 + return [x, y];
6 +}
compiler/forget/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda.expect.md new
+55
@@ -0,0 +1,55 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = [];
7 + const y = x.map((item) => {
8 + item.updated = true;
9 + return item;
10 + });
11 + return [x, y];
12 +}
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +import { unstable_useMemoCache as useMemoCache } from "react";
20 +function Component(props) {
21 + const $ = useMemoCache(4);
22 + let t1;
23 + let x;
24 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25 + x = [];
26 + let t0;
27 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
28 + t0 = (item) => {
29 + item.updated = true;
30 + return item;
31 + };
32 + $[2] = t0;
33 + } else {
34 + t0 = $[2];
35 + }
36 + t1 = x.map(t0);
37 + $[0] = t1;
38 + $[1] = x;
39 + } else {
40 + t1 = $[0];
41 + x = $[1];
42 + }
43 + const y = t1;
44 + let t2;
45 + if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
46 + t2 = [x, y];
47 + $[3] = t2;
48 + } else {
49 + t2 = $[3];
50 + }
51 + return t2;
52 +}
53 +
54 +```
55 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda.js new
+8
@@ -0,0 +1,8 @@
1 +function Component(props) {
2 + const x = [];
3 + const y = x.map((item) => {
4 + item.updated = true;
5 + return item;
6 + });
7 + return [x, y];
8 +}