@samitouri / QOS-React / commits / 0977115440

TryStatement: maybe-throw terminal for per-instruction throw points

Adds a "maybe-throw" terminal which represents the possibility that the block may or may not throw, and can either continue forward or exit to an exception handler (`catch`). Also updates HIRBuilder to track the current mode, and when inside a try block to wrap every instruction inside a basic block that ends in a maybe-throw. So far this code isn't used yet, so doesn't affect output.

Joe Savona committed Sep 7, 2023 at 14:22 UTC 0977115440b53ca505a62e793ab3ec44355f69aa
6 files changed +75 -1
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+10 -1
@@ -279,7 +279,8 @@ export type Terminal =
279 | TernaryTerminal
280 | OptionalTerminal
281 | LabelTerminal
282 - | SequenceTerminal;
282 + | SequenceTerminal
283 + | MaybeThrowTerminal;
284
285 function _staticInvariantTerminalHasLocation(
286 terminal: Terminal
@@ -444,6 +445,14 @@ export type SequenceTerminal = {
445 loc: SourceLocation;
446 };
447
448 +export type MaybeThrowTerminal = {
449 + kind: "maybe-throw";
450 + continuation: BlockId;
451 + handler: BlockId;
452 + id: InstructionId;
453 + loc: SourceLocation;
454 +};
455 +
456 /**
457 * Instructions generally represent expressions but with all nesting flattened away,
458 * such that all operands to each instruction are either primitive values OR are
compiler/packages/babel-plugin-react-forget/src/HIR/HIRBuilder.ts
+31
@@ -77,6 +77,17 @@ export type Bindings = Map<
77 { node: t.Identifier; identifier: Identifier }
78 >;
79
80 +// Determines how instructions should be constructed in order to preserve
81 +// exception semantics
82 +type ExceptionsMode =
83 + // Mode used for code not covered by explicit exception handling, any
84 + // errors are assumed to be thrown out of the function
85 + | { kind: "ThrowExceptions" }
86 + // Mode used for code that *is* covered by explicit exception handling
87 + // (ie try/catch), which requires modeling the possibility of control
88 + // flow to the exception handler.
89 + | { kind: "CatchExceptions"; handler: BlockId };
90 +
91 /**
92 * Helper class for constructing a CFG
93 */
@@ -88,6 +99,7 @@ export default class HIRBuilder {
99 #context: t.Identifier[];
100 #bindings: Bindings;
101 #env: Environment;
102 + #mode: ExceptionsMode = { kind: "ThrowExceptions" };
103 parentFunction: NodePath<t.Function>;
104 errors: CompilerError = new CompilerError();
105
@@ -130,6 +142,20 @@ export default class HIRBuilder {
142 */
143 push(instruction: Instruction): void {
144 this.#current.instructions.push(instruction);
145 + if (this.#mode.kind === "CatchExceptions") {
146 + const handler = this.#mode.handler;
147 + const continuationBlock = this.reserve(this.currentBlockKind());
148 + this.terminateWithContinuation(
149 + {
150 + kind: "maybe-throw",
151 + continuation: continuationBlock.id,
152 + handler,
153 + id: makeInstructionId(0),
154 + loc: instruction.loc,
155 + },
156 + continuationBlock
157 + );
158 + }
159 }
160
161 makeTemporary(): Identifier {
@@ -723,6 +749,11 @@ export function reversePostorderBlocks(func: HIR): void {
749 visit(terminal.block);
750 break;
751 }
752 + case "maybe-throw": {
753 + visit(terminal.handler);
754 + visit(terminal.continuation);
755 + break;
756 + }
757 case "unsupported": {
758 break;
759 }
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+5
@@ -80,6 +80,7 @@ export function printMixedHIR(
80 return printInstruction(value);
81 }
82 switch (value.kind) {
83 + case "maybe-throw":
84 case "sequence":
85 case "label":
86 case "optional":
@@ -230,6 +231,10 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
231 value = `Unsupported`;
232 break;
233 }
234 + case "maybe-throw": {
235 + value = `MaybeThrow continuation=bb${terminal.continuation} handler=bb${terminal.handler}`;
236 + break;
237 + }
238 default: {
239 assertExhaustive(
240 terminal,
compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts
+20
@@ -731,6 +731,17 @@ export function mapTerminalSuccessors(
731 loc: terminal.loc,
732 };
733 }
734 + case "maybe-throw": {
735 + const continuation = fn(terminal.continuation);
736 + const handler = fn(terminal.handler);
737 + return {
738 + kind: "maybe-throw",
739 + continuation,
740 + handler,
741 + id: makeInstructionId(0),
742 + loc: terminal.loc,
743 + };
744 + }
745 case "unsupported": {
746 return terminal;
747 }
@@ -750,6 +761,7 @@ export function mapTerminalSuccessors(
761 */
762 export function terminalFallthrough(terminal: Terminal): BlockId | null {
763 switch (terminal.kind) {
764 + case "maybe-throw":
765 case "branch":
766 case "goto":
767 case "return":
@@ -784,6 +796,7 @@ export function mapOptionalFallthroughs(
796 fn: (block: BlockId) => BlockId | null
797 ): void {
798 switch (terminal.kind) {
799 + case "maybe-throw":
800 case "branch":
801 case "goto":
802 case "return":
@@ -917,6 +930,11 @@ export function* eachTerminalSuccessor(terminal: Terminal): Iterable<BlockId> {
930 yield terminal.block;
931 break;
932 }
933 + case "maybe-throw": {
934 + yield terminal.continuation;
935 + yield terminal.handler;
936 + break;
937 + }
938 case "unsupported":
939 break;
940 default: {
@@ -956,6 +974,7 @@ export function mapTerminalOperands(
974 terminal.value = fn(terminal.value);
975 break;
976 }
977 + case "maybe-throw":
978 case "sequence":
979 case "label":
980 case "optional":
@@ -1004,6 +1023,7 @@ export function* eachTerminalOperand(terminal: Terminal): Iterable<Place> {
1023 yield terminal.value;
1024 break;
1025 }
1026 + case "maybe-throw":
1027 case "sequence":
1028 case "label":
1029 case "optional":
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/BuildReactiveFunction.ts
+7
@@ -636,6 +636,13 @@ class Driver {
636 }
637 break;
638 }
639 + case "maybe-throw": {
640 + this.visitBlock(
641 + this.cx.ir.blocks.get(terminal.continuation)!,
642 + blockValue
643 + );
644 + break;
645 + }
646 case "unsupported": {
647 CompilerError.invariant(false, {
648 reason: "Unexpected unsupported terminal",
compiler/packages/babel-plugin-react-forget/src/Utils/VisualizeHIRMermaid.ts
+2
@@ -139,6 +139,7 @@ function printTerminalLabel(terminal: Terminal): string {
139 buffer.push("Sequence");
140 break;
141 }
142 + case "maybe-throw":
143 case "unsupported": {
144 break;
145 }
@@ -246,6 +247,7 @@ function printTerminalArrows(blockId: BlockId, terminal: Terminal): string {
247 buffer.push(printJumpArrow(blockId, terminal.fallthrough, "fallthrough"));
248 break;
249 }
250 + case "maybe-throw":
251 case "throw":
252 case "return":
253 case "unsupported": {