1 module d.semantic.dtemplate;
2 
3 import d.semantic.semantic;
4 
5 import d.ast.declaration;
6 
7 import d.ir.expression;
8 import d.ir.symbol;
9 import d.ir.type;
10 
11 import source.location;
12 
13 struct TemplateInstancier {
14 	private SemanticPass pass;
15 	alias pass this;
16 	
17 	this(SemanticPass pass) {
18 		this.pass = pass;
19 	}
20 	
21 	auto instanciate(
22 		Location location,
23 		OverloadSet s,
24 		TemplateArgument[] args,
25 		Expression[] fargs,
26 	) {
27 		import std.algorithm;
28 		auto cds = s.set.filter!((s) {
29 			if (auto t = cast(Template) s) {
30 				pass.scheduler.require(t);
31 				return t.parameters.length >= args.length;
32 			}
33 			
34 			assert(0, "this isn't a template");
35 		});
36 		
37 		Template match;
38 		TemplateArgument[] matchedArgs;
39 		CandidateLoop: foreach(candidate; cds) {
40 			auto t = cast(Template) candidate;
41 			assert(t, "We should have ensured that we only have templates at this point.");
42 			
43 			TemplateArgument[] cdArgs;
44 			cdArgs.length = t.parameters.length;
45 			if (!matchArguments(t, args, fargs, cdArgs)) {
46 				continue CandidateLoop;
47 			}
48 			
49 			if (!match) {
50 				match = t;
51 				matchedArgs = cdArgs;
52 				continue CandidateLoop;
53 			}
54 			
55 			TemplateArgument[] dummy;
56 			dummy.length = t.parameters.length;
57 			
58 			static buildArg(TemplateParameter p) {
59 				if (auto tp = cast(TypeTemplateParameter) p) {
60 					return TemplateArgument(tp.specialization);
61 				}
62 				
63 				// XXX: Clarify the specialization thing...
64 				if (auto ap = cast(ValueTemplateParameter) p) {
65 					return TemplateArgument.init;
66 				}
67 				
68 				if (auto ap = cast(AliasTemplateParameter) p) {
69 					return TemplateArgument.init;
70 				}
71 				
72 				import source.exception;
73 				throw new CompileException(
74 					p.location,
75 					typeid(p).toString() ~ " not implemented",
76 				);
77 			}
78 			
79 			import std.algorithm, std.array;
80 			auto asArg = match.parameters.map!buildArg.array();
81 			bool match2t = matchArguments(t, asArg, [], dummy);
82 			
83 			dummy = null;
84 			dummy.length = match.parameters.length;
85 			asArg = t.parameters.map!buildArg.array();
86 			bool t2match = matchArguments(match, asArg, [], dummy);
87 			
88 			if (t2match == match2t) {
89 				assert(0, "Ambiguous template");
90 			}
91 			
92 			if (t2match) {
93 				match = t;
94 				matchedArgs = cdArgs;
95 				continue CandidateLoop;
96 			}
97 		}
98 		
99 		if (!match) {
100 			import source.exception;
101 			throw new CompileException(location, "No match");
102 		}
103 		
104 		return instanciateFromResolvedArgs(location, match, matchedArgs);
105 	}
106 	
107 	auto instanciate(
108 		Location location,
109 		Template t,
110 		TemplateArgument[] args,
111 		Expression[] fargs = [],
112 	) {
113 		scheduler.require(t);
114 		
115 		TemplateArgument[] matchedArgs;
116 		if (t.parameters.length > 0) {
117 			matchedArgs.length = t.parameters.length;
118 			
119 			if (!matchArguments(t, args, fargs, matchedArgs)) {
120 				import source.exception;
121 				throw new CompileException(location, "No match");
122 			}
123 		}
124 		
125 		return instanciateFromResolvedArgs(location, t, matchedArgs);
126 	}
127 	
128 private:
129 	bool matchArguments(
130 		Template t,
131 		TemplateArgument[] args,
132 		Expression[] fargs,
133 		TemplateArgument[] matchedArgs,
134 	) in {
135 		assert(t.step == Step.Processed);
136 		assert(t.parameters.length >= args.length);
137 		assert(matchedArgs.length == t.parameters.length);
138 	} do {
139 		uint i = 0;
140 		foreach(a; args) {
141 			if (!matchArgument(t.parameters[i++], a, matchedArgs)) {
142 				return false;
143 			}
144 		}
145 		
146 		if (fargs.length == t.ifti.length) {
147 			foreach(j, a; fargs) {
148 				auto m = IftiTypeMatcher(pass, matchedArgs, a.type)
149 					.visit(t.ifti[j]);
150 				if (!m) {
151 					return false;
152 				}
153 			}
154 		}
155 		
156 		// Match unspecified parameters.
157 		foreach(a; matchedArgs[i .. $]) {
158 			if (!matchArgument(t.parameters[i++], a, matchedArgs)) {
159 				return false;
160 			}
161 		}
162 		
163 		return true;
164 	}
165 	
166 	bool matchArgument(
167 		TemplateParameter p,
168 		TemplateArgument a,
169 		TemplateArgument[] matchedArgs,
170 	) {
171 		return a.apply!(delegate bool() {
172 			if (auto t = cast(TypeTemplateParameter) p) {
173 				return TypeParameterMatcher(pass, matchedArgs, t.defaultValue)
174 					.visit(t);
175 			} else if (auto v = cast(ValueTemplateParameter) p) {
176 				if (v.defaultValue !is null) {
177 					import d.semantic.caster;
178 					auto e = pass.evaluate(buildImplicitCast(
179 						pass,
180 						v.location,
181 						v.type,
182 						v.defaultValue,
183 					));
184 					return ValueMatcher(pass, matchedArgs, e).visit(v);
185 				}
186 			}
187 			
188 			return false;
189 		}, (identified) {
190 			static if (is(typeof(identified) : Type)) {
191 				return TypeParameterMatcher(pass, matchedArgs, identified).visit(p);
192 			} else static if (is(typeof(identified) : Expression)) {
193 				return ValueMatcher(pass, matchedArgs, identified).visit(p);
194 			} else static if (is(typeof(identified) : Symbol)) {
195 				return SymbolMatcher(pass, matchedArgs, identified).visit(p);
196 			} else {
197 				return false;
198 			}
199 		})();
200 	}
201 	
202 	auto instanciateFromResolvedArgs(
203 		Location location,
204 		Template t,
205 		TemplateArgument[] args,
206 	) in {
207 		assert(t.step == Step.Processed);
208 		assert(t.parameters.length == args.length);
209 	} do {
210 		auto i = 0;
211 		Symbol[] argSyms;
212 		
213 		// XXX: have to put array once again to avoid multiple map.
214 		import std.algorithm, std.array;
215 		string id = args.map!(
216 			a => a.apply!(function string() {
217 				assert(0, "All passed argument must be defined.");
218 			}, delegate string(identified) {
219 				auto p = t.parameters[i++];
220 				
221 				alias T = typeof(identified);
222 				static if (is(T : Type)) {
223 					auto a = new TypeAlias(p.location, p.name, identified);
224 					
225 					import d.semantic.mangler;
226 					a.mangle = pass.context.getName(TypeMangler(pass).visit(identified));
227 					a.step = Step.Processed;
228 					
229 					argSyms ~= a;
230 					return "T" ~ a.mangle.toString(pass.context);
231 				} else static if (is(T : CompileTimeExpression)) {
232 					auto a = new ValueAlias(p.location, p.name, identified);
233 					
234 					import d.semantic.mangler;
235 					auto typeMangle = TypeMangler(pass).visit(identified.type);
236 					auto valueMangle = ValueMangler(pass).visit(identified);
237 					a.mangle = pass.context.getName(typeMangle ~ valueMangle);
238 					a.step = Step.Processed;
239 					
240 					argSyms ~= a;
241 					return "V" ~ a.mangle.toString(pass.context);
242 				} else static if (is(T : Symbol)) {
243 					auto a = new SymbolAlias(p.location, p.name, identified);
244 					
245 					import d.semantic.symbol;
246 					SymbolAnalyzer(pass).process(a);
247 					
248 					argSyms ~= a;
249 					return "S" ~ a.mangle.toString(pass.context);
250 				} else {
251 					assert(0, typeid(identified).toString() ~ " is not supported.");
252 				}
253 			})
254 		).array().join();
255 		
256 		return t.instances.get(id, {
257 			auto oldManglePrefix = pass.manglePrefix;
258 			auto oldScope = pass.currentScope;
259 			scope(exit) {
260 				pass.manglePrefix = oldManglePrefix;
261 				pass.currentScope = oldScope;
262 			}
263 			
264 			auto i = new TemplateInstance(location, t, args);
265 			auto mangle = t.mangle.toString(pass.context);
266 			i.mangle = pass.context.getName(mangle ~ "T" ~ id ~ "Z");
267 			i.storage = t.storage;
268 			
269 			// Prefill arguments.
270 			foreach (a; argSyms) {
271 				i.addSymbol(a);
272 			}
273 			
274 			pass.scheduler.schedule(t, i);
275 			return t.instances[id] = i;
276 		}());
277 	}
278 }
279 
280 // Conflict with Interface in object.di
281 alias Interface = d.ir.symbol.Interface;
282 
283 struct TypeParameterMatcher {
284 	TemplateArgument[] matchedArgs;
285 	Type matchee;
286 	
287 	SemanticPass pass;
288 	alias pass this;
289 	
290 	this(SemanticPass pass, TemplateArgument[] matchedArgs, Type matchee) {
291 		this.pass = pass;
292 		this.matchedArgs = matchedArgs;
293 		this.matchee = matchee.getCanonical();
294 	}
295 	
296 	bool visit(TemplateParameter p) {
297 		return this.dispatch(p);
298 	}
299 	
300 	bool visit(AliasTemplateParameter p) {
301 		matchedArgs[p.index] = TemplateArgument(matchee);
302 		return true;
303 	}
304 	
305 	bool visit(TypeTemplateParameter p) {
306 		auto originalMatchee = matchee;
307 		auto originalMatched = matchedArgs[p.index];
308 		matchedArgs[p.index] = TemplateArgument.init;
309 		
310 		auto ct = p.specialization.getCanonical();
311 		if (!StaticTypeMatcher(pass, matchedArgs, matchee).visit(ct)) {
312 			return false;
313 		}
314 		
315 		matchedArgs[p.index].apply!({
316 			matchedArgs[p.index] = TemplateArgument(originalMatchee);
317 		}, (_) {})();
318 		
319 		return originalMatched.apply!(() => true, (o) {
320 			static if (is(typeof(o) : Type)) {
321 				return matchedArgs[p.index].apply!(() => false, (m) {
322 					static if (is(typeof(m) : Type)) {
323 						import d.semantic.caster;
324 						return implicitCastFrom(pass, m, o) == CastKind.Exact;
325 					} else {
326 						return false;
327 					}
328 				})();
329 			} else {
330 				return false;
331 			}
332 		})();
333 	}
334 }
335 
336 alias StaticTypeMatcher = TypeMatcher!false;
337 alias IftiTypeMatcher = TypeMatcher!true;
338 
339 struct TypeMatcher(bool isIFTI) {
340 	TemplateArgument[] matchedArgs;
341 	Type matchee;
342 	
343 	SemanticPass pass;
344 	alias pass this;
345 	
346 	this(SemanticPass pass, TemplateArgument[] matchedArgs, Type matchee) {
347 		this.pass = pass;
348 		this.matchedArgs = matchedArgs;
349 		this.matchee = matchee.getCanonical();
350 	}
351 	
352 	bool visit(Type t) {
353 		return t.getCanonical().accept(this);
354 	}
355 	
356 	bool visit(BuiltinType t) {
357 		return (matchee.kind == TypeKind.Builtin)
358 			? t == matchee.builtin
359 			: false;
360 	}
361 	
362 	bool visitPointerOf(Type t) {
363 		if (matchee.kind != TypeKind.Pointer) {
364 			return false;
365 		}
366 		
367 		matchee = matchee.element.getCanonical();
368 		return visit(t);
369 	}
370 	
371 	bool visitSliceOf(Type t) {
372 		if (matchee.kind != TypeKind.Slice) {
373 			return false;
374 		}
375 		
376 		matchee = matchee.element.getCanonical();
377 		return visit(t);
378 	}
379 	
380 	bool visitArrayOf(uint size, Type t) {
381 		if (matchee.kind != TypeKind.Array) {
382 			return false;
383 		}
384 		
385 		if (matchee.size != size) {
386 			return false;
387 		}
388 		
389 		matchee = matchee.element.getCanonical();
390 		return visit(t);
391 	}
392 	
393 	bool visit(Struct s) {
394 		if (matchee.kind != TypeKind.Struct) {
395 			return false;
396 		}
397 		
398 		return s is matchee.dstruct;
399 	}
400 	
401 	bool visit(Class c) {
402 		assert(0, "Not implemented.");
403 	}
404 	
405 	bool visit(Enum e) {
406 		if (matchee.kind != TypeKind.Enum) {
407 			return visit(e.type);
408 		}
409 		
410 		return matchee.denum is e;
411 	}
412 	
413 	bool visit(TypeAlias a) {
414 		assert(0, "Not implemented.");
415 	}
416 	
417 	bool visit(Interface i) {
418 		assert(0, "Not implemented.");
419 	}
420 	
421 	bool visit(Union u) {
422 		if (matchee.kind != TypeKind.Union) {
423 			return false;
424 		}
425 		
426 		return u is matchee.dunion;
427 	}
428 	
429 	bool visit(Function f) {
430 		assert(0, "Not implemented.");
431 	}
432 	
433 	bool visit(Type[] seq) {
434 		assert(0, "Not implemented.");
435 	}
436 	
437 	bool visit(FunctionType f) {
438 		assert(0, "Not implemented.");
439 	}
440 	
441 	bool visit(Pattern p) {
442 		return p.accept(this);
443 	}
444 	
445 	bool visit(TypeTemplateParameter p) {
446 		auto i = p.index;
447 		return matchedArgs[i].apply!({
448 			matchedArgs[i] = TemplateArgument(matchee);
449 			return true;
450 		}, delegate bool(identified) {
451 			static if (is(typeof(identified) : Type)) {
452 				import d.semantic.caster;
453 				auto castKind = implicitCastFrom(pass, matchee, identified);
454 				return isIFTI
455 					? castKind > CastKind.Invalid
456 					: castKind == CastKind.Exact;
457 			} else {
458 				return false;
459 			}
460 		})();
461 	}
462 	
463 	bool visit(Type t, ValueTemplateParameter p) {
464 		if (matchee.kind != TypeKind.Array) {
465 			return false;
466 		}
467 		
468 		auto size = matchee.size;
469 		matchee = matchee.element.getCanonical();
470 		if (!visit(t)) {
471 			return false;
472 		}
473 		
474 		auto s = new IntegerLiteral(
475 			p.location,
476 			size,
477 			pass.object.getSizeT().type.builtin,
478 		);
479 		
480 		return ValueMatcher(pass, matchedArgs, s).visit(p);
481 	}
482 	
483 	bool visit(Symbol s, TemplateArgument[] args) {
484 		// We are matching a template aggregate.
485 		if (!matchee.isAggregate()) {
486 			return false;
487 		}
488 		
489 		// If this is not a templated type, bail.
490 		auto a = matchee.aggregate;
491 		if (!a.inTemplate) {
492 			return false;
493 		}
494 		
495 		auto name = a.name;
496 		auto ti = cast(TemplateInstance) a.getParentScope();
497 		if (ti is null) {
498 			// Cannot find the template instance.
499 			return false;
500 		}
501 		
502 		if (args.length > ti.args.length) {
503 			// Incompatible argument count.
504 			return false;
505 		}
506 		
507 		// Match the template itself.
508 		Template t = ti.getTemplate();
509 		if (!SymbolMatcher(pass, matchedArgs, t).visit(s)) {
510 			return false;
511 		}
512 		
513 		// We got our instance, let's match parameters.
514 		foreach (i, arg; args) {
515 			if (arg.tag != TemplateArgument.Tag.Symbol) {
516 				assert(0, "Only symbols are implemented for now");
517 			}
518 			
519 			auto p = cast(TemplateParameter) arg.get!(TemplateArgument.Tag.Symbol);
520 			if (p is null) {
521 				assert(0, "Expected a tepmplate argument");
522 			}
523 			
524 			if (!TemplateInstancier(pass).matchArgument(p, ti.args[i], matchedArgs)) {
525 				return false;
526 			}
527 		}
528 		
529 		return true;
530 	}
531 	
532 	import d.ir.error;
533 	bool visit(CompileError e) {
534 		assert(0, "Not implemented.");
535 	}
536 }
537 
538 struct ValueMatcher {
539 	TemplateArgument[] matchedArgs;
540 	CompileTimeExpression matchee;
541 	
542 	SemanticPass pass;
543 	alias pass this;
544 	
545 	this(
546 		SemanticPass pass,
547 		TemplateArgument[] matchedArgs,
548 		CompileTimeExpression matchee,
549 	) {
550 		this.pass = pass;
551 		this.matchedArgs = matchedArgs;
552 		this.matchee = matchee;
553 	}
554 	
555 	bool visit(TemplateParameter p) {
556 		return this.dispatch(p);
557 	}
558 	
559 	private bool matchTyped(Type t, uint i) {
560 		if (t.kind == TypeKind.Pattern) {
561 			matchedArgs[i] = TemplateArgument(matchee);
562 			return IftiTypeMatcher(pass, matchedArgs, matchee.type).visit(t);
563 		}
564 		
565 		import d.semantic.caster;
566 		matchee = evaluate(
567 			buildImplicitCast(pass, matchee.location, t, matchee),
568 		);
569 		
570 		import d.ir.error;
571 		if (cast(ErrorExpression) matchee) {
572 			return false;
573 		}
574 		
575 		matchedArgs[i] = TemplateArgument(matchee);
576 		return true;
577 	}
578 	
579 	bool visit(ValueTemplateParameter p) {
580 		return matchTyped(p.type, p.index);
581 	}
582 	
583 	bool visit(AliasTemplateParameter p) {
584 		matchedArgs[p.index] = TemplateArgument(matchee);
585 		return true;
586 	}
587 	
588 	bool visit(TypedAliasTemplateParameter p) {
589 		return matchTyped(p.type, p.index);
590 	}
591 }
592 
593 struct SymbolMatcher {
594 	TemplateArgument[] matchedArgs;
595 	Symbol matchee;
596 	
597 	SemanticPass pass;
598 	alias pass this;
599 	
600 	this(SemanticPass pass, TemplateArgument[] matchedArgs, Symbol matchee) {
601 		this.pass = pass;
602 		this.matchedArgs = matchedArgs;
603 		this.matchee = matchee;
604 	}
605 	
606 	bool visit(Symbol s) {
607 		if (auto p = cast(TemplateParameter) s) {
608 			return visit(p);
609 		}
610 		
611 		// Peel aliases
612 		while (true) {
613 			auto a = cast(SymbolAlias) s;
614 			if (a is null) {
615 				break;
616 			}
617 			
618 			s = a;
619 		}
620 		
621 		// We have a match.
622 		return s is matchee;
623 	}
624 	
625 	bool visit(TemplateParameter p) {
626 		return this.dispatch(p);
627 	}
628 	
629 	bool visit(AliasTemplateParameter p) {
630 		matchedArgs[p.index] = TemplateArgument(matchee);
631 		return true;
632 	}
633 	
634 	bool visit(TypedAliasTemplateParameter p) {
635 		if (auto vs = cast(ValueSymbol) matchee) {
636 			import d.semantic.identifier : IdentifierResolver, apply;
637 			return IdentifierResolver(pass)
638 				.postProcess(p.location, vs)
639 				.apply!(delegate bool(i) {
640 					alias T = typeof(i);
641 					static if (is(T : Expression)) {
642 						auto v = pass.evaluate(i);
643 						return ValueMatcher(pass, matchedArgs, v).visit(p);
644 					} else {
645 						return false;
646 					}
647 				})();
648 		}
649 		
650 		return false;
651 	}
652 	
653 	bool visit(TypeTemplateParameter p) {
654 		import d.semantic.identifier : IdentifierResolver, apply;
655 		return IdentifierResolver(pass)
656 			.postProcess(p.location, matchee)
657 			.apply!(delegate bool(identified) {
658 				alias T = typeof(identified);
659 				static if (is(T : Type)) {
660 					return TypeParameterMatcher(
661 						pass,
662 						matchedArgs,
663 						identified,
664 					).visit(p);
665 				} else {
666 					return false;
667 				}
668 			})();
669 	}
670 	
671 	bool visit(ValueTemplateParameter p) {
672 		import d.semantic.identifier : IdentifierResolver, apply;
673 		return IdentifierResolver(pass)
674 			.postProcess(p.location, matchee)
675 			.apply!(delegate bool(identified) {
676 				alias T = typeof(identified);
677 				static if (is(T : Expression)) {
678 					auto v = pass.evaluate(identified);
679 					return ValueMatcher(pass, matchedArgs, v).visit(p);
680 				} else {
681 					return false;
682 				}
683 			})();
684 	}
685 }