[compiler] Fix issue with macro arguments being outlined
Summary: Fixes issue documented by #30435. We change the pipeline order so that outlining comes after tracking macro operands, and any function that is referenced in a macro will now not be outlined. ghstack-source-id: f731ad65c8b84db3fc5f3a2ff3a6986112765963 Pull Request resolved: https://github.com/facebook/react/pull/30587
Mike Vitousek committed
Aug 2, 2024 at 14:55 UTC
3af905d95448d582cbd62fe6d41bd976ce9787ea
7 files changed
+89
-43
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+9
-9
@@ -56,7 +56,7 @@ import {
56
flattenReactiveLoops,
57
flattenScopesWithHooksOrUse,
58
inferReactiveScopeVariables,
59
- memoizeFbtOperandsInSameScope,
59
+ memoizeFbtAndMacroOperandsInSameScope,
60
mergeOverlappingReactiveScopes,
61
mergeReactiveScopesThatInvalidateTogether,
62
promoteUsedTemporaries,
@@ -243,8 +243,15 @@ function* runWithEnvironment(
243
inferReactiveScopeVariables(hir);
244
yield log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
245
246
+ const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
247
+ yield log({
248
+ kind: 'hir',
249
+ name: 'MemoizeFbtAndMacroOperandsInSameScope',
250
+ value: hir,
251
+ });
252
+
253
if (env.config.enableFunctionOutlining) {
247
- outlineFunctions(hir);
254
+ outlineFunctions(hir, fbtOperands);
255
yield log({kind: 'hir', name: 'OutlineFunctions', value: hir});
256
}
257
@@ -262,13 +269,6 @@ function* runWithEnvironment(
269
value: hir,
270
});
271
265
- const fbtOperands = memoizeFbtOperandsInSameScope(hir);
266
- yield log({
267
- kind: 'hir',
268
- name: 'MemoizeFbtAndMacroOperandsInSameScope',
269
- value: hir,
270
- });
271
-
272
if (env.config.enableReactiveScopesInHIR) {
273
pruneUnusedLabelsHIR(hir);
274
yield log({
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+5
@@ -485,6 +485,11 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
485
continue;
486
}
487
488
+ if (key === 'customMacros' && val) {
489
+ maybeConfig[key] = [val];
490
+ continue;
491
+ }
492
+
493
if (typeof defaultConfig[key as keyof EnvironmentConfig] !== 'boolean') {
494
// skip parsing non-boolean properties
495
continue;
compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts
+9
-6
@@ -5,27 +5,30 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {HIRFunction} from '../HIR';
8
+import {HIRFunction, IdentifierId} from '../HIR';
9
10
-export function outlineFunctions(fn: HIRFunction): void {
10
+export function outlineFunctions(
11
+ fn: HIRFunction,
12
+ fbtOperands: Set<IdentifierId>,
13
+): void {
14
for (const [, block] of fn.body.blocks) {
15
for (const instr of block.instructions) {
13
- const {value} = instr;
16
+ const {value, lvalue} = instr;
17
18
if (
19
value.kind === 'FunctionExpression' ||
20
value.kind === 'ObjectMethod'
21
) {
22
// Recurse in case there are inner functions which can be outlined
20
- outlineFunctions(value.loweredFunc.func);
23
+ outlineFunctions(value.loweredFunc.func, fbtOperands);
24
}
22
-
25
if (
26
value.kind === 'FunctionExpression' &&
27
value.loweredFunc.dependencies.length === 0 &&
28
value.loweredFunc.func.context.length === 0 &&
29
// TODO: handle outlining named functions
28
- value.loweredFunc.func.id === null
30
+ value.loweredFunc.func.id === null &&
31
+ !fbtOperands.has(lvalue.identifier.id)
32
) {
33
const loweredFunc = value.loweredFunc.func;
34
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/index.ts
+1
-1
@@ -16,7 +16,7 @@ export {extractScopeDeclarationsFromDestructuring} from './ExtractScopeDeclarati
16
export {flattenReactiveLoops} from './FlattenReactiveLoops';
17
export {flattenScopesWithHooksOrUse} from './FlattenScopesWithHooksOrUse';
18
export {inferReactiveScopeVariables} from './InferReactiveScopeVariables';
19
-export {memoizeFbtAndMacroOperandsInSameScope as memoizeFbtOperandsInSameScope} from './MemoizeFbtAndMacroOperandsInSameScope';
19
+export {memoizeFbtAndMacroOperandsInSameScope} from './MemoizeFbtAndMacroOperandsInSameScope';
20
export {mergeOverlappingReactiveScopes} from './MergeOverlappingReactiveScopes';
21
export {mergeReactiveScopesThatInvalidateTogether} from './MergeReactiveScopesThatInvalidateTogether';
22
export {printReactiveFunction} from './PrintReactiveFunction';
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.idx-outlining.expect.md
deleted
-27
@@ -1,27 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import idx from 'idx';
6
-
7
-function Component(props) {
8
- // the lambda should not be outlined
9
- const groupName = idx(props, _ => _.group.label);
10
- return <div>{groupName}</div>;
11
-}
12
-
13
-export const FIXTURE_ENTRYPOINT = {
14
- fn: Component,
15
- params: [{}],
16
-};
17
-
18
-```
19
-
20
-
21
-## Error
22
-
23
-```
24
-The second argument supplied to `idx` must be an arrow function. (This is an error on an internal node. Probably an internal error.)
25
-```
26
-
27
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/idx-no-outlining.expect.md
new
+64
@@ -0,0 +1,64 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @customMacros(idx)
6
+import idx from 'idx';
7
+
8
+function Component(props) {
9
+ // the lambda should not be outlined
10
+ const groupName = idx(props, _ => _.group.label);
11
+ return <div>{groupName}</div>;
12
+}
13
+
14
+export const FIXTURE_ENTRYPOINT = {
15
+ fn: Component,
16
+ params: [{}],
17
+};
18
+
19
+```
20
+
21
+## Code
22
+
23
+```javascript
24
+import { c as _c } from "react/compiler-runtime"; // @customMacros(idx)
25
+
26
+function Component(props) {
27
+ var _ref2;
28
+ const $ = _c(4);
29
+ let t0;
30
+ if ($[0] !== props) {
31
+ var _ref;
32
+
33
+ t0 =
34
+ (_ref = props) != null
35
+ ? (_ref = _ref.group) != null
36
+ ? _ref.label
37
+ : _ref
38
+ : _ref;
39
+ $[0] = props;
40
+ $[1] = t0;
41
+ } else {
42
+ t0 = $[1];
43
+ }
44
+ const groupName = t0;
45
+ let t1;
46
+ if ($[2] !== groupName) {
47
+ t1 = <div>{groupName}</div>;
48
+ $[2] = groupName;
49
+ $[3] = t1;
50
+ } else {
51
+ t1 = $[3];
52
+ }
53
+ return t1;
54
+}
55
+
56
+export const FIXTURE_ENTRYPOINT = {
57
+ fn: Component,
58
+ params: [{}],
59
+};
60
+
61
+```
62
+
63
+### Eval output
64
+(kind: ok) <div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/idx-no-outlining.js
renamed
+1
@@ -1,3 +1,4 @@
1
+// @customMacros(idx)
2
import idx from 'idx';
3
4
function Component(props) {