1 module d.semantic.declaration;
2 
3 import d.semantic.semantic;
4 
5 import d.ast.conditional;
6 import d.ast.declaration;
7 
8 import d.ir.dscope;
9 import d.ir.symbol;
10 import d.ir.type;
11 
12 alias Module = d.ir.symbol.Module;
13 
14 // Conflict with Interface in object.di
15 alias Interface = d.ir.symbol.Interface;
16 
17 enum AggregateType {
18 	None,
19 	Union,
20 	Struct,
21 	Class,
22 }
23 
24 struct DeclarationVisitor {
25 	private SemanticPass pass;
26 	alias pass this;
27 	
28 	alias Step = SemanticPass.Step;
29 	
30 	uint fieldIndex;
31 	uint methodIndex;
32 	
33 	CtUnit[] ctUnits;
34 	ConditionalBranch[] cdBranches;
35 	
36 	private {
37 		import std.bitmanip;
38 		mixin(bitfields!(
39 			Linkage, "linkage", 3,
40 			Visibility, "visibility", 3,
41 			Storage, "storage", 2,
42 			AggregateType, "aggregateType", 2,
43 			CtUnitLevel, "ctLevel", 2,
44 			InTemplate, "inTemplate", 1,
45 			bool, "addThis", 1,
46 			bool, "addContext", 1,
47 			bool, "isRef", 1,
48 			bool, "isOverride", 1,
49 			bool, "isAbstract", 1,
50 			bool, "isProperty", 1,
51 			bool, "isNoGC", 1,
52 			uint, "", 12,
53 		));
54 	}
55 	
56 	static assert(DeclarationVisitor.init.linkage == Linkage.D);
57 	static assert(DeclarationVisitor.init.visibility == Visibility.Private);
58 	static assert(DeclarationVisitor.init.storage == Storage.Local);
59 	static assert(DeclarationVisitor.init.aggregateType == AggregateType.None);
60 	static assert(DeclarationVisitor.init.addThis == false);
61 	static assert(DeclarationVisitor.init.addContext == false);
62 	
63 	this(SemanticPass pass) {
64 		this.pass = pass;
65 	}
66 	
67 	Symbol[] flatten(S)(
68 		Declaration[] decls,
69 		S dscope,
70 	) if (is(S : Symbol) && is(S : Scope)) {
71 		static assert(
72 			!is(S : Class),
73 			"Classes need to have fieldIndex and methodIndex",
74 		);
75 		
76 		uint fi = is(S : Aggregate) ? dscope.hasContext : 0;
77 		return flattenImpl(decls, dscope, fi, 0);
78 	}
79 	
80 	Symbol[] flatten(
81 		Declaration[] decls,
82 		Class c,
83 		uint fieldIndex,
84 		uint methodIndex,
85 	) {
86 		return flattenImpl(decls, c, fieldIndex, methodIndex);
87 	}
88 	
89 	private Symbol[] flattenImpl(S)(
90 		Declaration[] decls,
91 		S dscope,
92 		uint fieldIndex,
93 		uint methodIndex,
94 	) if (is(S : Symbol) && is(S : Scope)) {
95 		visibility = Visibility.Public;
96 		
97 		aggregateType =
98 			  is(S : Class) ? AggregateType.Class
99 			: is(S : Struct) ? AggregateType.Struct
100 			: is(S : Union) ? AggregateType.Union
101 			: AggregateType.None;
102 		
103 		linkage = dscope.linkage;
104 		
105 		// What a mess !
106 		if (aggregateType > AggregateType.None) {
107 			inTemplate = dscope.inTemplate;
108 			addThis = true;
109 		}
110 		
111 		static if (is(S : Module)) {
112 			storage = Storage.Static;
113 		}
114 		
115 		static if (is(S : TemplateInstance)) {
116 			inTemplate = InTemplate.Yes;
117 			addThis = dscope.hasThis;
118 			addContext = dscope.hasContext;
119 			storage = dscope.storage;
120 		}
121 		
122 		this.fieldIndex = fieldIndex;
123 		this.methodIndex = methodIndex;
124 		
125 		auto oldScope = currentScope;
126 		scope(exit) currentScope = oldScope;
127 		currentScope = dscope;
128 		
129 		auto ctus = flattenDecls(decls);
130 		dscope.step = Step.Populated;
131 		
132 		dscope.setPoisoningMode();
133 		scope(exit) dscope.clearPoisoningMode();
134 		
135 		return DeclarationFlattener!S(&this, dscope).lowerToSymbols(ctus);
136 	}
137 	
138 	// FIXME: Pass the function down here.
139 	Symbol[] flatten(Declaration d) {
140 		addContext = true;
141 		
142 		import std.range;
143 		auto ctus = flattenDecls(only(d));
144 		assert(ctus.length == 1);
145 		
146 		auto u = ctus[0];
147 		assert(u.level == CtUnitLevel.Done);
148 		assert(u.type == CtUnitType.Symbols);
149 		
150 		import std.algorithm, std.range;
151 		return u.symbols.map!(su => su.s).array();
152 	}
153 	
154 	private auto flattenDecls(R)(R decls) {
155 		auto oldCtLevel = ctLevel;
156 		scope(exit) ctLevel = oldCtLevel;
157 		
158 		ctLevel = CtUnitLevel.Done;
159 		
160 		auto oldCtUnits = ctUnits;
161 		scope(exit) ctUnits = oldCtUnits;
162 		
163 		ctUnits = [CtUnit()];
164 		
165 		foreach(d; decls) {
166 			visit(d);
167 		}
168 		
169 		return ctUnits;
170 	}
171 	
172 	void visit(Declaration d) {
173 		return this.dispatch(d);
174 	}
175 	
176 	private void select(D, S)(D d, S s) if(is(D : Declaration) && is(S : Symbol)) {
177 		auto unit = &(ctUnits[$ - 1]);
178 		assert(unit.type == CtUnitType.Symbols);
179 		
180 		if (unit.level == CtUnitLevel.Done) {
181 			scheduler.schedule(d, s);
182 		}
183 		
184 		unit.symbols ~= SymbolUnit(d, s);
185 	}
186 	
187 	void visit(FunctionDeclaration d) {
188 		auto stc = d.storageClass;
189 		auto storage = getStorage(stc);
190 		
191 		Function f;
192 		
193 		auto isStatic = storage.isGlobal;
194 		if (isStatic || aggregateType != AggregateType.Class || d.name.isReserved) {
195 			f = new Function(
196 				d.location,
197 				currentScope,
198 				FunctionType.init,
199 				d.name,
200 				[],
201 			);
202 		} else {
203 			uint index = -1;
204 			if (!isOverride && !stc.isOverride) {
205 				index = methodIndex++;
206 			}
207 			
208 			f = new Method(
209 				d.location,
210 				currentScope,
211 				index,
212 				FunctionType.init,
213 				d.name,
214 				[],
215 			);
216 		}
217 		
218 		f.linkage = getLinkage(stc);
219 		f.visibility = getVisibility(stc);
220 		f.inTemplate = inTemplate;
221 		
222 		f.hasThis = isStatic ? false : addThis;
223 		f.hasContext = isStatic ? false : addContext;
224 		
225 		f.isAbstract = isAbstract || stc.isAbstract;
226 		f.isProperty = isProperty || stc.isProperty;
227 		
228 		addOverloadableSymbol(f);
229 		select(d, f);
230 	}
231 	
232 	void visit(VariableDeclaration d) {
233 		auto stc = d.storageClass;
234 		auto storage = getStorage(stc);
235 		
236 		if (aggregateType == AggregateType.None || storage.isGlobal) {
237 			auto v = new Variable(d.location, Type.get(BuiltinType.None), d.name);
238 			v.linkage = getLinkage(stc);
239 			v.visibility = getVisibility(stc);
240 			v.storage = storage;
241 			v.inTemplate = inTemplate;
242 			
243 			addSymbol(v);
244 			select(d, v);
245 		} else {
246 			auto f = new Field(
247 				d.location,
248 				fieldIndex,
249 				Type.get(BuiltinType.None),
250 				d.name,
251 			);
252 			
253 			// Union have all their fields at the same index.
254 			if (aggregateType > AggregateType.Union) {
255 				fieldIndex++;
256 			}
257 			
258 			f.linkage = getLinkage(stc);
259 			f.visibility = getVisibility(stc);
260 			f.inTemplate = inTemplate;
261 			
262 			addSymbol(f);
263 			select(d, f);
264 		}
265 	}
266 	
267 	void visit(StructDeclaration d) {
268 		auto s = new Struct(d.location, currentScope, d.name, []);
269 		s.linkage = linkage;
270 		s.visibility = visibility;
271 		s.inTemplate = inTemplate;
272 		
273 		s.hasContext = storage.isGlobal ? false : addContext;
274 		
275 		addSymbol(s);
276 		select(d, s);
277 	}
278 	
279 	void visit(UnionDeclaration d) {
280 		auto u = new Union(d.location, currentScope, d.name, []);
281 		u.linkage = linkage;
282 		u.visibility = visibility;
283 		u.inTemplate = inTemplate;
284 		
285 		u.hasContext = storage.isGlobal ? false : addContext;
286 		
287 		addSymbol(u);
288 		select(d, u);
289 	}
290 	
291 	void visit(ClassDeclaration d) {
292 		auto c = new Class(d.location, currentScope, d.name, []);
293 		c.linkage = linkage;
294 		c.visibility = visibility;
295 		c.inTemplate = inTemplate;
296 		
297 		c.hasThis = storage.isGlobal ? false : addThis;
298 		c.hasContext = storage.isGlobal ? false : addContext;
299 		
300 		addSymbol(c);
301 		select(d, c);
302 	}
303 	
304 	void visit(InterfaceDeclaration d) {
305 		auto i = new Interface(d.location, currentScope, d.name, [], []);
306 		i.linkage = linkage;
307 		i.visibility = visibility;
308 		i.inTemplate = inTemplate;
309 		
310 		addSymbol(i);
311 		select(d, i);
312 	}
313 	
314 	void visit(EnumDeclaration d) {
315 		if (d.name.isDefined) {
316 			auto e = new Enum(
317 				d.location,
318 				currentScope,
319 				d.name,
320 				Type.get(BuiltinType.None),
321 				[],
322 			);
323 			
324 			e.linkage = linkage;
325 			e.visibility = visibility;
326 			
327 			addSymbol(e);
328 			select(d, e);
329 		} else {
330 			// XXX: Code duplication with symbols. Refactor.
331 			import d.ast.expression : AstExpression, AstBinaryExpression, AstBinaryOp;
332 			AstExpression previous;
333 			AstExpression one;
334 			foreach(vd; d.entries) {
335 				auto v = new Variable(
336 					vd.location,
337 					Type.get(BuiltinType.None),
338 					vd.name,
339 				);
340 				
341 				v.visibility = visibility;
342 				
343 				if (!vd.value) {
344 					import d.ir.expression;
345 					if (previous) {
346 						if (!one) {
347 							one = new IntegerLiteral(
348 								vd.location,
349 								1,
350 								BuiltinType.Int,
351 							);
352 						}
353 						
354 						vd.value = new AstBinaryExpression(
355 							vd.location,
356 							AstBinaryOp.Add,
357 							previous,
358 							one,
359 						);
360 					} else {
361 						vd.value = new IntegerLiteral(
362 							vd.location,
363 							0,
364 							BuiltinType.Int,
365 						);
366 					}
367 				}
368 				
369 				v.storage = Storage.Enum;
370 				previous = vd.value;
371 				
372 				addSymbol(v);
373 				select(vd, v);
374 			}
375 		}
376 	}
377 	
378 	void visit(TemplateDeclaration d) {
379 		auto t = new Template(
380 			d.location,
381 			currentScope,
382 			d.name,
383 			[],
384 			d.declarations,
385 		);
386 		
387 		t.linkage = linkage;
388 		t.visibility = visibility;
389 		t.hasThis = addThis;
390 		t.inTemplate = inTemplate;
391 		t.storage = storage;
392 		
393 		addOverloadableSymbol(t);
394 		select(d, t);
395 	}
396 	
397 	void visit(IdentifierAliasDeclaration d) {
398 		auto a = new SymbolAlias(d.location, d.name, null);
399 		
400 		a.linkage = linkage;
401 		a.visibility = visibility;
402 		a.inTemplate = inTemplate;
403 		
404 		addSymbol(a);
405 		select(d, a);
406 	}
407 	
408 	void visit(TypeAliasDeclaration d) {
409 		auto a = new TypeAlias(d.location, d.name, Type.get(BuiltinType.None));
410 		
411 		a.linkage = linkage;
412 		a.visibility = visibility;
413 		a.inTemplate = inTemplate;
414 		
415 		addSymbol(a);
416 		select(d, a);
417 	}
418 	
419 	void visit(ValueAliasDeclaration d) {
420 		auto a = new ValueAlias(d.location, d.name, null);
421 		
422 		a.linkage = linkage;
423 		a.visibility = visibility;
424 		a.inTemplate = inTemplate;
425 		
426 		addSymbol(a);
427 		select(d, a);
428 	}
429 	
430 	void visit(AliasThisDeclaration d) {
431 		assert(
432 			aggregateType != AggregateType.None,
433 			"alias this can only appear in aggregates"
434 		);
435 		
436 		// TODO: have a better scheme to do this in order to:
437 		// - keep the location of the alias for error messages.
438 		// - not redo identifier resolution all the time.
439 		auto a = cast(Aggregate) currentScope;
440 		assert(a !is null, "Aggergate expected");
441 		
442 		a.aliasThis ~= d.name;
443 	}
444 	
445 	void visit(GroupDeclaration d) {
446 		auto oldStorage = storage;
447 		auto oldVisibility = visibility;
448 		auto oldLinkage = linkage;
449 		
450 		auto oldIsRef      = isRef;
451 		auto oldIsOverride = isOverride;
452 		auto oldIsAbstract = isAbstract;
453 		auto oldIsProperty = isProperty;
454 		auto oldIsNoGC     = isNoGC;
455 		
456 		scope(exit) {
457 			storage = oldStorage;
458 			visibility = oldVisibility;
459 			linkage = oldLinkage;
460 			
461 			isRef      = oldIsRef;
462 			isOverride = oldIsOverride;
463 			isAbstract = oldIsAbstract;
464 			isProperty = oldIsProperty;
465 			isNoGC     = oldIsNoGC;
466 		}
467 		
468 		auto stc = d.storageClass;
469 		
470 		storage = getStorage(stc);
471 		// qualifier = getQualifier(stc);
472 		visibility = getVisibility(stc);
473 		linkage = getLinkage(stc);
474 		
475 		isRef      = isRef      || stc.isRef;
476 		isOverride = isOverride || stc.isOverride;
477 		isAbstract = isAbstract || stc.isAbstract;
478 		isProperty = isProperty || stc.isProperty;
479 		isNoGC     = isNoGC     || stc.isNoGC;
480 		
481 		foreach(decl; d.declarations) {
482 			visit(decl);
483 		}
484 	}
485 	
486 	private Storage getStorage(StorageClass stc) {
487 		if (stc.isStatic && stc.isEnum) {
488 			assert(0, "cannot be static AND enum");
489 		} else if (stc.isStatic) {
490 			return Storage.Static;
491 		} else if (stc.isEnum) {
492 			return Storage.Enum;
493 		}
494 		
495 		return storage;
496 	}
497 	/+
498 	private TypeQualifier getQualifier(StorageClass stc) {
499 		return stc.hasQualifier
500 			? qualifier.add(stc.qualifier)
501 			: qualifier;
502 	}
503 	+/
504 	private Visibility getVisibility(StorageClass stc) {
505 		return stc.hasVisibility
506 			? stc.visibility
507 			: visibility;
508 	}
509 	
510 	private Linkage getLinkage(StorageClass stc) {
511 		return stc.hasLinkage
512 			? stc.linkage
513 			: linkage;
514 	}
515 	
516 	void visit(ImportDeclaration d) {
517 		foreach(name; d.modules) {
518 			currentScope.addImport(importModule(name));
519 		}
520 	}
521 	
522 	void visit(UnittestDeclaration d) {
523 		// Do something only if unittest are enabled.
524 		if (!enableUnittest) {
525 			return;
526 		}
527 		
528 		auto stc = d.storageClass;
529 		auto storage = getStorage(stc);
530 		
531 		auto f = new Function(
532 			d.location,
533 			currentScope,
534 			FunctionType.init,
535 			d.name,
536 			[],
537 		);
538 		
539 		f.inTemplate = inTemplate;
540 		select(d, f);
541 	}
542 	
543 	void visit(StaticIfDeclaration d) {
544 		import std.algorithm : max;
545 		
546 		auto finalCtLevel = CtUnitLevel.Conditional;
547 		auto oldCtLevel = ctLevel;
548 		scope(exit) ctLevel = max(finalCtLevel, oldCtLevel);
549 		
550 		ctLevel = CtUnitLevel.Conditional;
551 		
552 		auto finalCtUnits = ctUnits;
553 		scope(exit) ctUnits = finalCtUnits;
554 		
555 		auto unit = CtUnit();
556 		unit.type = CtUnitType.StaticIf;
557 		unit.staticIf = d;
558 		
559 		cdBranches ~= ConditionalBranch(d, true);
560 		scope(exit) {
561 			cdBranches = cdBranches[0 .. $ - 1];
562 		}
563 		
564 		ctUnits = [CtUnit()];
565 		ctUnits[0].level = CtUnitLevel.Conditional;
566 		
567 		foreach(item; d.items) {
568 			visit(item);
569 		}
570 		
571 		unit.items = ctUnits;
572 		
573 		finalCtLevel = max(finalCtLevel, ctLevel);
574 		ctLevel = CtUnitLevel.Conditional;
575 		
576 		cdBranches = cdBranches[0 .. $ - 1] ~ ConditionalBranch(d, false);
577 		
578 		ctUnits = [CtUnit()];
579 		ctUnits[0].level = CtUnitLevel.Conditional;
580 		
581 		foreach(item; d.elseItems) {
582 			visit(item);
583 		}
584 		
585 		unit.elseItems = ctUnits;
586 		
587 		finalCtLevel = max(finalCtLevel, ctLevel);
588 		unit.level = finalCtLevel;
589 		
590 		auto previous = finalCtUnits[$ - 1];
591 		assert(previous.type == CtUnitType.Symbols);
592 		
593 		auto next = CtUnit();
594 		next.level = previous.level;
595 		
596 		finalCtUnits ~= unit;
597 		finalCtUnits ~= next;
598 	}
599 	
600 	void visit(StaticAssert!Declaration d) {
601 		auto unit = CtUnit();
602 		unit.level = CtUnitLevel.Done;
603 		unit.type = CtUnitType.StaticAssert;
604 		unit.staticAssert = d;
605 		
606 		ctUnits ~= unit;
607 		ctUnits ~= CtUnit();
608 	}
609 	
610 	void visit(Version!Declaration d) {
611 		foreach(v; versions) {
612 			if(d.versionId == v) {
613 				foreach(item; d.items) {
614 					visit(item);
615 				}
616 				
617 				return;
618 			}
619 		}
620 		
621 		// Version has not been found.
622 		foreach(item; d.elseItems) {
623 			visit(item);
624 		}
625 	}
626 	
627 	void visit(Mixin!Declaration d) {
628 		ctLevel = CtUnitLevel.Unknown;
629 		
630 		auto unit = CtUnit();
631 		unit.level = CtUnitLevel.Unknown;
632 		unit.type = CtUnitType.Mixin;
633 		unit.mixinDecl = d;
634 		
635 		ctUnits ~= unit;
636 		ctUnits ~= CtUnit();
637 	}
638 	
639 private:
640 	void addSymbol(Symbol s) {
641 		if (cdBranches.length) {
642 			currentScope.addConditionalSymbol(s, cdBranches);
643 		} else {
644 			currentScope.addSymbol(s);
645 		}
646 	}
647 
648 	void addOverloadableSymbol(Symbol s) {
649 		if (cdBranches.length) {
650 			currentScope.addConditionalSymbol(s, cdBranches);
651 		} else {
652 			currentScope.addOverloadableSymbol(s);
653 		}
654 	}
655 }
656 
657 private :
658 
659 enum CtUnitLevel {
660 	Done,
661 	Conditional,
662 	Unknown,
663 }
664 
665 enum CtUnitType {
666 	Symbols,
667 	StaticAssert,
668 	StaticIf,
669 	Mixin,
670 }
671 
672 struct SymbolUnit {
673 	Declaration d;
674 	Symbol s;
675 }
676 
677 struct CtUnit {
678 	import std.bitmanip;
679 	mixin(bitfields!(
680 		CtUnitLevel, "level", 2,
681 		CtUnitType, "type", 2,
682 		uint, "", 4,
683 	));
684 	
685 	union {
686 		SymbolUnit[] symbols;
687 		StaticAssert!Declaration staticAssert;
688 		Mixin!Declaration mixinDecl;
689 		struct {
690 			// TODO: special declaration subtype here.
691 			StaticIfDeclaration staticIf;
692 			CtUnit[] items;
693 			CtUnit[] elseItems;
694 		};
695 	}
696 }
697 
698 struct DeclarationFlattener(S) if(is(S : Scope)) {
699 	private DeclarationVisitor* dv;
700 	alias dv this;
701 	
702 	S dscope;
703 	
704 	this(DeclarationVisitor* dv, S dscope) {
705 		this.dv = dv;
706 		this.dscope = dscope;
707 	}
708 	
709 	// At this point, CTFE can yield, and change object state,
710 	// so we pass things as parameters.
711 	private Symbol[] lowerToSymbols(CtUnit[] ctus) {
712 		// Process level 2 construct
713 		ctus = lowerStaticIfs(lowerMixins(ctus));
714 		assert(ctus[0].type == CtUnitType.Symbols);
715 		
716 		Symbol[] syms;
717 		
718 		foreach(u; ctus) {
719 			assert(u.level == CtUnitLevel.Done);
720 			final switch(u.type) with(CtUnitType) {
721 				case Symbols:
722 					import std.algorithm, std.range;
723 					syms ~= u.symbols.map!(su => su.s).array();
724 					break;
725 				
726 				case StaticAssert:
727 					checkStaticAssert(u.staticAssert);
728 					break;
729 				
730 				case StaticIf, Mixin:
731 					assert(0, "invalid ctUnit");
732 			}
733 		}
734 		
735 		return syms;
736 	}
737 	
738 	private CtUnit[] lowerStaticIfs(CtUnit[] ctus) {
739 		CtUnit[] cdUnits;
740 		cdUnits.reserve(ctus.length);
741 		foreach(u; ctus) {
742 			assert(u.level != CtUnitLevel.Unknown);
743 			
744 			if (u.level != CtUnitLevel.Conditional) {
745 				assert(u.level == CtUnitLevel.Done);
746 				
747 				cdUnits ~= u;
748 				continue;
749 			}
750 			
751 			final switch(u.type) with(CtUnitType) {
752 				case StaticIf :
753 					cdUnits ~= lowerStaticIf(u);
754 					break;
755 				
756 				case Mixin, Symbols, StaticAssert :
757 					assert(0, "invalid ctUnit");
758 			}
759 		}
760 		
761 		return cdUnits;
762 	}
763 	
764 	private auto lowerStaticIf(bool forMixin = false)(CtUnit unit) in {
765 		assert(unit.type == CtUnitType.StaticIf);
766 	} do {
767 		auto d = unit.staticIf;
768 		
769 		import d.ir.expression, d.semantic.caster, d.semantic.expression;
770 		auto condition = evalIntegral(buildExplicitCast(
771 			pass,
772 			d.condition.location,
773 			Type.get(BuiltinType.Bool),
774 			ExpressionVisitor(pass).visit(d.condition),
775 		));
776 		
777 		CtUnit[] items;
778 		if (condition) {
779 			dscope.resolveConditional(d, true);
780 			items = unit.items;
781 		} else {
782 			dscope.resolveConditional(d, false);
783 			items = unit.elseItems;
784 		}
785 		
786 		foreach(ref u; items) {
787 			if (u.type == CtUnitType.Symbols && u.level == CtUnitLevel.Conditional) {
788 				foreach(su; u.symbols) {
789 					import d.semantic.symbol;
790 					SymbolVisitor(pass).visit(su.d, su.s);
791 				}
792 				
793 				u.level = CtUnitLevel.Done;
794 			}
795 		}
796 		
797 		static if (forMixin) {
798 			return lowerMixins(items);
799 		} else {
800 			return lowerStaticIfs(items);
801 		}
802 	}
803 	
804 	private CtUnit[] lowerMixins(CtUnit[] ctus) {
805 		CtUnit[] cdUnits;
806 		cdUnits.reserve(ctus.length);
807 		foreach(u; ctus) {
808 			if (u.level != CtUnitLevel.Unknown) {
809 				cdUnits ~= u;
810 				continue;
811 			}
812 			
813 			final switch(u.type) with(CtUnitType) {
814 				case StaticIf :
815 					cdUnits ~= lowerStaticIf!true(u);
816 					break;
817 				
818 				case Mixin :
819 					cdUnits ~= lowerMixin(u.mixinDecl);
820 					break;
821 				
822 				case Symbols, StaticAssert :
823 					assert(0, "invalid ctUnit");
824 			}
825 		}
826 		
827 		return cdUnits;
828 	}
829 	
830 	private auto lowerMixin(Mixin!Declaration d) {
831 		import d.semantic.expression : ExpressionVisitor;
832 		auto str = evalString(ExpressionVisitor(pass).visit(d.value));
833 		
834 		// XXX: in order to avoid identifier resolution weirdness.
835 		auto location = d.location;
836 		auto base = context.registerMixin(location, str ~ '\0');
837 
838 		import source.dlexer;
839 		auto trange = lex(base, context);
840 		
841 		import d.parser.base;
842 		trange.match(TokenType.Begin);
843 		
844 		Declaration[] decls;
845 		while(trange.front.type != TokenType.End) {
846 			import d.parser.declaration;
847 			decls ~= trange.parseDeclaration();
848 		}
849 		
850 		return lowerMixins(flattenDecls(decls));
851 	}
852 	
853 	private void checkStaticAssert(StaticAssert!Declaration a) {
854 		import d.semantic.caster, d.semantic.expression;
855 		auto condition = evalIntegral(buildExplicitCast(
856 			pass,
857 			a.condition.location,
858 			Type.get(BuiltinType.Bool),
859 			ExpressionVisitor(pass).visit(a.condition),
860 		));
861 		
862 		if (condition) {
863 			return;
864 		}
865 		
866 		import source.exception;
867 		if (a.message is null) {
868 			throw new CompileException(a.location, "assertion failure");
869 		}
870 		
871 		auto msg = evalString(buildExplicitCast(
872 			pass,
873 			a.condition.location,
874 			Type.get(BuiltinType.Char).getSlice(TypeQualifier.Immutable),
875 			ExpressionVisitor(pass).visit(a.message),
876 		));
877 		
878 		throw new CompileException(a.location, "assertion failure: " ~ msg);
879 	}
880 }