1 /// Copyright: Copyright (c) 2017-2020 Andrey Penechko.
2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 /// Authors: Andrey Penechko.
4 module vox.fe.ast.stmt.switch_stmt;
5 
6 import vox.all;
7 
8 
9 @(AstType.stmt_switch)
10 struct SwitchStmtNode
11 {
12 	mixin AstNodeData!(AstType.stmt_switch, 0, AstNodeState.name_register_self_done);
13 	AstIndex condition;
14 	AstIndex elseStmt; // else block stmt, nullable
15 	Array!SwitchCase cases;
16 	IrIndex[] argsValues;
17 }
18 
19 struct SwitchCase
20 {
21 	AstIndex expr; // case constant expr
22 	AstIndex stmt; // block stmt
23 }
24 
25 void print_switch(SwitchStmtNode* node, ref AstPrintState state)
26 {
27 	state.print("SWITCH");
28 	print_ast(node.condition, state);
29 	foreach (SwitchCase c; node.cases)
30 	{
31 		state.print("CASE");
32 		print_ast(c.expr, state);
33 		print_ast(c.stmt, state);
34 	}
35 	if (node.elseStmt)
36 	{
37 		state.print("ELSE");
38 		print_ast(node.elseStmt, state);
39 	}
40 }
41 
42 void post_clone_switch(SwitchStmtNode* node, ref CloneState state)
43 {
44 	state.fixAstIndex(node.condition);
45 	state.fixAstIndex(node.elseStmt);
46 	node.cases = node.cases.dup(state.context.arrayArena);
47 	foreach(ref SwitchCase c; node.cases) {
48 		state.fixAstIndex(c.expr);
49 		state.fixAstIndex(c.stmt);
50 	}
51 }
52 
53 void name_register_nested_switch(SwitchStmtNode* node, ref NameRegisterState state)
54 {
55 	node.state = AstNodeState.name_register_nested;
56 	require_name_register(node.condition, state);
57 	if (node.elseStmt.isDefined) require_name_register(node.elseStmt, state);
58 	foreach(ref SwitchCase c; node.cases) {
59 		require_name_register(c.expr, state);
60 		require_name_register(c.stmt, state);
61 	}
62 	node.state = AstNodeState.name_register_nested_done;
63 }
64 
65 void name_resolve_switch(SwitchStmtNode* node, ref NameResolveState state)
66 {
67 	require_name_resolve(node.condition, state);
68 	if (node.elseStmt.isDefined) require_name_resolve(node.elseStmt, state);
69 	foreach(ref SwitchCase c; node.cases) {
70 		require_name_resolve(c.expr, state);
71 		require_name_resolve(c.stmt, state);
72 	}
73 }
74 
75 void type_check_switch(SwitchStmtNode* node, ref TypeCheckState state)
76 {
77 	CompilationContext* c = state.context;
78 	require_type_check(node.condition, state);
79 	if (node.elseStmt.isDefined) require_type_check(node.elseStmt, state);
80 	// Args: value + N integer constants
81 	node.argsValues = c.allocateTempArray!IrIndex(node.cases.length + 1);
82 	foreach(i, ref SwitchCase caseNode; node.cases) {
83 		require_type_check(caseNode.expr, state);
84 		require_type_check(caseNode.stmt, state);
85 		node.argsValues[i+1] = eval_static_expr(caseNode.expr, c);
86 	}
87 }
88 
89 bool isSwitchableType(CompilationContext* c, AstIndex type)
90 {
91 	return false;
92 }
93 
94 void ir_gen_switch(ref IrGenState gen, IrIndex currentBlock, ref IrLabel nextStmt, SwitchStmtNode* node)
95 {
96 	CompilationContext* c = gen.context;
97 
98 	IrLabel afterExpr = IrLabel(currentBlock);
99 	ExprValue lval = ir_gen_expr(gen, node.condition, currentBlock, afterExpr);
100 	currentBlock = afterExpr.blockIndex;
101 	IrIndex rval = lval.rvalue(gen, node.loc, currentBlock);
102 
103 	node.argsValues[0] = rval;
104 	gen.builder.emitInstr!(IrOpcode.branch_switch)(currentBlock, node.argsValues);
105 
106 	IrBasicBlock* block = gen.ir.getBlock(currentBlock);
107 	assert(!block.isFinished);
108 	block.isFinished = true;
109 
110 	// default case
111 	IrIndex defaultBlock = gen.builder.addBasicBlock;
112 	gen.builder.addBlockTarget(currentBlock, defaultBlock);
113 	gen.builder.sealBlock(defaultBlock);
114 	if (node.elseStmt.isDefined) {
115 		ir_gen_stmt(gen, node.elseStmt, defaultBlock, nextStmt);
116 	} else {
117 		gen.builder.addUnreachable(defaultBlock);
118 	}
119 
120 	// cases
121 	foreach(i, ref SwitchCase switchCase; node.cases) {
122 		IrIndex caseBlock = gen.builder.addBasicBlock;
123 		gen.builder.addBlockTarget(currentBlock, caseBlock);
124 		gen.builder.sealBlock(caseBlock);
125 		ir_gen_stmt(gen, switchCase.stmt, caseBlock, nextStmt);
126 	}
127 }