@samitouri / QOS-React-2 / commits / f51e2a6bf4

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;