TreeVisitor distinguish blocks from statements
TreeVisitor didn't distinguish between the type of a block and the type of an item that can occur within a block - this was fine for Codegen which can use `t.Statement` for both of those values. However, the upcoming scope construction needs to distinguish instructions in a block from a block itself, so this PR adds a new type parameter.
Joe Savona committed
Dec 12, 2022 at 16:46 UTC
5aa4143e2404ee8b993c735ea6c5146e52edca71
8 files changed
+87
-35
compiler/forget/src/HIR/Codegen.ts
+1
@@ -93,6 +93,7 @@ class CodegenVisitor
93
implements
94
Visitor<
95
Array<t.Statement>,
96
+ t.Statement,
97
Array<t.Statement>,
98
Array<t.Statement>,
99
t.Expression,
compiler/forget/src/HIR/HIRTreeVisitor.ts
+73
-29
@@ -28,6 +28,7 @@ import {
28
* See the {@link Visitor} interface for more about implementing a visitor.
29
*/
30
export function visitTree<
31
+ TBlockBuilder,
32
TBlock,
33
TInit,
34
TValueBlock,
@@ -36,32 +37,64 @@ export function visitTree<
37
TCase
38
>(
39
fn: HIRFunction,
39
- visitor: Visitor<TBlock, TInit, TValueBlock, TValue, TStatement, TCase>
40
-): TStatement {
40
+ visitor: Visitor<
41
+ TBlockBuilder,
42
+ TBlock,
43
+ TInit,
44
+ TValueBlock,
45
+ TValue,
46
+ TStatement,
47
+ TCase
48
+ >
49
+): TBlock {
50
const cx = new Context(fn.body);
51
const driver = new Driver(cx, visitor);
52
return driver.traverseBlock(cx.block(fn.body.entry));
53
}
54
46
-class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
55
+class Driver<
56
+ TBlockBuilder,
57
+ TBlock,
58
+ TInit,
59
+ TValueBlock,
60
+ TValue,
61
+ TStatement,
62
+ TCase
63
+> {
64
cx: Context;
48
- visitor: Visitor<TBlock, TInit, TValueBlock, TValue, TStatement, TCase>;
65
+ visitor: Visitor<
66
+ TBlockBuilder,
67
+ TBlock,
68
+ TInit,
69
+ TValueBlock,
70
+ TValue,
71
+ TStatement,
72
+ TCase
73
+ >;
74
75
constructor(
76
cx: Context,
52
- visitor: Visitor<TBlock, TInit, TValueBlock, TValue, TStatement, TCase>
77
+ visitor: Visitor<
78
+ TBlockBuilder,
79
+ TBlock,
80
+ TInit,
81
+ TValueBlock,
82
+ TValue,
83
+ TStatement,
84
+ TCase
85
+ >
86
) {
87
this.cx = cx;
88
this.visitor = visitor;
89
}
90
58
- traverseBlock(block: BasicBlock): TStatement {
91
+ traverseBlock(block: BasicBlock): TBlock {
92
const blockValue = this.visitor.enterBlock();
93
this.visitBlock(block, blockValue);
94
return this.visitor.leaveBlock(blockValue);
95
}
96
64
- visitBlock(block: BasicBlock, blockValue: TBlock): void {
97
+ visitBlock(block: BasicBlock, blockValue: TBlockBuilder): void {
98
invariant(
99
!this.cx.emitted.has(block.id),
100
`Cannot emit the same block twice: bb${block.id}`
@@ -120,19 +153,29 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
153
}
154
155
this.visitor.visitTerminalId(terminal.id);
123
- let consequent: TStatement | null = null;
156
+ let consequent: TBlock | null = null;
157
if (this.cx.isScheduled(terminal.consequent)) {
125
- consequent = this.visitBreak(terminal.consequent);
158
+ const break_ = this.visitBreak(terminal.consequent);
159
+ if (break_ !== null) {
160
+ const builder = this.visitor.enterBlock();
161
+ this.visitor.appendBlock(builder, break_);
162
+ consequent = this.visitor.leaveBlock(builder);
163
+ }
164
} else {
165
consequent = this.traverseBlock(
166
this.cx.ir.blocks.get(terminal.consequent)!
167
);
168
}
169
132
- let alternate: TStatement | null = null;
170
+ let alternate: TBlock | null = null;
171
if (alternateId !== null) {
172
if (this.cx.isScheduled(alternateId)) {
135
- alternate = this.visitBreak(alternateId);
173
+ const break_ = this.visitBreak(alternateId);
174
+ if (break_ !== null) {
175
+ const builder = this.visitor.enterBlock();
176
+ this.visitor.appendBlock(builder, break_);
177
+ alternate = this.visitor.leaveBlock(builder);
178
+ }
179
} else {
180
alternate = this.traverseBlock(this.cx.ir.blocks.get(alternateId)!);
181
}
@@ -271,7 +314,7 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
314
value: testTerminal.test,
315
id: testTerminal.id,
316
});
274
- let loopBody: TStatement;
317
+ let loopBody: TBlock;
318
if (loopId) {
319
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
320
} else {
@@ -360,7 +403,7 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
403
);
404
const updateValue = this.visitValueBlock(blockValue, updateBlock);
405
363
- let loopBody: TStatement;
406
+ let loopBody: TBlock;
407
if (loopId) {
408
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
409
} else {
@@ -434,7 +477,7 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
477
}
478
}
479
437
- visitInitBlock(parent: TBlock, block: BasicBlock): TInit {
480
+ visitInitBlock(parent: TBlockBuilder, block: BasicBlock): TInit {
481
const initBlock = this.visitor.enterInitBlock(parent);
482
for (const instr of block.instructions) {
483
const value = this.visitor.visitValue(instr.value, instr.id);
@@ -445,7 +488,7 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
488
}
489
490
visitValueBlock(
448
- parent: TBlock,
491
+ parent: TBlockBuilder,
492
block: BasicBlock,
493
terminalValue?: { value: InstructionValue; id: InstructionId }
494
): TValue {
@@ -475,7 +518,7 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
518
return this.visitor.leaveValueBlock(valueBlock, value);
519
}
520
478
- emptyBlock(): TStatement {
521
+ emptyBlock(): TBlock {
522
const block = this.visitor.enterBlock();
523
return this.visitor.leaveBlock(block);
524
}
@@ -533,7 +576,7 @@ class Driver<TBlock, TInit, TValueBlock, TValue, TStatement, TCase> {
576
}
577
}
578
536
- visitInstr(instr: Instruction, blockValue: TBlock): void {
579
+ visitInstr(instr: Instruction, blockValue: TBlockBuilder): void {
580
const value = this.visitor.visitValue(instr.value, instr.id);
581
const item = this.visitor.visitInstruction(instr, value);
582
this.visitor.appendBlock(blockValue, item);
@@ -775,6 +818,7 @@ type ControlFlowTarget =
818
* TCase = representation of a switch case
819
*/
820
export interface Visitor<
821
+ TBlockBuilder,
822
TBlock,
823
TInit,
824
TValueBlock,
@@ -786,20 +830,20 @@ export interface Visitor<
830
* Must create an "empty" instance of the visitor's represenation for
831
* the contents of a block.
832
*/
789
- enterBlock(): TBlock;
833
+ enterBlock(): TBlockBuilder;
834
835
/**
836
* Appends an item onto the given block, with an optional label. The label
837
* indicates that a break/continue will proceed to code *after* the given item.
838
*/
795
- appendBlock(block: TBlock, item: TStatement, label?: BlockId): void;
839
+ appendBlock(block: TBlockBuilder, item: TStatement, label?: BlockId): void;
840
841
/**
842
* Converts the visitor's block representation into the representation of a
843
* block item, simultaneously "closing" the given block. The block will no
844
* longer be modified by the visitor driver.
845
*/
802
- leaveBlock(block: TBlock): TStatement;
846
+ leaveBlock(block: TBlockBuilder): TBlock;
847
848
/**
849
* Must create an "empty" instance of the visitor's representation for a value
@@ -809,7 +853,7 @@ export interface Visitor<
853
* The exception is the for initializer, which may contain declarations but not
854
* other statement types.
855
*/
812
- enterValueBlock(block: TBlock): TValueBlock;
856
+ enterValueBlock(block: TBlockBuilder): TValueBlock;
857
858
/**
859
* Appends an item onto the given value block.
@@ -822,7 +866,7 @@ export interface Visitor<
866
*/
867
leaveValueBlock(block: TValueBlock, value: TValue | null): TValue;
868
825
- enterInitBlock(block: TBlock): TValueBlock;
869
+ enterInitBlock(block: TBlockBuilder): TValueBlock;
870
871
appendInitBlock(block: TValueBlock, item: TStatement): void;
872
@@ -860,38 +904,38 @@ export interface Visitor<
904
* own representations.
905
*/
906
visitTerminal(
863
- terminal: BlockTerminal<TInit, TValue, TStatement, TCase>
907
+ terminal: BlockTerminal<TInit, TValue, TBlock, TCase>
908
): TStatement;
909
910
/**
911
* Visits a switch case statement, which is collected into a switch terminal
912
* variant.
913
*/
870
- visitCase(test: TValue | null, block: TStatement): TCase;
914
+ visitCase(test: TValue | null, block: TBlock): TCase;
915
}
916
873
-export type BlockTerminal<TInit, TValue, TStatement, TCase> =
917
+export type BlockTerminal<TInit, TValue, TBlock, TCase> =
918
| { kind: "return"; loc: SourceLocation; value: TValue | null }
919
| { kind: "throw"; value: TValue }
920
| {
921
kind: "if";
922
test: TValue;
879
- consequent: TStatement;
880
- alternate: TStatement | null;
923
+ consequent: TBlock;
924
+ alternate: TBlock | null;
925
}
926
| { kind: "switch"; test: TValue; cases: Array<TCase> }
927
| {
928
kind: "while";
929
loc: SourceLocation;
930
test: TValue;
887
- loop: TStatement;
931
+ loop: TBlock;
932
}
933
| {
934
kind: "for";
935
init: TInit;
936
test: TValue;
937
update: TValue;
894
- loop: TStatement;
938
+ loop: TBlock;
939
}
940
| { kind: "break"; label: BlockId | null }
941
| { kind: "continue"; label: BlockId | null };
compiler/forget/src/HIR/InferReactiveScopeDependencies.ts
+1
-1
@@ -35,7 +35,7 @@ export function instructionInScope(
35
}
36
37
class ScopeDependenciesVisitor
38
- implements Visitor<void, void, void, InstructionValue, void, void>
38
+ implements Visitor<void, void, void, void, InstructionValue, void, void>
39
{
40
#identifiers: Map<Identifier, InstructionId> = new Map();
41
// Scopes that are currently active at this point in the traversal
compiler/forget/src/HIR/InferReactiveScopes.ts
+2
-2
@@ -166,7 +166,7 @@ type ShadowableReactiveScope = {
166
};
167
168
class MergeOverlappingReactiveScopesVisitor
169
- implements Visitor<void, void, void, void, void, void>
169
+ implements Visitor<void, void, void, void, void, void, void>
170
{
171
scopes: Array<BlockScope> = [];
172
seenScopes: Set<ScopeId> = new Set();
@@ -332,7 +332,7 @@ type PendingReactiveScope = { active: boolean; scope: ReactiveScope };
332
* were opened (start encountered).
333
*/
334
class AlignReactiveScopesToBlockScopeRangeVisitor
335
- implements Visitor<void, void, void, void, void, void>
335
+ implements Visitor<void, void, void, void, void, void, void>
336
{
337
// For each block scope (outer array) stores a list of ReactiveScopes that start
338
// in that block scope.
compiler/forget/src/HIR/PrintHIRTree.ts
+1
@@ -29,6 +29,7 @@ class PrintVisitor
29
implements
30
Visitor<
31
Array<string>,
32
+ string,
33
Array<string>,
34
Array<string>,
35
string,
compiler/forget/src/__tests__/fixtures/hir/complex-while.expect.md
+3
-1
@@ -81,7 +81,9 @@ flowchart TB
81
function foo$0(a$4, b$5, c$6) {
82
bb1: if (a$4) {
83
while (b$5) {
84
- bb7: if (c$6) break;
84
+ bb7: if (c$6) {
85
+ break;
86
+ }
87
}
88
}
89
}
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md
+3
-1
@@ -230,7 +230,9 @@ function Component$0(props$12) {
230
d$24 = z$19;
231
mutate$7(a$18, b$20);
232
233
- bb4: if (cond$8(a$18)) break;
233
+ bb4: if (cond$8(a$18)) {
234
+ break;
235
+ }
236
}
237
238
bb7: if (a$18) {
compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md
+3
-1
@@ -210,7 +210,9 @@ function Component$0(props$11) {
210
bb2: while (true) {
211
mutate$6(a$12, b$13);
212
213
- bb4: if (cond$7(a$12)) break;
213
+ bb4: if (cond$7(a$12)) {
214
+ break;
215
+ }
216
}
217
218
bb7: if (a$12) {