@samitouri / QOS-React-2 / commits / 1c3313707f

ReactiveTerminal: add loc and id to all variants

Joe Savona committed Apr 1, 2024 at 15:03 UTC 1c3313707f78caadd72869eca481f418b68b7f15
3 files changed +99 -88
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+28 -1
@@ -156,28 +156,46 @@ export type ReactiveTerminal =
156 | ReactiveLabelTerminal
157 | ReactiveTryTerminal;
158
159 +function _staticInvariantReactiveTerminalHasLocation(
160 + terminal: ReactiveTerminal
161 +): SourceLocation {
162 + // If this fails, it is because a variant of ReactiveTerminal is missing a .loc - add it!
163 + return terminal.loc;
164 +}
165 +
166 +function _staticInvariantReactiveTerminalHasInstructionId(
167 + terminal: ReactiveTerminal
168 +): InstructionId {
169 + // If this fails, it is because a variant of ReactiveTerminal is missing a .id - add it!
170 + return terminal.id;
171 +}
172 +
173 export type ReactiveTerminalTargetKind = "implicit" | "labeled" | "unlabeled";
174 export type ReactiveBreakTerminal = {
175 kind: "break";
176 target: BlockId;
163 - id: InstructionId | null;
177 + id: InstructionId;
178 targetKind: ReactiveTerminalTargetKind;
179 + loc: SourceLocation;
180 };
181 export type ReactiveContinueTerminal = {
182 kind: "continue";
183 target: BlockId;
184 id: InstructionId;
185 targetKind: ReactiveTerminalTargetKind;
186 + loc: SourceLocation;
187 };
188 export type ReactiveReturnTerminal = {
189 kind: "return";
190 value: Place;
191 id: InstructionId;
192 + loc: SourceLocation;
193 };
194 export type ReactiveThrowTerminal = {
195 kind: "throw";
196 value: Place;
197 id: InstructionId;
198 + loc: SourceLocation;
199 };
200 export type ReactiveSwitchTerminal = {
201 kind: "switch";
@@ -187,18 +205,21 @@ export type ReactiveSwitchTerminal = {
205 block: ReactiveBlock | void;
206 }>;
207 id: InstructionId;
208 + loc: SourceLocation;
209 };
210 export type ReactiveDoWhileTerminal = {
211 kind: "do-while";
212 loop: ReactiveBlock;
213 test: ReactiveValue;
214 id: InstructionId;
215 + loc: SourceLocation;
216 };
217 export type ReactiveWhileTerminal = {
218 kind: "while";
219 test: ReactiveValue;
220 loop: ReactiveBlock;
221 id: InstructionId;
222 + loc: SourceLocation;
223 };
224 export type ReactiveForTerminal = {
225 kind: "for";
@@ -207,18 +228,21 @@ export type ReactiveForTerminal = {
228 update: ReactiveValue | null;
229 loop: ReactiveBlock;
230 id: InstructionId;
231 + loc: SourceLocation;
232 };
233 export type ReactiveForOfTerminal = {
234 kind: "for-of";
235 init: ReactiveValue;
236 loop: ReactiveBlock;
237 id: InstructionId;
238 + loc: SourceLocation;
239 };
240 export type ReactiveForInTerminal = {
241 kind: "for-in";
242 init: ReactiveValue;
243 loop: ReactiveBlock;
244 id: InstructionId;
245 + loc: SourceLocation;
246 };
247 export type ReactiveIfTerminal = {
248 kind: "if";
@@ -226,11 +250,13 @@ export type ReactiveIfTerminal = {
250 consequent: ReactiveBlock;
251 alternate: ReactiveBlock | null;
252 id: InstructionId;
253 + loc: SourceLocation;
254 };
255 export type ReactiveLabelTerminal = {
256 kind: "label";
257 block: ReactiveBlock;
258 id: InstructionId;
259 + loc: SourceLocation;
260 };
261 export type ReactiveTryTerminal = {
262 kind: "try";
@@ -238,6 +264,7 @@ export type ReactiveTryTerminal = {
264 handlerBinding: Place | null;
265 handler: ReactiveBlock;
266 id: InstructionId;
267 + loc: SourceLocation;
268 };
269
270 // A function lowered to HIR form, ie where its body is lowered to an HIR control-flow graph
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/BuildReactiveFunction.ts
+68 -87
@@ -88,7 +88,7 @@ class Driver {
88 kind: "terminal",
89 terminal: {
90 kind: "return",
91 - // loc: terminal.loc,
91 + loc: terminal.loc,
92 value: terminal.value,
93 id: terminal.id,
94 },
@@ -101,6 +101,7 @@ class Driver {
101 kind: "terminal",
102 terminal: {
103 kind: "throw",
104 + loc: terminal.loc,
105 value: terminal.value,
106 id: terminal.id,
107 },
@@ -126,10 +127,10 @@ class Driver {
127
128 let consequent: ReactiveBlock | null = null;
129 if (this.cx.isScheduled(terminal.consequent)) {
129 - const break_ = this.visitBreak(terminal.consequent, null);
130 - if (break_ !== null) {
131 - consequent = [break_];
132 - }
130 + CompilerError.invariant(false, {
131 + reason: `Unexpected 'if' where the consequent is already scheduled`,
132 + loc: terminal.loc,
133 + });
134 } else {
135 consequent = this.traverseBlock(
136 this.cx.ir.blocks.get(terminal.consequent)!
@@ -139,10 +140,10 @@ class Driver {
140 let alternate: ReactiveBlock | null = null;
141 if (alternateId !== null) {
142 if (this.cx.isScheduled(alternateId)) {
142 - const break_ = this.visitBreak(alternateId, null);
143 - if (break_ !== null) {
144 - alternate = [break_];
145 - }
143 + CompilerError.invariant(false, {
144 + reason: `Unexpected 'if' where the alternate is already scheduled`,
145 + loc: terminal.loc,
146 + });
147 } else {
148 alternate = this.traverseBlock(this.cx.ir.blocks.get(alternateId)!);
149 }
@@ -153,6 +154,7 @@ class Driver {
154 kind: "terminal",
155 terminal: {
156 kind: "if",
157 + loc: terminal.loc,
158 test: terminal.test,
159 consequent: consequent ?? this.emptyBlock(),
160 alternate: alternate,
@@ -186,36 +188,16 @@ class Driver {
188 test: Place | null;
189 block: ReactiveBlock;
190 }> = [];
189 - [...terminal.cases].reverse().forEach((case_, index) => {
191 + [...terminal.cases].reverse().forEach((case_, _index) => {
192 const test = case_.test;
193
194 let consequent: ReactiveBlock;
195 if (this.cx.isScheduled(case_.block)) {
194 - /*
195 - * cases which are empty or contain only a `break` may point to blocks
196 - * that are already scheduled. emit as follows:
197 - * - if the block is for another case branch, don't emit a break and fall-through
198 - * - else, emit an explicit break.
199 - */
200 - const break_ = this.visitBreak(case_.block, null);
201 - if (
202 - index === 0 &&
203 - break_.terminal.targetKind === "implicit" &&
204 - case_.block === terminal.fallthrough &&
205 - case_.test === null
206 - ) {
207 - /*
208 - * If the last case statement (first in reverse order) is a default that
209 - * jumps to the fallthrough, then we would emit a useless `default: {}`,
210 - * so instead skip this case.
211 - */
212 - return;
213 - }
214 - const block = [];
215 - if (break_ !== null) {
216 - block.push(break_);
217 - }
218 - consequent = block;
196 + CompilerError.invariant(case_.block === terminal.fallthrough, {
197 + reason: `Unexpected 'switch' where a case is already scheduled and block is not the fallthrough`,
198 + loc: terminal.loc,
199 + });
200 + return;
201 } else {
202 consequent = this.traverseBlock(
203 this.cx.ir.blocks.get(case_.block)!
@@ -232,6 +214,7 @@ class Driver {
214 kind: "terminal",
215 terminal: {
216 kind: "switch",
217 + loc: terminal.loc,
218 test: terminal.test,
219 cases,
220 id: terminal.id,
@@ -269,14 +252,10 @@ class Driver {
252 if (loopId) {
253 loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
254 } else {
272 - const break_ = this.visitBreak(terminal.loop, null);
273 - CompilerError.invariant(break_ !== null, {
274 - reason: "If loop body is already scheduled it must be a break",
275 - description: null,
276 - loc: null,
277 - suggestions: null,
255 + CompilerError.invariant(false, {
256 + reason: `Unexpected 'do-while' where the loop is already scheduled`,
257 + loc: terminal.loc,
258 });
279 - loopBody = [break_];
259 }
260
261 const testValue = this.visitValueBlock(
@@ -289,6 +268,7 @@ class Driver {
268 kind: "terminal",
269 terminal: {
270 kind: "do-while",
271 + loc: terminal.loc,
272 test: testValue,
273 loop: loopBody,
274 id: terminal.id,
@@ -333,14 +313,10 @@ class Driver {
313 if (loopId) {
314 loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
315 } else {
336 - const break_ = this.visitBreak(terminal.loop, null);
337 - CompilerError.invariant(break_ !== null, {
338 - reason: "If loop body is already scheduled it must be a break",
339 - description: null,
340 - loc: null,
341 - suggestions: null,
316 + CompilerError.invariant(false, {
317 + reason: `Unexpected 'while' where the loop is already scheduled`,
318 + loc: terminal.loc,
319 });
343 - loopBody = [break_];
320 }
321
322 this.cx.unscheduleAll(scheduleIds);
@@ -348,7 +324,7 @@ class Driver {
324 kind: "terminal",
325 terminal: {
326 kind: "while",
351 - // loc: terminal.loc,
327 + loc: terminal.loc,
328 test: testValue,
329 loop: loopBody,
330 id: terminal.id,
@@ -425,14 +401,10 @@ class Driver {
401 if (loopId) {
402 loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
403 } else {
428 - const break_ = this.visitBreak(terminal.loop, null);
429 - CompilerError.invariant(break_ !== null, {
430 - reason: "If loop body is already scheduled it must be a break",
431 - description: null,
432 - loc: null,
433 - suggestions: null,
404 + CompilerError.invariant(false, {
405 + reason: `Unexpected 'for' where the loop is already scheduled`,
406 + loc: terminal.loc,
407 });
435 - loopBody = [break_];
408 }
409
410 this.cx.unscheduleAll(scheduleIds);
@@ -440,6 +412,7 @@ class Driver {
412 kind: "terminal",
413 terminal: {
414 kind: "for",
415 + loc: terminal.loc,
416 init: initValue,
417 test: testValue,
418 update: updateValue,
@@ -505,14 +478,10 @@ class Driver {
478 if (loopId) {
479 loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
480 } else {
508 - const break_ = this.visitBreak(terminal.loop, null);
509 - CompilerError.invariant(break_ !== null, {
510 - reason: "If loop body is already scheduled it must be a break",
511 - description: null,
512 - loc: null,
513 - suggestions: null,
481 + CompilerError.invariant(false, {
482 + reason: `Unexpected 'for-of' where the loop is already scheduled`,
483 + loc: terminal.loc,
484 });
515 - loopBody = [break_];
485 }
486
487 this.cx.unscheduleAll(scheduleIds);
@@ -520,6 +489,7 @@ class Driver {
489 kind: "terminal",
490 terminal: {
491 kind: "for-of",
492 + loc: terminal.loc,
493 init: initValue,
494 loop: loopBody,
495 id: terminal.id,
@@ -583,14 +553,10 @@ class Driver {
553 if (loopId) {
554 loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
555 } else {
586 - const break_ = this.visitBreak(terminal.loop, null);
587 - CompilerError.invariant(break_ !== null, {
588 - reason: "If loop body is already scheduled it must be a break",
589 - description: null,
590 - loc: null,
591 - suggestions: null,
556 + CompilerError.invariant(false, {
557 + reason: `Unexpected 'for-in' where the loop is already scheduled`,
558 + loc: terminal.loc,
559 });
593 - loopBody = [break_];
560 }
561
562 this.cx.unscheduleAll(scheduleIds);
@@ -598,6 +564,7 @@ class Driver {
564 kind: "terminal",
565 terminal: {
566 kind: "for-in",
567 + loc: terminal.loc,
568 init: initValue,
569 loop: loopBody,
570 id: terminal.id,
@@ -615,7 +582,11 @@ class Driver {
582 case "branch": {
583 let consequent: ReactiveBlock | null = null;
584 if (this.cx.isScheduled(terminal.consequent)) {
618 - const break_ = this.visitBreak(terminal.consequent, null);
585 + const break_ = this.visitBreak(
586 + terminal.consequent,
587 + terminal.id,
588 + terminal.loc
589 + );
590 if (break_ !== null) {
591 consequent = [break_];
592 }
@@ -627,10 +598,10 @@ class Driver {
598
599 let alternate: ReactiveBlock | null = null;
600 if (this.cx.isScheduled(terminal.alternate)) {
630 - const break_ = this.visitBreak(terminal.alternate, null);
631 - if (break_ !== null) {
632 - alternate = [break_];
633 - }
601 + CompilerError.invariant(false, {
602 + reason: `Unexpected 'branch' where the alternate is already scheduled`,
603 + loc: terminal.loc,
604 + });
605 } else {
606 alternate = this.traverseBlock(
607 this.cx.ir.blocks.get(terminal.alternate)!
@@ -641,6 +612,7 @@ class Driver {
612 kind: "terminal",
613 terminal: {
614 kind: "if",
615 + loc: terminal.loc,
616 test: terminal.test,
617 consequent: consequent ?? this.emptyBlock(),
618 alternate: alternate,
@@ -664,15 +636,10 @@ class Driver {
636
637 let block: ReactiveBlock;
638 if (this.cx.isScheduled(terminal.block)) {
667 - const break_ = this.visitBreak(terminal.block, null);
668 - CompilerError.invariant(break_ !== null, {
669 - reason:
670 - "Expected a break target for a label whose body is already scheduled",
671 - description: null,
639 + CompilerError.invariant(false, {
640 + reason: `Unexpected 'label' where the block is already scheduled`,
641 loc: terminal.loc,
673 - suggestions: null,
642 });
675 - block = [break_];
643 } else {
644 block = this.traverseBlock(this.cx.ir.blocks.get(terminal.block)!);
645 }
@@ -682,6 +649,7 @@ class Driver {
649 kind: "terminal",
650 terminal: {
651 kind: "label",
652 + loc: terminal.loc,
653 block,
654 id: terminal.id,
655 },
@@ -730,14 +698,22 @@ class Driver {
698 case "goto": {
699 switch (terminal.variant) {
700 case GotoVariant.Break: {
733 - const break_ = this.visitBreak(terminal.block, terminal.id);
701 + const break_ = this.visitBreak(
702 + terminal.block,
703 + terminal.id,
704 + terminal.loc
705 + );
706 if (break_ !== null) {
707 blockValue.push(break_);
708 }
709 break;
710 }
711 case GotoVariant.Continue: {
740 - const continue_ = this.visitContinue(terminal.block, terminal.id);
712 + const continue_ = this.visitContinue(
713 + terminal.block,
714 + terminal.id,
715 + terminal.loc
716 + );
717 if (continue_ !== null) {
718 blockValue.push(continue_);
719 }
@@ -796,6 +772,7 @@ class Driver {
772 : { id: fallthroughId, implicit: false },
773 terminal: {
774 kind: "try",
775 + loc: terminal.loc,
776 block,
777 handlerBinding: terminal.handlerBinding,
778 handler,
@@ -1134,7 +1111,8 @@ class Driver {
1111
1112 visitBreak(
1113 block: BlockId,
1137 - id: InstructionId | null
1114 + id: InstructionId,
1115 + loc: SourceLocation
1116 ): ReactiveTerminalStatement<ReactiveBreakTerminal> {
1117 const target = this.cx.getBreakTarget(block);
1118 if (target === null) {
@@ -1149,6 +1127,7 @@ class Driver {
1127 kind: "terminal",
1128 terminal: {
1129 kind: "break",
1130 + loc,
1131 target: target.block,
1132 id,
1133 targetKind: target.type,
@@ -1159,7 +1138,8 @@ class Driver {
1138
1139 visitContinue(
1140 block: BlockId,
1162 - id: InstructionId
1141 + id: InstructionId,
1142 + loc: SourceLocation
1143 ): ReactiveTerminalStatement<ReactiveContinueTerminal> {
1144 const target = this.cx.getContinueTarget(block);
1145 CompilerError.invariant(target !== null, {
@@ -1173,6 +1153,7 @@ class Driver {
1153 kind: "terminal",
1154 terminal: {
1155 kind: "continue",
1156 + loc,
1157 target: target.block,
1158 id,
1159 targetKind: target.type,
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateEarlyReturns.ts
+3
@@ -9,6 +9,7 @@ import { visitReactiveFunction } from ".";
9 import { CompilerError, Effect } from "..";
10 import {
11 Environment,
12 + GeneratedSource,
13 InstructionKind,
14 ReactiveFunction,
15 ReactiveScope,
@@ -245,6 +246,7 @@ class Transform extends ReactiveFunctionTransform<State> {
246 terminal: {
247 kind: "label",
248 id: makeInstructionId(0),
249 + loc: GeneratedSource,
250 block: instructions,
251 },
252 },
@@ -319,6 +321,7 @@ class Transform extends ReactiveFunctionTransform<State> {
321 terminal: {
322 kind: "break",
323 id: makeInstructionId(0),
324 + loc,
325 targetKind: "labeled",
326 target: earlyReturnValue.label,
327 },