1 module d.semantic.statement;
2 
3 import d.semantic.semantic;
4 
5 import d.ast.conditional;
6 import d.ast.expression;
7 import d.ast.statement;
8 
9 import source.location;
10 import source.name;
11 
12 import d.ir.dscope;
13 import d.ir.expression;
14 import d.ir.instruction;
15 import d.ir.symbol;
16 import d.ir.type;
17 
18 struct StatementVisitor {
19 private:
20 	SemanticPass pass;
21 	alias pass this;
22 	
23 	Body fbody;
24 	
25 	BasicBlockRef currentBlockRef;
26 	
27 	ref inout(BasicBlock) currentBlock() inout {
28 		return fbody[currentBlockRef];
29 	}
30 	
31 	bool doesTerminate(BasicBlockRef b) const {
32 		return !b || fbody[b].terminate;
33 	}
34 	
35 	@property terminate() const {
36 		return doesTerminate(currentBlockRef);
37 	}
38 	
39 	bool allowUnreachable;
40 	BreakKind breakKind;
41 	
42 	UnwindInfo[] unwindActions;
43 	
44 	VariableExpression retval;
45 	
46 	struct Label {
47 		BasicBlockRef block;
48 		uint level;
49 		
50 		alias block this;
51 	}
52 	
53 	Label breakLabel;
54 	Label continueLabel;
55 	
56 	Label[Name] labels;
57 	
58 	// Forward goto can only be resolved when the label is reached.
59 	struct UnresolvedGoto {
60 		UnwindInfo[] unwind;
61 		BasicBlockRef block;
62 	}
63 	
64 	UnresolvedGoto[][Name] inFlightGotos;
65 	
66 	CaseEntry[] cases;
67 	
68 	// Mechanism to detect jump over declaration.
69 	// XXX: Kind of clunky, but will do for now.
70 	Variable[] varStack;
71 	Variable[] switchStack;
72 	Variable[][Name] labelStacks;
73 	Variable[][][Name] inFlightGotosStacks;
74 	
75 public:
76 	this(SemanticPass pass) {
77 		this.pass = pass;
78 	}
79 	
80 	void getBody(Function f, BlockStatement b) {
81 		auto oldScope = currentScope;
82 		scope(exit) currentScope = oldScope;
83 		
84 		scope(failure) {
85 			f.fbody = fbody;
86 			f.dump(context);
87 		}
88 		
89 		currentScope = f;
90 		auto entry = startNewBranch(BuiltinName!"entry");
91 		
92 		flatten(b);
93 		f.fbody = fbody;
94 		
95 		auto rt = returnType.getType();
96 		// TODO: Handle auto return by specifying it to this visitor
97 		// instead of deducing it in dubious ways.
98 		if (rt.kind == TypeKind.Builtin &&
99 			rt.qualifier == TypeQualifier.Mutable &&
100 			rt.builtin == BuiltinType.None) {
101 			returnType = Type.get(BuiltinType.Void)
102 				.getParamType(ParamKind.Regular);
103 		}
104 		
105 		if (!terminate) {
106 			if (returnType.kind == TypeKind.Builtin &&
107 				returnType.builtin == BuiltinType.Void) {
108 				currentBlock.ret(b.location);
109 			} else {
110 				import source.exception;
111 				throw new CompileException(f.location, "Must return");
112 			}
113 		}
114 	}
115 	
116 	void visit(Statement s) {
117 		if (s is null) {
118 			return;
119 		}
120 		
121 		if (!terminate) {
122 			goto Dispatch;
123 		}
124 		
125 		if (auto b = cast(BlockStatement) s) {
126 			return visit(b);
127 		}
128 		
129 		if (auto c = cast(CaseStatement) s) {
130 			return visit(c);
131 		}
132 		
133 		if (auto l = cast(LabeledStatement) s) {
134 			return visit(l);
135 		}
136 		
137 		if (allowUnreachable) {
138 			startNewBranch(BuiltinName!"unreachable");
139 		} else {
140 			import source.exception;
141 			throw new CompileException(s.location, "Unreachable statement");
142 		}
143 		
144 		Dispatch:
145 		allowUnreachable = false;
146 		this.dispatch(s);
147 	}
148 	
149 private:
150 	BasicBlockRef flatten(BlockStatement b) {
151 		return buildBlock(b.location, b.statements);
152 	}
153 	
154 	BasicBlockRef getUnwindBlock() {
155 		foreach_reverse (ref b; unwindActions) {
156 			if (!b.isUnwind()) {
157 				continue;
158 			}
159 			
160 			if (!b.unwindBlock) {
161 				b.unwindBlock = fbody.newBasicBlock(BuiltinName!"unwind");
162 			}
163 			
164 			return b.unwindBlock;
165 		}
166 		
167 		return BasicBlockRef();
168 	}
169 	
170 	BasicBlockRef makeNewBranch(Name name) {
171 		return fbody.newBasicBlock(name, getUnwindBlock());
172 	}
173 	
174 	BasicBlockRef startNewBranch(Name name) {
175 		return currentBlockRef = makeNewBranch(name);
176 	}
177 	
178 	BasicBlockRef maybeBranchToNewBlock(Location location, Name name) {
179 		if (currentBlockRef && currentBlock.empty) {
180 			auto unwindBlock = getUnwindBlock();
181 			currentBlock.landingpad = unwindBlock;
182 			return currentBlockRef;
183 		}
184 		
185 		return maybeBranchTo(location, currentBlockRef, startNewBranch(name));
186 	}
187 	
188 	BasicBlockRef maybeBranchTo(Location location, BasicBlockRef dst) {
189 		return maybeBranchTo(location, currentBlockRef, dst);
190 	}
191 	
192 	BasicBlockRef maybeBranchTo(
193 		Location location,
194 		BasicBlockRef src,
195 		BasicBlockRef dst,
196 	) {
197 		if (!doesTerminate(src)) {
198 			fbody[src].branch(location, dst);
199 		}
200 		
201 		return dst;
202 	}
203 	
204 	BasicBlockRef buildBlock(U...)(Location location, U args) {
205 		auto oldScope = currentScope;
206 		auto oldVarStack = varStack;
207 		scope(exit) {
208 			currentScope = oldScope;
209 			varStack = oldVarStack;
210 		}
211 		
212 		currentScope = new NestedScope(currentScope);
213 		
214 		auto unwindLevel = unwindActions.length;
215 		scope(success) unwindTo(unwindLevel);
216 		
217 		process(args);
218 		
219 		return currentBlockRef;
220 	}
221 	
222 	void process(Statement[] statements) {
223 		foreach(s; statements) {
224 			visit(s);
225 		}
226 	}
227 	
228 	void process(Statement s) {
229 		visit(s);
230 	}
231 	
232 	BasicBlockRef autoBlock(Statement s) {
233 		if (auto b = cast(BlockStatement) s) {
234 			return flatten(b);
235 		}
236 		
237 		return buildBlock(s.location, s);
238 	}
239 	
240 	void alloca(Variable v) {
241 		currentBlock.alloca(v.location, v);
242 		varStack ~= v;
243 		
244 		auto t = v.type.getCanonical();
245 		if (t.kind != TypeKind.Struct || t.dstruct.isPod) {
246 			return;
247 		}
248 		
249 		unwindActions ~= UnwindInfo(v);
250 		maybeBranchToNewBlock(v.location, BuiltinName!"");
251 	}
252 	
253 	Expression check(Expression e) {
254 		auto t = e.type;
255 		if (t.kind == TypeKind.Error) {
256 			import source.exception;
257 			throw new CompileException(t.error.location, t.error.message);
258 		}
259 		
260 		// FIXME: Update flags.
261 		return e;
262 	}
263 	
264 	auto buildExpression(AstExpression expr) {
265 		import d.semantic.expression;
266 		return check(ExpressionVisitor(pass).visit(expr));
267 	}
268 	
269 	auto buildExpression(AstExpression expr, Type type) {
270 		import d.semantic.caster, d.semantic.expression;
271 		return check(buildExplicitCast(
272 			pass,
273 			expr.location,
274 			type,
275 			ExpressionVisitor(pass).visit(expr),
276 		));
277 	}
278 	
279 	auto buildCondition(AstExpression expr) {
280 		return buildExpression(expr, Type.get(BuiltinType.Bool));
281 	}
282 	
283 	auto buildString(AstExpression expr) {
284 		return buildExpression(
285 			expr,
286 			Type.get(BuiltinType.Char).getSlice(TypeQualifier.Immutable),
287 		);
288 	}
289 	
290 public:
291 	void visit(BlockStatement b) {
292 		flatten(b);
293 	}
294 	
295 	void visit(ExpressionStatement s) {
296 		currentBlock.eval(s.location, buildExpression(s.expression));
297 	}
298 	
299 	void visit(DeclarationStatement s) {
300 		import d.semantic.declaration;
301 		auto syms = DeclarationVisitor(pass).flatten(s.declaration);
302 		
303 		scheduler.require(syms);
304 		
305 		foreach(sym; syms) {
306 			if (auto v = cast(Variable) sym) {
307 				alloca(v);
308 			} else if (cast(Aggregate) sym) {
309 				// FIXME: We should get rid of this.
310 				currentBlock.declare(sym.location, sym);
311 			}
312 		}
313 	}
314 	
315 	void visit(IdentifierStarNameStatement s) {
316 		import d.semantic.identifier;
317 		IdentifierResolver(pass)
318 			.build(s.identifier)
319 			.apply!(delegate void(identified) {
320 				alias T = typeof(identified);
321 				static if (is(T : Expression)) {
322 					assert(0, "expression identifier * identifier are not implemented.");
323 				} else static if (is(T : Type)) {
324 					auto t = identified.getPointer();
325 					
326 					import d.semantic.expression;
327 					import d.semantic.defaultinitializer : InitBuilder;
328 					auto value = s.value
329 						? ExpressionVisitor(pass).visit(s.value)
330 						: InitBuilder(pass, s.location).visit(t);
331 					
332 					import d.semantic.caster;
333 					auto v = new Variable(
334 						s.location,
335 						t.getParamType(ParamKind.Regular),
336 						s.name,
337 						buildImplicitCast(pass, s.location, t, value),
338 					);
339 					
340 					v.step = Step.Processed;
341 					pass.currentScope.addSymbol(v);
342 					
343 					currentBlock.alloca(s.location, v);
344 				} else {
345 					assert(0, "Was not expecting " ~ T.stringof);
346 				}
347 			})();
348 	}
349 	
350 	void visit(IfStatement s) {
351 		auto ifBlock = currentBlockRef;
352 		
353 		auto ifTrue = startNewBranch(BuiltinName!"then");
354 		auto mergeTrue = autoBlock(s.then);
355 		
356 		BasicBlockRef ifFalse, mergeBlock;
357 		if (s.elseStatement) {
358 			ifFalse = startNewBranch(BuiltinName!"else");
359 			autoBlock(s.elseStatement);
360 			if (!terminate) {
361 				mergeBlock = maybeBranchToNewBlock(
362 					s.elseStatement.location,
363 					BuiltinName!"endif",
364 				);
365 			}
366 		} else {
367 			ifFalse = mergeBlock = startNewBranch(BuiltinName!"endif");
368 		}
369 		
370 		if (!doesTerminate(mergeTrue)) {
371 			if (!mergeBlock) {
372 				mergeBlock = startNewBranch(BuiltinName!"endif");
373 			}
374 			
375 			maybeBranchTo(s.then.location, mergeTrue, mergeBlock);
376 		}
377 		
378 		fbody[ifBlock].branch(
379 			s.location,
380 			buildCondition(s.condition),
381 			ifTrue,
382 			ifFalse,
383 		);
384 	}
385 	
386 	void genLoop(
387 		Location location,
388 		Expression condition,
389 		Statement statement,
390 		Expression increment,
391 		Variable element = null,
392 		bool skipFirstCond = false,
393 	) {
394 		auto oldBreakKind = breakKind;
395 		auto oldBreakLabel = breakLabel;
396 		auto oldContinueLabel = continueLabel;
397 		scope(exit) {
398 			currentBlockRef = breakLabel.block;
399 			
400 			breakKind = oldBreakKind;
401 			breakLabel = oldBreakLabel;
402 			continueLabel = oldContinueLabel;
403 		}
404 		
405 		breakKind = BreakKind.Loop;
406 		
407 		auto entryBlock = currentBlockRef;
408 		auto incBlock = startNewBranch(BuiltinName!"loop.continue");
409 		if (increment) {
410 			currentBlock.eval(increment.location, increment);
411 		}
412 		
413 		auto testBlock = maybeBranchToNewBlock(location, BuiltinName!"loop.test");
414 		auto bodyBlock = startNewBranch(BuiltinName!"loop.body");
415 		
416 		continueLabel = Label(incBlock, cast(uint) unwindActions.length);
417 		breakLabel = Label(
418 			BasicBlockRef.init,
419 			cast(uint) unwindActions.length,
420 		);
421 		
422 		if (element !is null) {
423 			currentBlock.alloca(element.location, element);
424 		}
425 		
426 		autoBlock(statement);
427 		maybeBranchTo(location, incBlock);
428 		
429 		fbody[entryBlock].branch(location, skipFirstCond ? bodyBlock : testBlock);
430 		if (condition) {
431 			auto breakLabel = getBreakLabel(location);
432 			fbody[testBlock].branch(
433 				location,
434 				condition,
435 				bodyBlock,
436 				breakLabel.block,
437 			);
438 		} else {
439 			fbody[testBlock].branch(location, bodyBlock);
440 		}
441 	}
442 	
443 	void genLoop(
444 		Location location,
445 		AstExpression condition,
446 		Statement statement,
447 		AstExpression increment = null,
448 		Variable element = null,
449 		bool skipFirstCond = false,
450 	) {
451 		Expression cond, inc;
452 		
453 		if (condition) {
454 			cond = buildCondition(condition);
455 		}
456 		
457 		if (increment) {
458 			inc = buildExpression(increment);
459 		}
460 		
461 		genLoop(location, cond, statement, inc, element, skipFirstCond);
462 	}
463 	
464 	void visit(WhileStatement w) {
465 		genLoop(w.location, w.condition, w.statement);
466 	}
467 	
468 	void visit(DoWhileStatement w) {
469 		genLoop(w.location, w.condition, w.statement, null, null, true);
470 	}
471 	
472 	void visit(ForStatement f) {
473 		buildBlock(f.location, f);
474 	}
475 	
476 	void process(ForStatement f) {
477 		if (f.initialize) {
478 			visit(f.initialize);
479 		}
480 		
481 		genLoop(f.location, f.condition, f.statement, f.increment);
482 	}
483 	
484 	void visit(ForeachStatement f) {
485 		buildBlock(f.location, f);
486 	}
487 	
488 	void process(ForeachStatement f) {
489 		assert(!f.reverse, "foreach_reverse not supported at this point.");
490 		
491 		import d.semantic.expression;
492 		auto iterated = ExpressionVisitor(pass).visit(f.iterated);
493 		
494 		import source.name, d.semantic.identifier;
495 		auto length = IdentifierResolver(pass)
496 			.buildIn(iterated.location, iterated, BuiltinName!"length")
497 			.apply!(delegate Expression(e) {
498 				static if (is(typeof(e) : Expression)) {
499 					return e;
500 				} else {
501 					import d.ir.error;
502 					return new CompileError(
503 						iterated.location,
504 						typeid(e).toString() ~ " is not a valid length",
505 					).expression;
506 				}
507 			})();
508 		
509 		Variable idx;
510 		
511 		auto loc = f.location;
512 		switch (f.tupleElements.length) {
513 			case 1:
514 				import d.semantic.defaultinitializer;
515 				idx = new Variable(
516 					loc,
517 					length.type,
518 					BuiltinName!"",
519 					InitBuilder(pass, loc).visit(length.type),
520 				);
521 				
522 				idx.step = Step.Processed;
523 				break;
524 			
525 			case 2:
526 				auto idxDecl = f.tupleElements[0];
527 				if (idxDecl.type.paramKind != ParamKind.Regular) {
528 					assert(0, "index can't be ref");
529 				}
530 				
531 				import d.semantic.type;
532 				auto t = idxDecl.type.getType().isAuto
533 					? length.type
534 					: TypeVisitor(pass).visit(idxDecl.type.getType());
535 				
536 				auto idxLoc = idxDecl.location;
537 				
538 				import d.semantic.defaultinitializer;
539 				idx = new Variable(
540 					idxLoc,
541 					t,
542 					idxDecl.name,
543 					InitBuilder(pass, idxLoc).visit(t),
544 				);
545 				
546 				idx.step = Step.Processed;
547 				currentScope.addSymbol(idx);
548 				
549 				break;
550 			
551 			default:
552 				assert(0, "Wrong number of elements");
553 		}
554 		
555 		assert(idx);
556 		currentBlock.alloca(idx.location, idx);
557 		
558 		auto idxExpr = new VariableExpression(idx.location, idx);
559 		auto increment = build!UnaryExpression(
560 			loc,
561 			idx.type,
562 			UnaryOp.PreInc,
563 			idxExpr,
564 		);
565 		
566 		import d.semantic.caster;
567 		length = buildImplicitCast(pass, idx.location, idx.type, length);
568 		auto condition = build!ICmpExpression(loc, ICmpOp.Less, idxExpr, length);
569 		
570 		auto iType = iterated.type.getCanonical();
571 		assert(iType.hasElement, "Only array and slice are supported for now.");
572 		
573 		Type et = iType.element;
574 		
575 		auto eDecl = f.tupleElements[$ - 1];
576 		auto eLoc = eDecl.location;
577 		
578 		import d.semantic.expression;
579 		auto eVal = ExpressionVisitor(pass).getIndex(eLoc, iterated, idxExpr);
580 		auto eType = eVal.type.getParamType(eDecl.type.paramKind);
581 		
582 		if (!eDecl.type.getType().isAuto) {
583 			import d.semantic.type;
584 			eType = TypeVisitor(pass).visit(eDecl.type);
585 			
586 			import d.semantic.caster;
587 			eVal = buildImplicitCast(pass, eLoc, eType.getType(), eVal);
588 		}
589 		
590 		auto element = new Variable(eLoc, eType, eDecl.name, eVal);
591 		element.step = Step.Processed;
592 		currentScope.addSymbol(element);
593 		
594 		genLoop(loc, condition, f.statement, increment, element);
595 	}
596 	
597 	void visit(ForeachRangeStatement f) {
598 		buildBlock(f.location, f);
599 	}
600 	
601 	void process(ForeachRangeStatement f) {
602 		import d.semantic.expression;
603 		auto start = ExpressionVisitor(pass).visit(f.start);
604 		auto stop  = ExpressionVisitor(pass).visit(f.stop);
605 		
606 		assert(f.tupleElements.length == 1, "Wrong number of elements");
607 		auto iDecl = f.tupleElements[0];
608 		
609 		auto loc = f.location;
610 		
611 		import d.semantic.type, d.semantic.typepromotion;
612 		auto type = iDecl.type.getType().isAuto
613 			? getPromotedType(pass, loc, start.type, stop.type)
614 			: TypeVisitor(pass).visit(iDecl.type).getType();
615 		
616 		import d.semantic.caster;
617 		start = buildImplicitCast(pass, start.location, type, start);
618 		stop  = buildImplicitCast(pass, stop.location, type, stop);
619 		
620 		if (f.reverse) {
621 			auto tmp = start;
622 			start = stop;
623 			stop = tmp;
624 		}
625 		
626 		auto idx = new Variable(
627 			iDecl.location,
628 			type.getParamType(iDecl.type.paramKind),
629 			iDecl.name,
630 			start,
631 		);
632 		
633 		idx.step = Step.Processed;
634 		currentScope.addSymbol(idx);
635 		currentBlock.alloca(idx.location, idx);
636 		
637 		Expression idxExpr = new VariableExpression(idx.location, idx);
638 		Expression increment, condition;
639 		
640 		if (f.reverse) {
641 			// for(...; idx-- > stop; idx)
642 			condition = build!ICmpExpression(
643 				loc,
644 				ICmpOp.Greater,
645 				build!UnaryExpression(loc, type, UnaryOp.PostDec, idxExpr),
646 				stop,
647 			);
648 		} else {
649 			// for(...; idx < stop; idx++)
650 			condition = build!ICmpExpression(loc, ICmpOp.Less, idxExpr, stop);
651 			increment = build!UnaryExpression(loc, type, UnaryOp.PreInc, idxExpr);
652 		}
653 		
654 		genLoop(loc, condition, f.statement, increment);
655 	}
656 	
657 	void visit(ReturnStatement s) {
658 		// TODO: precompute autotype instead of managing it here.
659 		auto rt = returnType.getType();
660 		auto isAutoReturn =
661 			rt.kind == TypeKind.Builtin &&
662 			rt.qualifier == TypeQualifier.Mutable &&
663 			rt.builtin == BuiltinType.None;
664 		
665 		// return; has no value.
666 		if (s.value is null) {
667 			if (isAutoReturn) {
668 				returnType = Type.get(BuiltinType.Void)
669 					.getParamType(ParamKind.Regular);
670 			}
671 			
672 			closeBlockTo(0);
673 			currentBlock.ret(s.location);
674 			return;
675 		}
676 		
677 		auto value = buildExpression(s.value);
678 		
679 		// TODO: Handle auto return by specifying it to this visitor
680 		// instead of deducing it in dubious ways.
681 		if (isAutoReturn) {
682 			// TODO: auto ref return.
683 			returnType = value.type.getParamType(ParamKind.Regular);
684 		} else {
685 			import d.semantic.caster;
686 			value = buildImplicitCast(pass, s.location, returnType.getType(), value);
687 			if (returnType.isRef) {
688 				if (!value.isLvalue) {
689 					import source.exception;
690 					throw new CompileException(s.location, "Cannot ref return lvalues");
691 				}
692 				
693 				value = build!UnaryExpression(
694 					s.location,
695 					value.type.getPointer(),
696 					UnaryOp.AddressOf,
697 					value,
698 				);
699 			}
700 		}
701 		
702 		// If unwind work is needed, store the result in a temporary.
703 		if (unwindActions.length) {
704 			auto location = value.location;
705 			if (retval is null) {
706 				auto v = new Variable(
707 					location,
708 					value.type,
709 					BuiltinName!"return",
710 					new VoidInitializer(location, value.type),
711 				);
712 				
713 				v.step = Step.Processed;
714 				retval = new VariableExpression(location, v);
715 			}
716 			
717 			currentBlock.eval(location, check(build!BinaryExpression(
718 				location,
719 				retval.type,
720 				BinaryOp.Assign,
721 				retval,
722 				value,
723 			)));
724 			
725 			value = retval;
726 		}
727 		
728 		closeBlockTo(0);
729 		if (!terminate) {
730 			currentBlock.ret(s.location, check(value));
731 		}
732 	}
733 	
734 	private BasicBlockRef unwindAndBranch(Location location, Label l) in {
735 		assert(l, "Invalid label");
736 	} do {
737 		closeBlockTo(l.level);
738 		return maybeBranchTo(location, l.block);
739 	}
740 	
741 	Label getBreakLabel(Location location) {
742 		if (breakLabel) {
743 			return breakLabel;
744 		}
745 		
746 		Name name;
747 		final switch(breakKind) with(BreakKind) {
748 			case None:
749 				import source.exception;
750 				throw new CompileException(
751 					location,
752 					"Cannot break outside of switches and loops",
753 				);
754 			
755 			case Loop:
756 				name = BuiltinName!"loop.exit";
757 				break;
758 			
759 			case Switch:
760 				name = BuiltinName!"endswitch";
761 				break;
762 		}
763 		
764 		breakLabel.block = makeNewBranch(name);
765 		return breakLabel;
766 	}
767 	
768 	void visit(BreakStatement s) {
769 		unwindAndBranch(s.location, getBreakLabel(s.location));
770 	}
771 	
772 	void visit(ContinueStatement s) {
773 		if (!continueLabel) {
774 			import source.exception;
775 			throw new CompileException(
776 				s.location,
777 				"Cannot continue outside of loops",
778 			);
779 		}
780 		
781 		unwindAndBranch(s.location, continueLabel);
782 	}
783 	
784 	void visit(SwitchStatement s) {
785 		auto oldBreakKind = breakKind;
786 		auto oldBreakLabel = breakLabel;
787 		auto oldCases = cases;
788 		auto oldSwitchStack = switchStack;
789 		
790 		Label oldDefault;
791 		if (auto dPtr = BuiltinName!"default" in labels) {
792 			oldDefault = *dPtr;
793 			labels.remove(BuiltinName!"default");
794 		}
795 		
796 		scope(exit) {
797 			currentBlockRef = breakLabel.block;
798 			
799 			breakKind = oldBreakKind;
800 			breakLabel = oldBreakLabel;
801 			cases = oldCases;
802 			switchStack = oldSwitchStack;
803 			
804 			if (oldDefault.block) {
805 				labels[BuiltinName!"default"] = oldDefault;
806 			}
807 		}
808 		
809 		switchStack = varStack;
810 		breakKind = BreakKind.Switch;
811 		
812 		auto switchBlock = currentBlockRef;
813 		
814 		cases = [CaseEntry.init];
815 		
816 		breakLabel = Label(
817 			BasicBlockRef.init,
818 			cast(uint) unwindActions.length,
819 		);
820 		
821 		currentBlockRef = null;
822 		visit(s.statement);
823 		
824 		if (!terminate) {
825 			unwindAndBranch(s.location, getBreakLabel(s.location));
826 		}
827 		
828 		if (BuiltinName!"case" in inFlightGotos) {
829 			import source.exception;
830 			throw new CompileException(
831 				s.location,
832 				"Reached end of switch statement with unresolved goto case;",
833 			);
834 		}
835 		
836 		BasicBlockRef defaultBlock;
837 		if (auto defaultLabel = BuiltinName!"default" in labels) {
838 			defaultBlock = defaultLabel.block;
839 			labels.remove(BuiltinName!"default");
840 		} else {
841 			import source.exception;
842 			throw new CompileException(
843 				s.location,
844 				"switch statement without a default; use 'final switch' "
845 				~ "or add 'default: assert(0);' or add 'default: break;'",
846 			);
847 		}
848 		
849 		auto v = buildExpression(s.expression);
850 		
851 		auto switchTable = cast(SwitchTable*) cases.ptr;
852 		switchTable.entryCount = cast(uint) (cases.length - 1);
853 		switchTable.defaultBlock = defaultBlock;
854 		
855 		fbody[switchBlock].doSwitch(s.location, v, switchTable);
856 	}
857 	
858 	private void fixupGoto(Location location, Name name, Label label) {
859 		if (auto ifgsPtr = name in inFlightGotos) {
860 			auto ifgs = *ifgsPtr;
861 			inFlightGotos.remove(name);
862 			
863 			foreach(ifg; ifgs) {
864 				auto oldCurrentBlock = currentBlockRef;
865 				auto oldunwindActions = unwindActions;
866 				scope(exit) {
867 					currentBlockRef = oldCurrentBlock;
868 					unwindActions = oldunwindActions;
869 				}
870 				
871 				currentBlockRef = ifg.block;
872 				unwindActions = ifg.unwind;
873 				
874 				unwindAndBranch(location, label);
875 			}
876 			
877 			scope(success) inFlightGotosStacks.remove(name);
878 			foreach(igs; inFlightGotosStacks[name]) {
879 				// Check that all inflight goto to thta label are valid.
880 				import std.algorithm.searching;
881 				bool isValid = igs.startsWith(varStack);
882 				if (!isValid) {
883 					import source.exception;
884 					throw new CompileException(
885 						location,
886 						"Cannot jump over variable initialization.",
887 					);
888 				}
889 			}
890 		}
891 	}
892 	
893 	private void setCaseEntry(
894 		Location location,
895 		string switchError,
896 		string fallthroughError,
897 	) {
898 		scope(success) {
899 			varStack = switchStack;
900 		}
901 		
902 		if (cases.length == 0) {
903 			import source.exception;
904 			throw new CompileException(location, switchError);
905 		}
906 		
907 		if (terminate) {
908 			return;
909 		}
910 		
911 		// Check for case a: case b:
912 		// TODO: consider default: case a:
913 		if (cases[$ - 1].block != currentBlockRef || !currentBlock.empty) {
914 			import source.exception;
915 			throw new CompileException(location, fallthroughError);
916 		}
917 	}
918 	
919 	void visit(CaseStatement s) {
920 		setCaseEntry(
921 			s.location,
922 			"Case statement can only appear within switch statement.",
923 			"Fallthrough is disabled, use goto case.",
924 		);
925 		
926 		auto caseBlock = maybeBranchToNewBlock(s.location, BuiltinName!"case");
927 		fixupGoto(
928 			s.location,
929 			BuiltinName!"case",
930 			Label(caseBlock, cast(uint) unwindActions.length),
931 		);
932 		
933 		foreach (e; s.cases) {
934 			auto c = cast(uint) evalIntegral(buildExpression(e));
935 			cases ~= CaseEntry(caseBlock, c);
936 		}
937 		
938 		visit(s.statement);
939 	}
940 	
941 	void visit(LabeledStatement s) {
942 		auto name = s.label;
943 		if (name == BuiltinName!"default") {
944 			setCaseEntry(
945 				s.location,
946 				"Default statement can only appear within switch statement.",
947 				"Fallthrough is disabled, use goto default.",
948 			);
949 		}
950 		
951 		if (name in labels) {
952 			import source.exception;
953 			throw new CompileException(s.location, "Label is already defined");
954 		}
955 		
956 		auto labelBlock = maybeBranchToNewBlock(s.location, name);
957 		auto label = Label(labelBlock, cast(uint) unwindActions.length);
958 		labels[name] = label;
959 		labelStacks[name] = varStack;
960 		
961 		fixupGoto(s.location, name, label);
962 		visit(s.statement);
963 	}
964 	
965 	void visit(GotoStatement s) {
966 		auto name = s.label;
967 		if (auto bPtr = name in labelStacks) {
968 			auto labelStack = *bPtr;
969 			
970 			import std.algorithm.searching;
971 			bool isValid = varStack.startsWith(labelStack);
972 			if (!isValid) {
973 				import source.exception;
974 				throw new CompileException(
975 					s.location,
976 					"Cannot goto over variable initialization.",
977 				);
978 			}
979 		}
980 		
981 		if (auto bPtr = name in inFlightGotosStacks) {
982 			auto varStacks = *bPtr;
983 			varStacks ~= varStack;
984 			*bPtr = varStacks;
985 		} else {
986 			inFlightGotosStacks[name] = [varStack];
987 		}
988 		
989 		if (auto bPtr = name in labels) {
990 			unwindAndBranch(s.location, *bPtr);
991 			return;
992 		}
993 		
994 		auto unresolvedGoto = UnresolvedGoto(unwindActions, currentBlockRef);
995 		if (auto bPtr = name in inFlightGotos) {
996 			*bPtr ~= unresolvedGoto;
997 		} else {
998 			inFlightGotos[name] = [unresolvedGoto];
999 		}
1000 		
1001 		currentBlockRef = null;
1002 	}
1003 	
1004 	void visit(ScopeStatement s) {
1005 		unwindActions ~= UnwindInfo(s.kind, s.statement);
1006 		maybeBranchToNewBlock(s.location, BuiltinName!"scope.entry");
1007 	}
1008 	
1009 	void visit(AssertStatement s) {
1010 		Expression msg;
1011 		if (s.message) {
1012 			msg = buildString(s.message);
1013 		}
1014 		
1015 		bool isHalt;
1016 		if (auto b = cast(BooleanLiteral) s.condition) {
1017 			isHalt = !b.value;
1018 		} else if (auto i = cast(IntegerLiteral) s.condition) {
1019 			isHalt = !i.value;
1020 		} else if (auto n = cast(NullLiteral) s.condition) {
1021 			isHalt = true;
1022 		}
1023 		
1024 		if (isHalt) {
1025 			currentBlock.halt(s.location, msg);
1026 			return;
1027 		}
1028 		
1029 		auto testBlock = currentBlockRef;
1030 		auto failBlock = startNewBranch(BuiltinName!"assert.fail");
1031 		currentBlock.halt(s.location, msg);
1032 		
1033 		auto successBlock = startNewBranch(BuiltinName!"assert.success");
1034 		fbody[testBlock].branch(
1035 			s.location,
1036 			buildCondition(s.condition),
1037 			successBlock,
1038 			failBlock,
1039 		);
1040 	}
1041 	
1042 	void visit(ThrowStatement s) {
1043 		currentBlock.doThrow(s.location, buildExpression(
1044 			s.value,
1045 			Type.get(pass.object.getThrowable()),
1046 		));
1047 	}
1048 	
1049 	void visit(TryStatement s) {
1050 		auto unwindLevel = unwindActions.length;
1051 		scope(success) unwindTo(unwindLevel);
1052 		
1053 		if (s.finallyBlock) {
1054 			unwindActions ~= UnwindInfo(ScopeKind.Exit, s.finallyBlock);
1055 		}
1056 		
1057 		// No cath blocks, just bypass the whole thing.
1058 		if (s.catches.length == 0) {
1059 			maybeBranchToNewBlock(s.location, BuiltinName!"try");
1060 			autoBlock(s.statement);
1061 			return;
1062 		}
1063 		
1064 		auto preCatchLevel = unwindActions.length;
1065 		unwindActions ~= UnwindInfo(ScopeKind.Failure, null);
1066 		
1067 		maybeBranchToNewBlock(s.location, BuiltinName!"try");
1068 		autoBlock(s.statement);
1069 		
1070 		auto preUnwindBlock = currentBlockRef;
1071 		assert(unwindActions[$ - 1].kind == UnwindKind.Failure);
1072 		assert(unwindActions[$ - 1].statement is null);
1073 		
1074 		auto catchSwitchBlock = unwindActions[$ - 1].unwindBlock;
1075 		assert(catchSwitchBlock, "No catch switch block");
1076 		
1077 		unwindActions = unwindActions[0 .. preCatchLevel];
1078 		
1079 		CatchPad[] catchpads;
1080 		catchpads.reserve(s.catches.length + 1);
1081 		catchpads ~= CatchPad();
1082 		
1083 		BasicBlockRef endCatchBlock;
1084 		foreach(c; s.catches) {
1085 			import d.semantic.identifier;
1086 			auto type = IdentifierResolver(pass)
1087 				.resolve(c.type)
1088 				.apply!(function Class(identified) {
1089 					alias T = typeof(identified);
1090 					static if (is(T : Symbol)) {
1091 						if (auto c = cast(Class) identified) {
1092 							return c;
1093 						}
1094 					}
1095 					
1096 					static if (is(T : Type)) {
1097 						assert(0);
1098 					} else {
1099 						import source.exception;
1100 						throw new CompileException(
1101 							identified.location,
1102 							typeid(identified).toString() ~ " is not a class.",
1103 						);
1104 					}
1105 				})();
1106 			
1107 			auto catchBlock = startNewBranch(BuiltinName!"catch");
1108 			catchpads ~= CatchPad(type, catchBlock);
1109 			
1110 			auto mergeCatchBlock = autoBlock(c.statement);
1111 			if (terminate) {
1112 				continue;
1113 			}
1114 			
1115 			if (!endCatchBlock) {
1116 				endCatchBlock = startNewBranch(BuiltinName!"endcatch");
1117 			}
1118 			
1119 			maybeBranchTo(c.location, mergeCatchBlock, endCatchBlock);
1120 		}
1121 		
1122 		auto catchTable = cast(CatchTable*) catchpads.ptr;
1123 		catchTable.catchCount = catchpads.length - 1;
1124 		fbody[catchSwitchBlock].doCatch(s.location, catchTable);
1125 		
1126 		if (doesTerminate(preUnwindBlock)) {
1127 			currentBlockRef = endCatchBlock;
1128 			return;
1129 		}
1130 		
1131 		if (!endCatchBlock) {
1132 			endCatchBlock = startNewBranch(BuiltinName!"endcatch");
1133 		}
1134 		
1135 		maybeBranchTo(s.location, preUnwindBlock, endCatchBlock);
1136 		currentBlockRef = endCatchBlock;
1137 	}
1138 	
1139 	void visit(StaticIf!Statement s) {
1140 		auto items = evalIntegral(buildCondition(s.condition))
1141 			? s.items
1142 			: s.elseItems;
1143 		
1144 		foreach(item; items) {
1145 			visit(item);
1146 		}
1147 		
1148 		// Do not error on unrechable statement after static if.
1149 		allowUnreachable = true;
1150 	}
1151 	
1152 	void visit(StaticAssert!Statement s) {
1153 		if (evalIntegral(buildCondition(s.condition))) {
1154 			return;
1155 		}
1156 		
1157 		import source.exception;
1158 		if (s.message is null) {
1159 			throw new CompileException(s.location, "assertion failure");
1160 		}
1161 		
1162 		auto msg = evalString(buildString(s.message));
1163 		throw new CompileException(s.location, "assertion failure: " ~ msg);
1164 	}
1165 	
1166 	void visit(Mixin!Statement s) {
1167 		auto str = evalString(buildString(s.value)) ~ '\0';
1168 		auto base = context.registerMixin(s.location, str);
1169 		
1170 		import source.dlexer;
1171 		auto trange = lex(base, context);
1172 		
1173 		import d.parser.base;
1174 		trange.match(TokenType.Begin);
1175 		while(trange.front.type != TokenType.End) {
1176 			import d.parser.statement;
1177 			visit(trange.parseStatement());
1178 		}
1179 	}
1180 	
1181 	void destroy(Variable v) {
1182 		maybeBranchToNewBlock(v.location, BuiltinName!"destroy");
1183 		currentBlock.destroy(v.location, v);
1184 	}
1185 	
1186 	/**
1187 	 * Unwinding facilities
1188 	 */
1189 	void closeBlockTo(size_t level) {
1190 		auto oldunwindActions = unwindActions;
1191 		scope(exit) unwindActions = oldunwindActions;
1192 		
1193 		while(unwindActions.length > level) {
1194 			if (terminate) {
1195 				break;
1196 			}
1197 			
1198 			auto b = unwindActions[$ - 1];
1199 			unwindActions = unwindActions[0 .. $ - 1];
1200 			
1201 			final switch(b.kind) with(UnwindKind) {
1202 				case Success, Exit:
1203 					maybeBranchToNewBlock(Location.init, BuiltinName!"cleanup");
1204 					autoBlock(b.statement);
1205 					break;
1206 				
1207 				case Failure:
1208 					continue;
1209 				
1210 				case Destroy:
1211 					destroy(b.var);
1212 					break;
1213 			}
1214 		}
1215 	}
1216 	
1217 	void concludeUnwind(Location location) {
1218 		if (terminate) {
1219 			return;
1220 		}
1221 		
1222 		foreach_reverse(ref b; unwindActions) {
1223 			if (!b.isUnwind()) {
1224 				continue;
1225 			}
1226 			
1227 			if (!b.unwindBlock) {
1228 				b.unwindBlock = makeNewBranch(BuiltinName!"unwind");
1229 			}
1230 			
1231 			currentBlock.branch(location, b.unwindBlock);
1232 			return;
1233 		}
1234 		
1235 		if (!terminate) {
1236 			currentBlock.doThrow(location);
1237 		}
1238 	}
1239 	
1240 	void unwindTo(size_t level) in {
1241 		assert(unwindActions.length >= level);
1242 	} do {
1243 		if (unwindActions.length == level) {
1244 			// Nothing to unwind, done !
1245 			return;
1246 		}
1247 		
1248 		closeBlockTo(level);
1249 		
1250 		auto preUnwindBlock = currentBlockRef;
1251 		scope(exit) currentBlockRef = preUnwindBlock;
1252 		
1253 		auto i = unwindActions.length;
1254 		while (i --> level) {
1255 			auto bPtr = &unwindActions[i];
1256 			auto b = *bPtr;
1257 			if (!b.isUnwind()) {
1258 				continue;
1259 			}
1260 			
1261 			unwindActions = unwindActions[0 .. i];
1262 			
1263 			// We encountered a scope statement that
1264 			// can be reached while unwinding.
1265 			scope(success) concludeUnwind(Location.init);
1266 			
1267 			// Emit the exception cleanup code.
1268 			currentBlockRef = b.unwindBlock;
1269 			final switch(b.kind) with(UnwindKind) {
1270 				case Success:
1271 					assert(0);
1272 				
1273 				case Exit:
1274 					autoBlock(b.statement);
1275 					break;
1276 				
1277 				case Failure:
1278 					assert(
1279 						b.statement !is null,
1280 						"Catch blocks must be handled with try statements",
1281 					);
1282 					
1283 					goto case Exit;
1284 				
1285 				case Destroy:
1286 					destroy(b.var);
1287 					break;
1288 			}
1289 		}
1290 		
1291 		if (unwindActions.length != level) {
1292 			foreach (b; unwindActions[level .. $]) {
1293 				assert(!b.isUnwind());
1294 			}
1295 			
1296 			unwindActions = unwindActions[0 .. level];
1297 			concludeUnwind(Location.init);
1298 		}
1299 		
1300 		if (!terminate) {
1301 			maybeBranchToNewBlock(Location.init, BuiltinName!"resume");
1302 		}
1303 	}
1304 }
1305 
1306 private:
1307 
1308 enum BreakKind {
1309 	None,
1310 	Loop,
1311 	Switch,
1312 }
1313 
1314 struct UnwindInfo {
1315 private:
1316 	import std.bitmanip;
1317 	mixin(taggedClassRef!(
1318 		Statement, "_statement",
1319 		UnwindKind, "_kind", 2,
1320 	));
1321 	
1322 public:
1323 	@property kind() const {
1324 		return _kind;
1325 	}
1326 	
1327 	@property statement() inout in {
1328 		assert(kind != UnwindKind.Destroy);
1329 	} do {
1330 		return _statement;
1331 	}
1332 	
1333 	@property var() inout in {
1334 		assert(kind == UnwindKind.Destroy);
1335 	} do {
1336 		auto s = _statement;
1337 		return *(cast(Variable*) &s);
1338 	}
1339 	
1340 	BasicBlockRef unwindBlock;
1341 	BasicBlockRef cleanupBlock;
1342 	
1343 	this(ScopeKind kind, Statement statement) {
1344 		_kind = cast(UnwindKind) kind;
1345 		_statement = statement;
1346 	}
1347 	
1348 	this(Variable v) in {
1349 		assert(!v.type.dstruct.isPod);
1350 	} do {
1351 		_kind = UnwindKind.Destroy;
1352 		_statement = *(cast(Statement*) &v);
1353 	}
1354 	
1355 	bool isCleanup() const {
1356 		return .isCleanup(kind);
1357 	}
1358 	
1359 	bool isUnwind() const {
1360 		return .isUnwind(kind);
1361 	}
1362 }
1363 
1364 enum UnwindKind {
1365 	Success,
1366 	Exit,
1367 	Failure,
1368 	Destroy,
1369 }
1370 
1371 bool isCleanup(UnwindKind k) {
1372 	return k != UnwindKind.Failure;
1373 }
1374 
1375 bool isUnwind(UnwindKind k) {
1376 	return k != UnwindKind.Success;
1377 }
1378 
1379 unittest {
1380 	assert(isCleanup(UnwindKind.Success));
1381 	assert(isCleanup(UnwindKind.Exit));
1382 	assert(!isCleanup(UnwindKind.Failure));
1383 	assert(isCleanup(UnwindKind.Destroy));
1384 	
1385 	assert(!isUnwind(UnwindKind.Success));
1386 	assert(isUnwind(UnwindKind.Exit));
1387 	assert(isUnwind(UnwindKind.Failure));
1388 	assert(isUnwind(UnwindKind.Destroy));
1389 	
1390 	import std.conv;
1391 	static assert(UnwindKind.Exit.asOriginalType() == ScopeKind.Exit.asOriginalType());
1392 	static assert(UnwindKind.Success.asOriginalType() == ScopeKind.Success.asOriginalType());
1393 	static assert(UnwindKind.Failure.asOriginalType() == ScopeKind.Failure.asOriginalType());
1394 }