Skip to content

Commit

Permalink
[compiler] Special-case phi inference for mixed readonly type
Browse files Browse the repository at this point in the history
This allows us to handle common operations such as `useFragment(...).edges.nodes ?? []` where we have a `Phi(MixedReadonly, Array)`. The underlying pattern remains general-purpose and not Relay-specific, and any API that returns transitively "mixed" data (primitives, arrays, plain objects) can benefit from the same type refinement.

ghstack-source-id: d99442d5c5bb4d1a2b7b5e0269c2d6fad0ed0c74
Pull Request resolved: #30797
  • Loading branch information
josephsavona committed Aug 23, 2024
1 parent 1e23e9d commit 52f7e42
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import {
Identifier,
IdentifierId,
Instruction,
isArrayType,
makeType,
ObjectType,
PropType,
Type,
typeEquals,
Expand All @@ -25,6 +27,7 @@ import {
BuiltInArrayId,
BuiltInFunctionId,
BuiltInJsxId,
BuiltInMixedReadonlyId,
BuiltInObjectId,
BuiltInPropsId,
BuiltInRefValueId,
Expand Down Expand Up @@ -496,8 +499,23 @@ class Unifier {
if (candidateType === null) {
candidateType = resolved;
} else if (!typeEquals(resolved, candidateType)) {
candidateType = null;
break;
function isArrayOrMixedReadonly(ty: Type): ty is ObjectType {
return (
ty.kind === 'Object' &&
(ty.shapeId === BuiltInArrayId ||
ty.shapeId === BuiltInMixedReadonlyId)
);
}
if (
isArrayOrMixedReadonly(resolved) &&
isArrayOrMixedReadonly(candidateType)
) {
candidateType =
resolved.shapeId === BuiltInArrayId ? resolved : candidateType;
} else {
candidateType = null;
break;
}
} // else same type, continue
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@

## Input

```javascript
// @flow @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
import {useFragment} from 'shared-runtime';

function Component() {
const data = useFragment();
const nodes = data.nodes ?? [];
const flatMap = nodes.flatMap(node => node.items);
const filtered = flatMap.filter(item => item != null);
const map = useMemo(() => filtered.map(), [filtered]);
const index = filtered.findIndex(x => x === null);

return (
<div>
{map}
{index}
</div>
);
}

```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useMemo } from "react";
import { useFragment } from "shared-runtime";

function Component() {
const $ = _c(11);
const data = useFragment();
let t0;
if ($[0] !== data.nodes) {
t0 = data.nodes ?? [];
$[0] = data.nodes;
$[1] = t0;
} else {
t0 = $[1];
}
const nodes = t0;
let t1;
if ($[2] !== nodes) {
t1 = nodes.flatMap(_temp);
$[2] = nodes;
$[3] = t1;
} else {
t1 = $[3];
}
const flatMap = t1;
let t2;
if ($[4] !== flatMap) {
t2 = flatMap.filter(_temp2);
$[4] = flatMap;
$[5] = t2;
} else {
t2 = $[5];
}
const filtered = t2;
let t3;
let t4;
if ($[6] !== filtered) {
t4 = filtered.map();
$[6] = filtered;
$[7] = t4;
} else {
t4 = $[7];
}
t3 = t4;
const map = t3;
const index = filtered.findIndex(_temp3);
let t5;
if ($[8] !== map || $[9] !== index) {
t5 = (
<div>
{map}
{index}
</div>
);
$[8] = map;
$[9] = index;
$[10] = t5;
} else {
t5 = $[10];
}
return t5;
}
function _temp3(x) {
return x === null;
}
function _temp2(item) {
return item != null;
}
function _temp(node) {
return node.items;
}

```
### Eval output
(kind: exception) Fixture not implemented

0 comments on commit 52f7e42

Please sign in to comment.