mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix block scoping issues from MergeConsecutiveBlocks
Fixes the issues from the previous PR. It's a simple fix — we don't merge consecutive blocks if the successor block is some terminal's fallthrough.
This commit is contained in:
@@ -14,7 +14,7 @@ import {
|
||||
Instruction,
|
||||
} from "./HIR";
|
||||
import { markPredecessors, removeUnreachableFallthroughs } from "./HIRBuilder";
|
||||
import { mapOptionalFallthroughs } from "./visitors";
|
||||
import { mapOptionalFallthroughs, terminalFallthrough } from "./visitors";
|
||||
|
||||
/*
|
||||
* Merges sequences of blocks that will always execute consecutively —
|
||||
@@ -30,7 +30,13 @@ import { mapOptionalFallthroughs } from "./visitors";
|
||||
*/
|
||||
export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
||||
const merged = new MergedBlocks();
|
||||
const fallthroughBlocks = new Set<BlockId>();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
const fallthrough = terminalFallthrough(block.terminal);
|
||||
if (fallthrough !== null) {
|
||||
fallthroughBlocks.add(fallthrough);
|
||||
}
|
||||
|
||||
for (const instr of block.instructions) {
|
||||
if (
|
||||
instr.value.kind === "FunctionExpression" ||
|
||||
@@ -40,11 +46,14 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Can only merge blocks with a single predecessor, can't merge
|
||||
* value blocks
|
||||
*/
|
||||
if (block.kind !== "block" || block.preds.size !== 1) {
|
||||
if (
|
||||
// Can only merge blocks with a single predecessor
|
||||
block.preds.size !== 1 ||
|
||||
// Value blocks cannot merge
|
||||
block.kind !== "block" ||
|
||||
// Merging across fallthroughs could move the predecessor out of its block scope
|
||||
fallthroughBlocks.has(block.id)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const originalPredecessorId = Array.from(block.preds)[0]!;
|
||||
|
||||
+8
-4
@@ -28,11 +28,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
```javascript
|
||||
function useHook(a, b) {
|
||||
switch (a) {
|
||||
bb1: switch (a) {
|
||||
case 1: {
|
||||
if (b == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(b);
|
||||
break bb1;
|
||||
}
|
||||
case 2: {
|
||||
return;
|
||||
@@ -41,8 +44,6 @@ function useHook(a, b) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(b);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
@@ -51,4 +52,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok)
|
||||
logs: ['foo']
|
||||
+16
-23
@@ -35,23 +35,21 @@ import { useMemo, unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(2);
|
||||
let t22;
|
||||
bb2: {
|
||||
let t0;
|
||||
if ($[0] !== props.value) {
|
||||
t0 = { value: props.value };
|
||||
$[0] = props.value;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
let t0;
|
||||
if ($[0] !== props.value) {
|
||||
t0 = { value: props.value };
|
||||
$[0] = props.value;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const handlers = t0;
|
||||
bb2: switch (props.test) {
|
||||
case true: {
|
||||
console.log(handlers.value);
|
||||
break bb2;
|
||||
}
|
||||
const handlers = t0;
|
||||
switch (props.test) {
|
||||
case true: {
|
||||
console.log(handlers.value);
|
||||
break bb2;
|
||||
}
|
||||
default: {
|
||||
}
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +66,5 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) handlers.foo is not a function
|
||||
logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
|
||||
'\n' +
|
||||
' at WrapperTestComponent (<project_root>/packages/sprout/dist/runner-evaluator.js:55:26)\n' +
|
||||
'\n' +
|
||||
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
|
||||
'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
|
||||
(kind: ok) {"value":"hello"}
|
||||
logs: ['hello']
|
||||
+2
-2
@@ -79,6 +79,8 @@ function Component(props) {
|
||||
x.push(42);
|
||||
t37 = x;
|
||||
break bb8;
|
||||
} else {
|
||||
console.log("fallthrough");
|
||||
}
|
||||
}
|
||||
$[0] = t37;
|
||||
@@ -88,8 +90,6 @@ function Component(props) {
|
||||
if (t37 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t37;
|
||||
}
|
||||
|
||||
console.log("fallthrough");
|
||||
let t0;
|
||||
if ($[1] !== props.a) {
|
||||
t0 = makeArray(props.a);
|
||||
|
||||
+2
-2
@@ -29,9 +29,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
function Component(props) {
|
||||
let t16;
|
||||
bb10: {
|
||||
bb5: {
|
||||
bb2: {
|
||||
if (props.cond) {
|
||||
break bb5;
|
||||
break bb2;
|
||||
}
|
||||
|
||||
t16 = props.a;
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
*/
|
||||
|
||||
const skipFilter = new Set([
|
||||
/**
|
||||
* Observable different in logging between Forget and non-Forget
|
||||
*/
|
||||
"early-return-no-declarations-reassignments-dependencies",
|
||||
|
||||
/**
|
||||
* Category A:
|
||||
* Tests with 0 parameters and 0 refs to external values
|
||||
@@ -517,9 +522,6 @@ const skipFilter = new Set([
|
||||
"bug-invalid-code-when-bailout",
|
||||
"component-syntax-ref-gating.flow",
|
||||
|
||||
"block-scoping-switch-dead-code",
|
||||
"block-scoping-switch-variable-scoping",
|
||||
|
||||
// 'react-forget-runtime' not yet supported
|
||||
"flag-enable-emit-hook-guards",
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user