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
},