Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[compiler] Special-case phi inference for mixed readonly type #30797

Merged
merged 3 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
BuiltInArrayId,
BuiltInFunctionId,
BuiltInJsxId,
BuiltInMixedReadonlyId,
BuiltInObjectId,
BuiltInPropsId,
BuiltInRefValueId,
Expand Down Expand Up @@ -496,8 +497,13 @@ class Unifier {
if (candidateType === null) {
candidateType = resolved;
} else if (!typeEquals(resolved, candidateType)) {
candidateType = null;
break;
const unionType = tryUnionTypes(resolved, candidateType);
if (unionType === null) {
candidateType = null;
break;
} else {
candidateType = unionType;
}
} // else same type, continue
}

Expand Down Expand Up @@ -650,3 +656,39 @@ const RefLikeNameRE = /^(?:[a-zA-Z$_][a-zA-Z$_0-9]*)Ref$|^ref$/;
function isRefLikeName(t: PropType): boolean {
return RefLikeNameRE.test(t.objectName) && t.propertyName === 'current';
}

function tryUnionTypes(ty1: Type, ty2: Type): Type | null {
let readonlyType: Type;
let otherType: Type;
if (ty1.kind === 'Object' && ty1.shapeId === BuiltInMixedReadonlyId) {
readonlyType = ty1;
otherType = ty2;
} else if (ty2.kind === 'Object' && ty2.shapeId === BuiltInMixedReadonlyId) {
readonlyType = ty2;
otherType = ty1;
} else {
return null;
}
if (otherType.kind === 'Primitive') {
/**
* Union(Primitive | MixedReadonly) = MixedReadonly
*
* For example, `data ?? null` could return `data`, the fact that RHS
* is a primitive doesn't guarantee the result is a primitive.
*/
return readonlyType;
} else if (
otherType.kind === 'Object' &&
otherType.shapeId === BuiltInArrayId
) {
/**
* Union(Array | MixedReadonly) = Array
*
* In practice this pattern means the result is always an array. Given
* that this behavior requires opting-in to the mixedreadonly type
* (via moduleTypeProvider) this seems like a reasonable heuristic.
*/
return otherType;
}
return null;
}

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