Infer type of ref argument

Extend type inference to infer second argument of component as a ref type
This commit is contained in:
Sathya Gunasekaran
2024-02-29 14:47:52 -08:00
parent ad8f19675c
commit 7f994241b0
5 changed files with 60 additions and 64 deletions
@@ -18,7 +18,11 @@ import {
TypeId,
TypeVar,
} from "../HIR/HIR";
import { BuiltInArrayId, BuiltInObjectId } from "../HIR/ObjectShape";
import {
BuiltInArrayId,
BuiltInObjectId,
BuiltInUseRefId,
} from "../HIR/ObjectShape";
import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
@@ -94,6 +98,16 @@ function equation(left: Type, right: Type): TypeEquation {
function* generate(
func: HIRFunction
): Generator<TypeEquation, void, undefined> {
if (func.env.fnType === "Component") {
const [_, ref] = func.params;
if (ref && ref.kind === "Identifier") {
yield equation(ref.identifier.type, {
kind: "Object",
shapeId: BuiltInUseRefId,
});
}
}
for (const [_, block] of func.body.blocks) {
for (const phi of block.phis) {
yield equation(phi.type, {
@@ -0,0 +1,32 @@
## Input
```javascript
// @validateRefAccessDuringRender:true
function Foo(props, ref) {
console.log(ref.current);
return <div>{props.bar}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
isComponent: true,
};
```
## Error
```
1 | // @validateRefAccessDuringRender:true
2 | function Foo(props, ref) {
> 3 | console.log(ref.current);
| ^^^^^^^^^^^ [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)
4 | return <div>{props.bar}</div>;
5 | }
6 |
```
@@ -1,6 +1,6 @@
// @validateRefAccessDuringRender: true
// @validateRefAccessDuringRender:true
function Foo(props, ref) {
ref.current = 2;
console.log(ref.current);
return <div>{props.bar}</div>;
}
@@ -25,32 +25,28 @@ export const FIXTURE_ENTRYPOINT = {
import { useEffect, unstable_useMemoCache as useMemoCache } from "react";
function Foo(props, ref) {
const $ = useMemoCache(5);
const $ = useMemoCache(4);
let t0;
if ($[0] !== ref.current) {
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
ref.current = 2;
};
$[0] = ref.current;
$[1] = t0;
} else {
t0 = $[1];
}
let t1;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t1 = [];
$[2] = t1;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[2];
t0 = $[0];
t1 = $[1];
}
useEffect(t0, t1);
let t2;
if ($[3] !== props.bar) {
if ($[2] !== props.bar) {
t2 = <div>{props.bar}</div>;
$[3] = props.bar;
$[4] = t2;
$[2] = props.bar;
$[3] = t2;
} else {
t2 = $[4];
t2 = $[3];
}
return t2;
}
@@ -1,46 +0,0 @@
## Input
```javascript
// @validateRefAccessDuringRender: true
function Foo(props, ref) {
ref.current = 2;
return <div>{props.bar}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
isComponent: true,
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender: true
function Foo(props, ref) {
const $ = useMemoCache(2);
ref.current = 2;
let t0;
if ($[0] !== props.bar) {
t0 = <div>{props.bar}</div>;
$[0] = props.bar;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
isComponent: true,
};
```
### Eval output
(kind: ok) <div>foo</div>