@samitouri / QOS-React / commits / 7f994241b0

Infer type of ref argument

Extend type inference to infer second argument of component as a ref type

Sathya Gunasekaran committed Feb 29, 2024 at 14:47 UTC 7f994241b0ad54436b024c7097112e15b7cf0f26
5 files changed +60 -64
compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts
+15 -1
@@ -18,7 +18,11 @@ import {
18 TypeId,
19 TypeVar,
20 } from "../HIR/HIR";
21 -import { BuiltInArrayId, BuiltInObjectId } from "../HIR/ObjectShape";
21 +import {
22 + BuiltInArrayId,
23 + BuiltInObjectId,
24 + BuiltInUseRefId,
25 +} from "../HIR/ObjectShape";
26 import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors";
27 import { assertExhaustive } from "../Utils/utils";
28
@@ -94,6 +98,16 @@ function equation(left: Type, right: Type): TypeEquation {
98 function* generate(
99 func: HIRFunction
100 ): Generator<TypeEquation, void, undefined> {
101 + if (func.env.fnType === "Component") {
102 + const [_, ref] = func.params;
103 + if (ref && ref.kind === "Identifier") {
104 + yield equation(ref.identifier.type, {
105 + kind: "Object",
106 + shapeId: BuiltInUseRefId,
107 + });
108 + }
109 + }
110 +
111 for (const [_, block] of func.body.blocks) {
112 for (const phi of block.phis) {
113 yield equation(phi.type, {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.expect.md new
+32
@@ -0,0 +1,32 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateRefAccessDuringRender:true
6 +function Foo(props, ref) {
7 + console.log(ref.current);
8 + return <div>{props.bar}</div>;
9 +}
10 +
11 +export const FIXTURE_ENTRYPOINT = {
12 + fn: Foo,
13 + params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
14 + isComponent: true,
15 +};
16 +
17 +```
18 +
19 +
20 +## Error
21 +
22 +```
23 + 1 | // @validateRefAccessDuringRender:true
24 + 2 | function Foo(props, ref) {
25 +> 3 | console.log(ref.current);
26 + | ^^^^^^^^^^^ [ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $16:TObject<BuiltInRefValue> (3:3)
27 + 4 | return <div>{props.bar}</div>;
28 + 5 | }
29 + 6 |
30 +```
31 +
32 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.js renamed
+2 -2
@@ -1,6 +1,6 @@
1 -// @validateRefAccessDuringRender: true
1 +// @validateRefAccessDuringRender:true
2 function Foo(props, ref) {
3 - ref.current = 2;
3 + console.log(ref.current);
4 return <div>{props.bar}</div>;
5 }
6
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ref-parameter-mutate-in-effect.expect.md
+11 -15
@@ -25,32 +25,28 @@ export const FIXTURE_ENTRYPOINT = {
25 import { useEffect, unstable_useMemoCache as useMemoCache } from "react";
26
27 function Foo(props, ref) {
28 - const $ = useMemoCache(5);
28 + const $ = useMemoCache(4);
29 let t0;
30 - if ($[0] !== ref.current) {
30 + let t1;
31 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32 t0 = () => {
33 ref.current = 2;
34 };
34 - $[0] = ref.current;
35 - $[1] = t0;
36 - } else {
37 - t0 = $[1];
38 - }
39 - let t1;
40 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
35 t1 = [];
42 - $[2] = t1;
36 + $[0] = t0;
37 + $[1] = t1;
38 } else {
44 - t1 = $[2];
39 + t0 = $[0];
40 + t1 = $[1];
41 }
42 useEffect(t0, t1);
43 let t2;
48 - if ($[3] !== props.bar) {
44 + if ($[2] !== props.bar) {
45 t2 = <div>{props.bar}</div>;
50 - $[3] = props.bar;
51 - $[4] = t2;
46 + $[2] = props.bar;
47 + $[3] = t2;
48 } else {
53 - t2 = $[4];
49 + t2 = $[3];
50 }
51 return t2;
52 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.expect.md deleted
-46
@@ -1,46 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateRefAccessDuringRender: true
6 -function Foo(props, ref) {
7 - ref.current = 2;
8 - return <div>{props.bar}</div>;
9 -}
10 -
11 -export const FIXTURE_ENTRYPOINT = {
12 - fn: Foo,
13 - params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
14 - isComponent: true,
15 -};
16 -
17 -```
18 -
19 -## Code
20 -
21 -```javascript
22 -import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender: true
23 -function Foo(props, ref) {
24 - const $ = useMemoCache(2);
25 - ref.current = 2;
26 - let t0;
27 - if ($[0] !== props.bar) {
28 - t0 = <div>{props.bar}</div>;
29 - $[0] = props.bar;
30 - $[1] = t0;
31 - } else {
32 - t0 = $[1];
33 - }
34 - return t0;
35 -}
36 -
37 -export const FIXTURE_ENTRYPOINT = {
38 - fn: Foo,
39 - params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
40 - isComponent: true,
41 -};
42 -
43 -```
44 -
45 -### Eval output
46 -(kind: ok) <div>foo</div>
\ No newline at end of file