Enforce terminal has a .loc (and .id)
This PR ensures (via a static assertion function) that all terminal variants have a SourceLocation, and adds locations to the variants which didn't have it before. This also adds a static assertion that terminals have an InstructionId, though we already relied on that so it was checked via usage.
Joe Savona committed
Apr 6, 2023 at 15:50 UTC
f51e2a6bf408c6a5d5fadc25c243e2bd8a5d8927
5 files changed
+84
-17
compiler/forget/src/HIR/BuildHIR.ts
+49
-15
@@ -182,6 +182,7 @@ function lowerStatement(
182
kind: "throw",
183
value,
184
id: makeInstructionId(0),
185
+ loc: stmt.node.loc ?? GeneratedSource,
186
};
187
builder.terminate(terminal, "block");
188
return;
@@ -211,12 +212,14 @@ function lowerStatement(
212
const continuationBlock = builder.reserve("block");
213
// Block for the consequent (if the test is truthy)
214
const consequentBlock = builder.enter("block", (_blockId) => {
214
- lowerStatement(builder, stmt.get("consequent"));
215
+ const consequent = stmt.get("consequent");
216
+ lowerStatement(builder, consequent);
217
return {
218
kind: "goto",
219
block: continuationBlock.id,
220
variant: GotoVariant.Break,
221
id: makeInstructionId(0),
222
+ loc: consequent.node.loc ?? GeneratedSource,
223
};
224
});
225
// Block for the alternate (if the test is not truthy)
@@ -230,6 +233,7 @@ function lowerStatement(
233
block: continuationBlock.id,
234
variant: GotoVariant.Break,
235
id: makeInstructionId(0),
236
+ loc: alternate.node?.loc ?? GeneratedSource,
237
};
238
});
239
} else {
@@ -244,6 +248,7 @@ function lowerStatement(
248
alternate: alternateBlock,
249
fallthrough: continuationBlock.id,
250
id: makeInstructionId(0),
251
+ loc: stmt.node.loc ?? GeneratedSource,
252
};
253
builder.terminateWithContinuation(terminal, continuationBlock);
254
return;
@@ -262,6 +267,7 @@ function lowerStatement(
267
block,
268
variant: GotoVariant.Break,
269
id: makeInstructionId(0),
270
+ loc: stmt.node.loc ?? GeneratedSource,
271
},
272
"block"
273
);
@@ -276,6 +282,7 @@ function lowerStatement(
282
block,
283
variant: GotoVariant.Continue,
284
id: makeInstructionId(0),
285
+ loc: stmt.node.loc ?? GeneratedSource,
286
},
287
"block"
288
);
@@ -297,7 +304,11 @@ function lowerStatement(
304
severity: ErrorSeverity.Todo,
305
nodePath: stmt,
306
});
300
- return { kind: "unsupported", id: makeInstructionId(0) };
307
+ return {
308
+ kind: "unsupported",
309
+ id: makeInstructionId(0),
310
+ loc: init.node?.loc ?? GeneratedSource,
311
+ };
312
}
313
lowerStatement(builder, init);
314
return {
@@ -305,6 +316,7 @@ function lowerStatement(
316
block: testBlock.id,
317
variant: GotoVariant.Break,
318
id: makeInstructionId(0),
319
+ loc: init.node.loc ?? GeneratedSource,
320
};
321
});
322
@@ -318,6 +330,7 @@ function lowerStatement(
330
block: testBlock.id,
331
variant: GotoVariant.Break,
332
id: makeInstructionId(0),
333
+ loc: update.node?.loc ?? GeneratedSource,
334
};
335
});
336
}
@@ -328,12 +341,14 @@ function lowerStatement(
341
updateBlock ?? testBlock.id,
342
continuationBlock.id,
343
() => {
331
- lowerStatement(builder, stmt.get("body"));
344
+ const body = stmt.get("body");
345
+ lowerStatement(builder, body);
346
return {
347
kind: "goto",
348
block: updateBlock ?? testBlock.id,
349
variant: GotoVariant.Continue,
350
id: makeInstructionId(0),
351
+ loc: body.node.loc ?? GeneratedSource,
352
};
353
}
354
);
@@ -371,6 +386,7 @@ function lowerStatement(
386
consequent: bodyBlock,
387
alternate: continuationBlock.id,
388
id: makeInstructionId(0),
389
+ loc: stmt.node.loc ?? GeneratedSource,
390
},
391
continuationBlock
392
);
@@ -390,12 +406,14 @@ function lowerStatement(
406
conditionalBlock.id,
407
continuationBlock.id,
408
() => {
393
- lowerStatement(builder, stmt.get("body"));
409
+ const body = stmt.get("body");
410
+ lowerStatement(builder, body);
411
return {
412
kind: "goto",
413
block: conditionalBlock.id,
414
variant: GotoVariant.Continue,
415
id: makeInstructionId(0),
416
+ loc: body.node.loc ?? GeneratedSource,
417
};
418
}
419
);
@@ -427,6 +445,7 @@ function lowerStatement(
445
consequent: loopBlock,
446
alternate: continuationBlock.id,
447
id: makeInstructionId(0),
448
+ loc: stmt.node.loc ?? GeneratedSource,
449
};
450
// Complete the conditional and continue with code after the loop
451
builder.terminateWithContinuation(terminal, continuationBlock);
@@ -452,14 +471,16 @@ function lowerStatement(
471
// explicitly *don't* pass the label down
472
const continuationBlock = builder.reserve("block");
473
const block = builder.enter("block", () => {
474
+ const body = stmt.get("body");
475
builder.label(label, continuationBlock.id, () => {
456
- lowerStatement(builder, stmt.get("body"));
476
+ lowerStatement(builder, body);
477
});
478
return {
479
kind: "goto",
480
block: continuationBlock.id,
481
variant: GotoVariant.Break,
482
id: makeInstructionId(0),
483
+ loc: body.node.loc ?? GeneratedSource,
484
};
485
});
486
builder.terminateWithContinuation(
@@ -521,6 +542,7 @@ function lowerStatement(
542
block: fallthrough,
543
variant: GotoVariant.Break,
544
id: makeInstructionId(0),
545
+ loc: case_.node.loc ?? GeneratedSource,
546
};
547
});
548
});
@@ -561,6 +583,7 @@ function lowerStatement(
583
cases,
584
fallthrough: continuationBlock.id,
585
id: makeInstructionId(0),
586
+ loc: stmt.node.loc ?? GeneratedSource,
587
},
588
continuationBlock
589
);
@@ -646,12 +669,14 @@ function lowerStatement(
669
conditionalBlock.id,
670
continuationBlock.id,
671
() => {
649
- lowerStatement(builder, stmt.get("body"));
672
+ const body = stmt.get("body");
673
+ lowerStatement(builder, body);
674
return {
675
kind: "goto",
676
block: conditionalBlock.id,
677
variant: GotoVariant.Continue,
678
id: makeInstructionId(0),
679
+ loc: body.node.loc ?? GeneratedSource,
680
};
681
}
682
);
@@ -681,6 +706,7 @@ function lowerStatement(
706
consequent: loopBlock,
707
alternate: continuationBlock.id,
708
id: makeInstructionId(0),
709
+ loc,
710
};
711
// Complete the conditional and continue with code after the loop
712
builder.terminateWithContinuation(terminal, continuationBlock);
@@ -729,12 +755,14 @@ function lowerStatement(
755
756
const loopBlock = builder.enter("block", (_blockId) => {
757
return builder.loop(label, initBlock.id, continuationBlock.id, () => {
732
- lowerStatement(builder, stmt.get("body"));
758
+ const body = stmt.get("body");
759
+ lowerStatement(builder, body);
760
return {
761
kind: "goto",
762
block: initBlock.id,
763
variant: GotoVariant.Continue,
764
id: makeInstructionId(0),
765
+ loc: body.node.loc ?? GeneratedSource,
766
};
767
});
768
});
@@ -794,6 +822,7 @@ function lowerStatement(
822
test,
823
consequent: loopBlock,
824
alternate: continuationBlock.id,
825
+ loc: stmt.node.loc ?? GeneratedSource,
826
},
827
continuationBlock
828
);
@@ -1078,6 +1107,7 @@ function lowerExpression(
1107
variant: GotoVariant.Break,
1108
block: continuationBlock.id,
1109
id: makeInstructionId(0),
1110
+ loc,
1111
};
1112
});
1113
@@ -1099,6 +1129,7 @@ function lowerExpression(
1129
variant: GotoVariant.Break,
1130
block: continuationBlock.id,
1131
id: makeInstructionId(0),
1132
+ loc,
1133
};
1134
});
1135
@@ -1217,10 +1248,8 @@ function lowerExpression(
1248
1249
// Block for the consequent (if the test is truthy)
1250
const consequentBlock = builder.enter("value", (_blockId) => {
1220
- const consequent = lowerExpressionToTemporary(
1221
- builder,
1222
- expr.get("consequent")
1223
- );
1251
+ const consequentPath = expr.get("consequent");
1252
+ const consequent = lowerExpressionToTemporary(builder, consequentPath);
1253
lowerValueToTemporary(builder, {
1254
kind: "StoreLocal",
1255
lvalue: { kind: InstructionKind.Const, place: { ...place } },
@@ -1232,14 +1261,13 @@ function lowerExpression(
1261
block: continuationBlock.id,
1262
variant: GotoVariant.Break,
1263
id: makeInstructionId(0),
1264
+ loc: consequentPath.node.loc ?? GeneratedSource,
1265
};
1266
});
1267
// Block for the alternate (if the test is not truthy)
1268
const alternateBlock = builder.enter("value", (_blockId) => {
1239
- const alternate = lowerExpressionToTemporary(
1240
- builder,
1241
- expr.get("alternate")
1242
- );
1269
+ const alternatePath = expr.get("alternate");
1270
+ const alternate = lowerExpressionToTemporary(builder, alternatePath);
1271
lowerValueToTemporary(builder, {
1272
kind: "StoreLocal",
1273
lvalue: { kind: InstructionKind.Const, place: { ...place } },
@@ -1251,6 +1279,7 @@ function lowerExpression(
1279
block: continuationBlock.id,
1280
variant: GotoVariant.Break,
1281
id: makeInstructionId(0),
1282
+ loc: alternatePath.node.loc ?? GeneratedSource,
1283
};
1284
});
1285
@@ -1272,6 +1301,7 @@ function lowerExpression(
1301
consequent: consequentBlock,
1302
alternate: alternateBlock,
1303
id: makeInstructionId(0),
1304
+ loc: exprLoc,
1305
},
1306
continuationBlock
1307
);
@@ -1299,6 +1329,7 @@ function lowerExpression(
1329
block: continuationBlock.id,
1330
variant: GotoVariant.Break,
1331
id: makeInstructionId(0),
1332
+ loc: leftPlace.loc,
1333
};
1334
});
1335
const alternate = builder.enter("value", () => {
@@ -1314,6 +1345,7 @@ function lowerExpression(
1345
block: continuationBlock.id,
1346
variant: GotoVariant.Break,
1347
id: makeInstructionId(0),
1348
+ loc: right.loc,
1349
};
1350
});
1351
builder.terminateWithContinuation(
@@ -1345,6 +1377,7 @@ function lowerExpression(
1377
consequent,
1378
alternate,
1379
id: makeInstructionId(0),
1380
+ loc: exprLoc,
1381
},
1382
continuationBlock
1383
);
@@ -2537,6 +2570,7 @@ function lowerAssignment(
2570
consequent,
2571
alternate,
2572
id: makeInstructionId(0),
2573
+ loc,
2574
},
2575
continuationBlock
2576
);
compiler/forget/src/HIR/HIR.ts
+29
-2
@@ -278,13 +278,36 @@ export type Terminal =
278
| OptionalCallTerminal
279
| LabelTerminal;
280
281
+function _staticInvariantTerminalHasLocation(
282
+ terminal: Terminal
283
+): SourceLocation {
284
+ // If this fails, it is because a variant of Terminal is missing a .loc - add it!
285
+ return terminal.loc;
286
+}
287
+
288
+function _staticInvariantTerminalHasInstructionId(
289
+ terminal: Terminal
290
+): InstructionId {
291
+ // If this fails, it is because a variant of Terminal is missing a .id - add it!
292
+ return terminal.id;
293
+}
294
+
295
/**
296
* Terminal nodes allowed for a value block
297
*/
298
export type ValueTerminal = IfTerminal | GotoTerminal;
299
// A terminal that couldn't be lowered correctly.
286
-export type UnsupportedTerminal = { kind: "unsupported"; id: InstructionId };
287
-export type ThrowTerminal = { kind: "throw"; value: Place; id: InstructionId };
300
+export type UnsupportedTerminal = {
301
+ kind: "unsupported";
302
+ id: InstructionId;
303
+ loc: SourceLocation;
304
+};
305
+export type ThrowTerminal = {
306
+ kind: "throw";
307
+ value: Place;
308
+ id: InstructionId;
309
+ loc: SourceLocation;
310
+};
311
export type Case = { test: Place | null; block: BlockId };
312
313
export type ReturnTerminal = {
@@ -299,6 +322,7 @@ export type GotoTerminal = {
322
block: BlockId;
323
variant: GotoVariant;
324
id: InstructionId;
325
+ loc: SourceLocation;
326
};
327
328
export enum GotoVariant {
@@ -313,6 +337,7 @@ export type IfTerminal = {
337
alternate: BlockId;
338
fallthrough: BlockId | null;
339
id: InstructionId;
340
+ loc: SourceLocation;
341
};
342
343
export type BranchTerminal = {
@@ -321,6 +346,7 @@ export type BranchTerminal = {
346
consequent: BlockId;
347
alternate: BlockId;
348
id: InstructionId;
349
+ loc: SourceLocation;
350
};
351
352
export type SwitchTerminal = {
@@ -329,6 +355,7 @@ export type SwitchTerminal = {
355
cases: Case[];
356
fallthrough: BlockId | null;
357
id: InstructionId;
358
+ loc: SourceLocation;
359
};
360
361
export type DoWhileTerminal = {
compiler/forget/src/HIR/HIRBuilder.ts
+1
@@ -581,6 +581,7 @@ export function removeDeadDoWhileStatements(func: HIR): void {
581
block: block.terminal.loop,
582
variant: GotoVariant.Break,
583
id: block.terminal.id,
584
+ loc: block.terminal.loc,
585
};
586
}
587
}
compiler/forget/src/HIR/visitors.ts
+4
@@ -512,6 +512,7 @@ export function mapTerminalSuccessors(
512
block: target,
513
variant: terminal.variant,
514
id: makeInstructionId(0),
515
+ loc: terminal.loc,
516
};
517
}
518
case "if": {
@@ -526,6 +527,7 @@ export function mapTerminalSuccessors(
527
alternate,
528
fallthrough,
529
id: makeInstructionId(0),
530
+ loc: terminal.loc,
531
};
532
}
533
case "branch": {
@@ -537,6 +539,7 @@ export function mapTerminalSuccessors(
539
consequent,
540
alternate,
541
id: makeInstructionId(0),
542
+ loc: terminal.loc,
543
};
544
}
545
case "switch": {
@@ -555,6 +558,7 @@ export function mapTerminalSuccessors(
558
cases,
559
fallthrough,
560
id: makeInstructionId(0),
561
+ loc: terminal.loc,
562
};
563
}
564
case "logical": {
compiler/forget/src/Optimization/ConstantPropagation.ts
+1
@@ -135,6 +135,7 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
135
variant: GotoVariant.Break,
136
block: targetBlockId,
137
id: terminal.id,
138
+ loc: terminal.loc,
139
};
140
}
141
break;