Skip to content

Commit c3875eb

Browse files
committed
[compiler] Infer return types of function expressions
Uses the returnIdentifier added in the previous PR to provide a stable identifier for which we can infer a return type for functions, then wires up the equations in InferTypes to infer the type. ghstack-source-id: f2a4452 Pull Request resolved: #30785
1 parent af29925 commit c3875eb

8 files changed

+60
-47
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function printFunction(fn: HIRFunction): string {
7272
if (definition.length !== 0) {
7373
output.push(definition);
7474
}
75+
output.push(printType(fn.returnIdentifier.type));
7576
output.push(printHIR(fn.body));
7677
output.push(...fn.directives);
7778
return output.join('\n');
@@ -555,7 +556,8 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
555556
}
556557
})
557558
.join(', ') ?? '';
558-
value = `${kind} ${name} @deps[${deps}] @context[${context}] @effects[${effects}]:\n${fn}`;
559+
const type = printType(instrValue.loweredFunc.func.returnIdentifier.type).trim();
560+
value = `${kind} ${name} @deps[${deps}] @context[${context}] @effects[${effects}]${type!== '' ? ` return${type}` : ''}:\n${fn}`;
559561
break;
560562
}
561563
case 'TaggedTemplateExpression': {

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,20 @@ function canMergeScopes(
481481
}
482482

483483
function isAlwaysInvalidatingType(type: Type): boolean {
484-
if (type.kind === 'Object') {
485-
switch (type.shapeId) {
486-
case BuiltInArrayId:
487-
case BuiltInObjectId:
488-
case BuiltInFunctionId:
489-
case BuiltInJsxId: {
490-
return true;
484+
switch (type.kind) {
485+
case 'Object': {
486+
switch (type.shapeId) {
487+
case BuiltInArrayId:
488+
case BuiltInObjectId:
489+
case BuiltInFunctionId:
490+
case BuiltInJsxId: {
491+
return true;
492+
}
491493
}
494+
break;
495+
}
496+
case 'Function': {
497+
return true;
492498
}
493499
}
494500
return false;

compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ function apply(func: HIRFunction, unifier: Unifier): void {
8888
}
8989
}
9090
}
91+
func.returnIdentifier.type = unifier.get(func.returnIdentifier.type);
9192
}
9293

9394
type TypeEquation = {
@@ -122,6 +123,7 @@ function* generate(
122123
}
123124

124125
const names = new Map();
126+
const returnTypes: Array<Type> = [];
125127
for (const [_, block] of func.body.blocks) {
126128
for (const phi of block.phis) {
127129
yield equation(phi.type, {
@@ -133,6 +135,18 @@ function* generate(
133135
for (const instr of block.instructions) {
134136
yield* generateInstructionTypes(func.env, names, instr);
135137
}
138+
const terminal = block.terminal;
139+
if (terminal.kind === 'return') {
140+
returnTypes.push(terminal.value.identifier.type);
141+
}
142+
}
143+
if (returnTypes.length > 1) {
144+
yield equation(func.returnIdentifier.type, {
145+
kind: 'Phi',
146+
operands: returnTypes,
147+
});
148+
} else if (returnTypes.length === 1) {
149+
yield equation(func.returnIdentifier.type, returnTypes[0]!);
136150
}
137151
}
138152

@@ -346,7 +360,7 @@ function* generateInstructionTypes(
346360

347361
case 'FunctionExpression': {
348362
yield* generate(value.loweredFunc.func);
349-
yield equation(left, {kind: 'Object', shapeId: BuiltInFunctionId});
363+
yield equation(left, {kind: 'Function', shapeId: BuiltInFunctionId, return: value.loweredFunc.func.returnIdentifier.type});
350364
break;
351365
}
352366

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-missed-memoization-from-capture-in-invoked-function-inferred-as-mutation.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ component Component() {
1313
if (data != null) {
1414
return true;
1515
} else {
16-
return false;
16+
return {};
1717
}
1818
};
1919

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-missed-memoization-from-capture-in-invoked-function-inferred-as-mutation.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ component Component() {
99
if (data != null) {
1010
return true;
1111
} else {
12-
return false;
12+
return {};
1313
}
1414
};
1515

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-const-declaration.expect.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,17 @@ export const FIXTURE_ENTRYPOINT = {
2525
import { c as _c } from "react/compiler-runtime";
2626
function hoisting() {
2727
const $ = _c(1);
28-
let t0;
28+
let foo;
2929
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30-
const foo = () => bar + baz;
30+
foo = () => bar + baz;
3131

3232
const bar = 3;
3333
const baz = 2;
34-
t0 = foo();
35-
$[0] = t0;
34+
$[0] = foo;
3635
} else {
37-
t0 = $[0];
36+
foo = $[0];
3837
}
39-
return t0;
38+
return foo();
4039
}
4140

4241
export const FIXTURE_ENTRYPOINT = {

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,17 @@ export const FIXTURE_ENTRYPOINT = {
2525
import { c as _c } from "react/compiler-runtime";
2626
function hoisting() {
2727
const $ = _c(1);
28-
let t0;
28+
let foo;
2929
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30-
const foo = () => bar + baz;
30+
foo = () => bar + baz;
3131

3232
let bar = 3;
3333
let baz = 2;
34-
t0 = foo();
35-
$[0] = t0;
34+
$[0] = foo;
3635
} else {
37-
t0 = $[0];
36+
foo = $[0];
3837
}
39-
return t0;
38+
return foo();
4039
}
4140

4241
export const FIXTURE_ENTRYPOINT = {

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-nonescaping-invoked-callback-escaping-return.expect.md

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMe
4040
import { useCallback } from "react";
4141

4242
function Component(t0) {
43-
const $ = _c(11);
43+
const $ = _c(9);
4444
const { entity, children } = t0;
4545
let t1;
4646
if ($[0] !== entity) {
@@ -51,46 +51,39 @@ function Component(t0) {
5151
t1 = $[1];
5252
}
5353
const showMessage = t1;
54+
55+
const shouldShowMessage = showMessage();
5456
let t2;
55-
if ($[2] !== showMessage) {
56-
t2 = showMessage();
57-
$[2] = showMessage;
57+
if ($[2] !== shouldShowMessage) {
58+
t2 = <div>{shouldShowMessage}</div>;
59+
$[2] = shouldShowMessage;
5860
$[3] = t2;
5961
} else {
6062
t2 = $[3];
6163
}
62-
const shouldShowMessage = t2;
6364
let t3;
64-
if ($[4] !== shouldShowMessage) {
65-
t3 = <div>{shouldShowMessage}</div>;
66-
$[4] = shouldShowMessage;
65+
if ($[4] !== children) {
66+
t3 = <div>{children}</div>;
67+
$[4] = children;
6768
$[5] = t3;
6869
} else {
6970
t3 = $[5];
7071
}
7172
let t4;
72-
if ($[6] !== children) {
73-
t4 = <div>{children}</div>;
74-
$[6] = children;
75-
$[7] = t4;
76-
} else {
77-
t4 = $[7];
78-
}
79-
let t5;
80-
if ($[8] !== t3 || $[9] !== t4) {
81-
t5 = (
73+
if ($[6] !== t2 || $[7] !== t3) {
74+
t4 = (
8275
<div>
76+
{t2}
8377
{t3}
84-
{t4}
8578
</div>
8679
);
87-
$[8] = t3;
88-
$[9] = t4;
89-
$[10] = t5;
80+
$[6] = t2;
81+
$[7] = t3;
82+
$[8] = t4;
9083
} else {
91-
t5 = $[10];
84+
t4 = $[8];
9285
}
93-
return t5;
86+
return t4;
9487
}
9588

9689
export const FIXTURE_ENTRYPOINT = {

0 commit comments

Comments
 (0)