Todo for "unreachable" possibly-hoisted functions
Adds a todo in HIRBuilder, before we prune unreachable code we check if there were any function expressions. Realistically that's only going to occur for hoisted functions, so this lets us target a todo rather than hit an invariant.
Joe Savona committed
Mar 8, 2024 at 13:59 UTC
052f5fe973e2d5c08293509063163b0b63a6d948
3 files changed
+40
-10
compiler/packages/babel-plugin-react-forget/src/HIR/HIRBuilder.ts
+32
-4
@@ -310,7 +310,24 @@ export default class HIRBuilder {
310
blocks: this.#completed,
311
entry: this.#entry,
312
};
313
- reversePostorderBlocks(ir);
313
+ const rpoBlocks = getReversePostorderedBlocks(ir);
314
+ for (const [id, block] of ir.blocks) {
315
+ if (
316
+ !rpoBlocks.has(id) &&
317
+ block.instructions.some(
318
+ (instr) => instr.value.kind === "FunctionExpression"
319
+ )
320
+ ) {
321
+ CompilerError.throwTodo({
322
+ reason: `Support functions with unreachable code that may contain hoisted declarations`,
323
+ loc: block.instructions[0]?.loc ?? block.terminal.loc,
324
+ description: null,
325
+ suggestions: null,
326
+ });
327
+ }
328
+ }
329
+ ir.blocks = rpoBlocks;
330
+
331
removeUnreachableForUpdates(ir);
332
removeUnreachableFallthroughs(ir);
333
removeDeadDoWhileStatements(ir);
@@ -654,9 +671,21 @@ export function removeDeadDoWhileStatements(func: HIR): void {
671
672
/*
673
* Converts the graph to reverse-postorder, with predecessor blocks appearing
657
- * before successors except in the case of back links (ie loops).
674
+ * before successors except in the case of back edges (ie loops).
675
*/
676
export function reversePostorderBlocks(func: HIR): void {
677
+ const rpoBlocks = getReversePostorderedBlocks(func);
678
+ func.blocks = rpoBlocks;
679
+}
680
+
681
+/**
682
+ * Returns a mapping of BlockId => BasicBlock where the insertion order of the map
683
+ * has blocks in reverse-postorder, with predecessor blocks appearing before successors
684
+ * except in the case of back edges (ie loops). Note that not all blocks in the input
685
+ * may be in the output: blocks will be removed in the case of unreachable code in
686
+ * the input.
687
+ */
688
+function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
689
const visited: Set<BlockId> = new Set();
690
const postorder: Array<BlockId> = [];
691
function visit(blockId: BlockId): void {
@@ -781,8 +810,7 @@ export function reversePostorderBlocks(func: HIR): void {
810
for (const blockId of postorder.reverse()) {
811
blocks.set(blockId, func.blocks.get(blockId)!);
812
}
784
-
785
- func.blocks = blocks;
813
+ return blocks;
814
}
815
816
export function markInstructionIds(func: HIR): void {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-hoisted-function-in-unreachable-code.expect.md
+7
-6
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+// @compilationMode(infer)
6
function Component() {
7
return <Foo />;
8
@@ -15,12 +16,12 @@ function Component() {
16
## Error
17
18
```
18
- 1 | function Component() {
19
-> 2 | return <Foo />;
20
- | ^^^ [ReactForget] Invariant: [hoisting] Expected value for identifier to be initialized. Foo$0 (2:2)
21
- 3 |
22
- 4 | // This is unreachable from a control-flow perspective, but it gets hoisted
23
- 5 | function Foo() {}
19
+ 4 |
20
+ 5 | // This is unreachable from a control-flow perspective, but it gets hoisted
21
+> 6 | function Foo() {}
22
+ | ^^^^^^^^^^^^^^^^^ [ReactForget] Todo: Support functions with unreachable code that may contain hoisted declarations (6:6)
23
+ 7 | }
24
+ 8 |
25
```
26
27
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-hoisted-function-in-unreachable-code.js
+1
@@ -1,3 +1,4 @@
1
+// @compilationMode(infer)
2
function Component() {
3
return <Foo />;
4