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 }