Fix/review bug repros, mark fixed

I reviewed the test cases we have marked as bugs ("_bug.*") and realized that 
several of them are already fixed — woohoo! Then one wasn't fixed _yet_: our 
type inference loses track of refs if you stash them inside an object/array. But 
that's why I added the ValidateNoRefAccessInRender pass, which i've updated to 
detect and reject these invalid cases. There are now only a few bugs left (more 
fixes coming).
This commit is contained in:
Joe Savona
2023-06-04 11:08:54 -07:00
parent 6d6518161a
commit 2ae3592d29
12 changed files with 42 additions and 66 deletions
@@ -40,6 +40,15 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
for (const [, block] of fn.body.blocks) {
for (const instr of block.instructions) {
switch (instr.value.kind) {
case "PropertyLoad":
case "LoadLocal":
case "StoreLocal":
case "Destructure": {
// These instructions are necessary for storing the results of a useRef into
// a variable and referencing them in functions. We can propagate type info
// for these instructions so they ensure we have a complete analysis.
break;
}
case "FunctionExpression": {
// For now we assume *all* function expressions are safe, eventually we can
// be more precise and disallow ref access in functions that may be called
@@ -57,6 +66,7 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
default: {
for (const operand of eachInstructionValueOperand(instr.value)) {
validateNonRefValue(error, operand);
validateNonRefObject(error, operand);
}
}
}
@@ -1,61 +0,0 @@
## Input
```javascript
function Foo({ a }) {
const ref = useRef();
// type information is lost here as we don't track types of fields
const val = { ref };
// without type info, we don't know that val.ref.current is a ref value so we
// end up depending on val.ref.current
const x = { a, val: val.ref.current };
return <VideoList videos={x} />;
}
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Foo(t23) {
const $ = useMemoCache(7);
const { a } = t23;
const ref = useRef();
const c_0 = $[0] !== ref;
let t0;
if (c_0) {
t0 = { ref };
$[0] = ref;
$[1] = t0;
} else {
t0 = $[1];
}
const val = t0;
const c_2 = $[2] !== a;
const c_3 = $[3] !== val.ref.current;
let t1;
if (c_2 || c_3) {
t1 = { a, val: val.ref.current };
$[2] = a;
$[3] = val.ref.current;
$[4] = t1;
} else {
t1 = $[4];
}
const x = t1;
const c_5 = $[5] !== x;
let t2;
if (c_5) {
t2 = <VideoList videos={x} />;
$[5] = x;
$[6] = t2;
} else {
t2 = $[6];
}
return t2;
}
```
@@ -15,10 +15,6 @@ function Component(props) {
## Error
```
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> $20:TObject<BuiltInRefValue> (4:4)
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> value$21:TObject<BuiltInRefValue> (5:5)
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> $23:TObject<BuiltInRefValue> (5:5)
```
@@ -14,6 +14,8 @@ function Component(props) {
## Error
```
[ReactForget] InvalidInput: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at <unknown> $22:TObject<BuiltInUseRefId> (3:3)
[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at <unknown> $25:TObject<BuiltInRefValue> (4:4)
```
@@ -0,0 +1,27 @@
## Input
```javascript
function Foo({ a }) {
const ref = useRef();
// type information is lost here as we don't track types of fields
const val = { ref };
// without type info, we don't know that val.ref.current is a ref value so we
// *would* end up depending on val.ref.current
// however, this is an instance of accessing a ref during render and is disallowed
// under React's rules, so we reject this input
const x = { a, val: val.ref.current };
return <VideoList videos={x} />;
}
```
## Error
```
[ReactForget] InvalidInput: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at <unknown> $30:TObject<BuiltInUseRefId> (4:4)
```
@@ -3,7 +3,9 @@ function Foo({ a }) {
// type information is lost here as we don't track types of fields
const val = { ref };
// without type info, we don't know that val.ref.current is a ref value so we
// end up depending on val.ref.current
// *would* end up depending on val.ref.current
// however, this is an instance of accessing a ref during render and is disallowed
// under React's rules, so we reject this input
const x = { a, val: val.ref.current };
return <VideoList videos={x} />;