@samitouri / QOS-React / commits / df84152780

[hir] Attach fnType to HIRFunction

--- (This came out of running a sync and observing hundreds of bailouts due from this validation) Reading `fnType` from environment overgeneralizes, as inner functions are usually not the type of the outer react function. ``` // Component type function Component() { // not Component type const helper = () => {...}; } ``` Let's attach fnType to `HIRFunction` and use that for our inference + validations

Mofei Zhang committed Mar 8, 2024 at 17:06 UTC df84152780880dfc159e9b8089e5e2e3ebac12aa
5 files changed +68 -2
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+1
@@ -210,6 +210,7 @@ export function lower(
210 return Ok({
211 id,
212 params,
213 + fnType: parent == null ? env.fnType : "Other",
214 returnType: null, // TODO: extract the actual return type node if present
215 body: builder.build(),
216 context,
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+2 -1
@@ -8,7 +8,7 @@
8 import * as t from "@babel/types";
9 import { CompilerError, CompilerErrorDetailOptions } from "../CompilerError";
10 import { assertExhaustive } from "../Utils/utils";
11 -import { Environment } from "./Environment";
11 +import { Environment, ReactFunctionType } from "./Environment";
12 import { HookKind } from "./ObjectShape";
13 import { Type } from "./Types";
14
@@ -240,6 +240,7 @@ export type ReactiveTryTerminal = {
240 export type HIRFunction = {
241 loc: SourceLocation;
242 id: string | null;
243 + fnType: ReactFunctionType;
244 env: Environment;
245 params: Array<Place | SpreadPattern>;
246 returnType: t.FlowType | t.TSType | null;
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+1 -1
@@ -137,7 +137,7 @@ export default function inferReferenceEffects(
137 reason: new Set([ValueReason.ReactiveFunctionArgument]),
138 };
139
140 - if (fn.env.fnType === "Component") {
140 + if (fn.fnType === "Component") {
141 CompilerError.invariant(fn.params.length <= 2, {
142 reason:
143 "Expected React component to have not more than two parameters: one for props and for ref",
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/component-inner-function-with-many-args.expect.md new
+53
@@ -0,0 +1,53 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { Stringify } from "shared-runtime";
6 +function Component(props) {
7 + const cb = (x, y, z) => x + y + z;
8 +
9 + return <Stringify cb={cb} id={props.id} />;
10 +}
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{ id: 0 }],
15 +};
16 +
17 +```
18 +
19 +## Code
20 +
21 +```javascript
22 +import { unstable_useMemoCache as useMemoCache } from "react";
23 +import { Stringify } from "shared-runtime";
24 +function Component(props) {
25 + const $ = useMemoCache(3);
26 + let t0;
27 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
28 + t0 = (x, y, z) => x + y + z;
29 + $[0] = t0;
30 + } else {
31 + t0 = $[0];
32 + }
33 + const cb = t0;
34 + let t1;
35 + if ($[1] !== props.id) {
36 + t1 = <Stringify cb={cb} id={props.id} />;
37 + $[1] = props.id;
38 + $[2] = t1;
39 + } else {
40 + t1 = $[2];
41 + }
42 + return t1;
43 +}
44 +
45 +export const FIXTURE_ENTRYPOINT = {
46 + fn: Component,
47 + params: [{ id: 0 }],
48 +};
49 +
50 +```
51 +
52 +### Eval output
53 +(kind: ok) <div>{"cb":"[[ function params=3 ]]","id":0}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/component-inner-function-with-many-args.tsx new
+11
@@ -0,0 +1,11 @@
1 +import { Stringify } from "shared-runtime";
2 +function Component(props) {
3 + const cb = (x, y, z) => x + y + z;
4 +
5 + return <Stringify cb={cb} id={props.id} />;
6 +}
7 +
8 +export const FIXTURE_ENTRYPOINT = {
9 + fn: Component,
10 + params: [{ id: 0 }],
11 +};