@samitouri / QOS-React-2 / commits / 052f5fe973

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