1 /** 2 * Handle introspection functionality of the `__traits()` construct. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits) 5 * 6 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d) 10 * Documentation: https://dlang.org/phobos/dmd_traits.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d 12 */ 13 14 module dmd.traits; 15 16 import core.stdc.stdio; 17 18 import dmd.aggregate; 19 import dmd.arraytypes; 20 import dmd.astcodegen; 21 import dmd.attrib; 22 import dmd.canthrow; 23 import dmd.dclass; 24 import dmd.declaration; 25 import dmd.denum; 26 import dmd.dimport; 27 import dmd.dmodule; 28 import dmd.dscope; 29 import dmd.dsymbol; 30 import dmd.dsymbolsem; 31 import dmd.dtemplate; 32 import dmd.errors; 33 import dmd.expression; 34 import dmd.expressionsem; 35 import dmd.func; 36 import dmd.globals; 37 import dmd.hdrgen; 38 import dmd.id; 39 import dmd.identifier; 40 import dmd.mtype; 41 import dmd.nogc; 42 import dmd.parse; 43 import dmd.root.array; 44 import dmd.root.speller; 45 import dmd.root.stringtable; 46 import dmd.target; 47 import dmd.tokens; 48 import dmd.typesem; 49 import dmd.visitor; 50 import dmd.root.rootobject; 51 import dmd.root.outbuffer; 52 import dmd.root.string; 53 54 enum LOGSEMANTIC = false; 55 56 /************************ TraitsExp ************************************/ 57 58 /************************************** 59 * Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally 60 * stripping off expression contexts. 61 * 62 * Some symbol related `__traits` ignore arguments expression contexts. 63 * For example: 64 * ---- 65 * struct S { void f() {} } 66 * S s; 67 * pragma(msg, __traits(isNested, s.f)); 68 * // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`. 69 * ---- 70 * 71 * This is used for that common `__traits` behavior. 72 * 73 * Input: 74 * oarg object to get the symbol for 75 * Returns: 76 * Dsymbol the corresponding symbol for oarg 77 */ 78 private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg) 79 { 80 if (auto e = isExpression(oarg)) 81 { 82 if (e.op == TOK.dotVariable) 83 return (cast(DotVarExp)e).var; 84 if (e.op == TOK.dotTemplateDeclaration) 85 return (cast(DotTemplateExp)e).td; 86 } 87 return getDsymbol(oarg); 88 } 89 90 private const StringTable!bool traitsStringTable; 91 92 shared static this() 93 { 94 static immutable string[] names = 95 [ 96 "isAbstractClass", 97 "isArithmetic", 98 "isAssociativeArray", 99 "isDisabled", 100 "isDeprecated", 101 "isFuture", 102 "isFinalClass", 103 "isPOD", 104 "isNested", 105 "isFloating", 106 "isIntegral", 107 "isScalar", 108 "isStaticArray", 109 "isUnsigned", 110 "isVirtualFunction", 111 "isVirtualMethod", 112 "isAbstractFunction", 113 "isFinalFunction", 114 "isOverrideFunction", 115 "isStaticFunction", 116 "isModule", 117 "isPackage", 118 "isRef", 119 "isOut", 120 "isLazy", 121 "isReturnOnStack", 122 "hasMember", 123 "identifier", 124 "getProtection", 125 "parent", 126 "getLinkage", 127 "getMember", 128 "getOverloads", 129 "getVirtualFunctions", 130 "getVirtualMethods", 131 "classInstanceSize", 132 "allMembers", 133 "derivedMembers", 134 "isSame", 135 "compiles", 136 "parameters", 137 "getAliasThis", 138 "getAttributes", 139 "getFunctionAttributes", 140 "getFunctionVariadicStyle", 141 "getParameterStorageClasses", 142 "getUnitTests", 143 "getVirtualIndex", 144 "getPointerBitmap", 145 "isZeroInit", 146 "getTargetInfo", 147 "getLocation", 148 "hasPostblit", 149 "hasCopyConstructor", 150 ]; 151 152 StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable; 153 stringTable._init(names.length); 154 155 foreach (s; names) 156 { 157 auto sv = stringTable.insert(s, true); 158 assert(sv); 159 } 160 } 161 162 /** 163 * get an array of size_t values that indicate possible pointer words in memory 164 * if interpreted as the type given as argument 165 * Returns: the size of the type in bytes, d_uns64.max on error 166 */ 167 d_uns64 getTypePointerBitmap(Loc loc, Type t, Array!(d_uns64)* data) 168 { 169 d_uns64 sz; 170 if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration()) 171 sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(loc); 172 else 173 sz = t.size(loc); 174 if (sz == SIZE_INVALID) 175 return d_uns64.max; 176 177 const sz_size_t = Type.tsize_t.size(loc); 178 if (sz > sz.max - sz_size_t) 179 { 180 error(loc, "size overflow for type `%s`", t.toChars()); 181 return d_uns64.max; 182 } 183 184 d_uns64 bitsPerWord = sz_size_t * 8; 185 d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t; 186 d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord; 187 188 data.setDim(cast(size_t)cntdata); 189 data.zero(); 190 191 extern (C++) final class PointerBitmapVisitor : Visitor 192 { 193 alias visit = Visitor.visit; 194 public: 195 extern (D) this(Array!(d_uns64)* _data, d_uns64 _sz_size_t) 196 { 197 this.data = _data; 198 this.sz_size_t = _sz_size_t; 199 } 200 201 void setpointer(d_uns64 off) 202 { 203 d_uns64 ptroff = off / sz_size_t; 204 (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t)); 205 } 206 207 override void visit(Type t) 208 { 209 Type tb = t.toBasetype(); 210 if (tb != t) 211 tb.accept(this); 212 } 213 214 override void visit(TypeError t) 215 { 216 visit(cast(Type)t); 217 } 218 219 override void visit(TypeNext t) 220 { 221 assert(0); 222 } 223 224 override void visit(TypeBasic t) 225 { 226 if (t.ty == Tvoid) 227 setpointer(offset); 228 } 229 230 override void visit(TypeVector t) 231 { 232 } 233 234 override void visit(TypeArray t) 235 { 236 assert(0); 237 } 238 239 override void visit(TypeSArray t) 240 { 241 d_uns64 arrayoff = offset; 242 d_uns64 nextsize = t.next.size(); 243 if (nextsize == SIZE_INVALID) 244 error = true; 245 d_uns64 dim = t.dim.toInteger(); 246 for (d_uns64 i = 0; i < dim; i++) 247 { 248 offset = arrayoff + i * nextsize; 249 t.next.accept(this); 250 } 251 offset = arrayoff; 252 } 253 254 override void visit(TypeDArray t) 255 { 256 setpointer(offset + sz_size_t); 257 } 258 259 // dynamic array is {length,ptr} 260 override void visit(TypeAArray t) 261 { 262 setpointer(offset); 263 } 264 265 override void visit(TypePointer t) 266 { 267 if (t.nextOf().ty != Tfunction) // don't mark function pointers 268 setpointer(offset); 269 } 270 271 override void visit(TypeReference t) 272 { 273 setpointer(offset); 274 } 275 276 override void visit(TypeClass t) 277 { 278 setpointer(offset); 279 } 280 281 override void visit(TypeFunction t) 282 { 283 } 284 285 override void visit(TypeDelegate t) 286 { 287 setpointer(offset); 288 } 289 290 // delegate is {context, function} 291 override void visit(TypeQualified t) 292 { 293 assert(0); 294 } 295 296 // assume resolved 297 override void visit(TypeIdentifier t) 298 { 299 assert(0); 300 } 301 302 override void visit(TypeInstance t) 303 { 304 assert(0); 305 } 306 307 override void visit(TypeTypeof t) 308 { 309 assert(0); 310 } 311 312 override void visit(TypeReturn t) 313 { 314 assert(0); 315 } 316 317 override void visit(TypeEnum t) 318 { 319 visit(cast(Type)t); 320 } 321 322 override void visit(TypeTuple t) 323 { 324 visit(cast(Type)t); 325 } 326 327 override void visit(TypeSlice t) 328 { 329 assert(0); 330 } 331 332 override void visit(TypeNull t) 333 { 334 // always a null pointer 335 } 336 337 override void visit(TypeStruct t) 338 { 339 d_uns64 structoff = offset; 340 foreach (v; t.sym.fields) 341 { 342 offset = structoff + v.offset; 343 if (v.type.ty == Tclass) 344 setpointer(offset); 345 else 346 v.type.accept(this); 347 } 348 offset = structoff; 349 } 350 351 // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references 352 void visitClass(TypeClass t) 353 { 354 d_uns64 classoff = offset; 355 // skip vtable-ptr and monitor 356 if (t.sym.baseClass) 357 visitClass(cast(TypeClass)t.sym.baseClass.type); 358 foreach (v; t.sym.fields) 359 { 360 offset = classoff + v.offset; 361 v.type.accept(this); 362 } 363 offset = classoff; 364 } 365 366 Array!(d_uns64)* data; 367 d_uns64 offset; 368 d_uns64 sz_size_t; 369 bool error; 370 } 371 372 scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t); 373 if (t.ty == Tclass) 374 pbv.visitClass(cast(TypeClass)t); 375 else 376 t.accept(pbv); 377 return pbv.error ? d_uns64.max : sz; 378 } 379 380 /** 381 * get an array of size_t values that indicate possible pointer words in memory 382 * if interpreted as the type given as argument 383 * the first array element is the size of the type for independent interpretation 384 * of the array 385 * following elements bits represent one word (4/8 bytes depending on the target 386 * architecture). If set the corresponding memory might contain a pointer/reference. 387 * 388 * Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...] 389 */ 390 private Expression pointerBitmap(TraitsExp e) 391 { 392 if (!e.args || e.args.dim != 1) 393 { 394 error(e.loc, "a single type expected for trait pointerBitmap"); 395 return new ErrorExp(); 396 } 397 398 Type t = getType((*e.args)[0]); 399 if (!t) 400 { 401 error(e.loc, "`%s` is not a type", (*e.args)[0].toChars()); 402 return new ErrorExp(); 403 } 404 405 Array!(d_uns64) data; 406 d_uns64 sz = getTypePointerBitmap(e.loc, t, &data); 407 if (sz == d_uns64.max) 408 return new ErrorExp(); 409 410 auto exps = new Expressions(data.dim + 1); 411 (*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t); 412 foreach (size_t i; 1 .. exps.dim) 413 (*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t); 414 415 auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.dim + 1), exps); 416 return ale; 417 } 418 419 Expression semanticTraits(TraitsExp e, Scope* sc) 420 { 421 static if (LOGSEMANTIC) 422 { 423 printf("TraitsExp::semantic() %s\n", e.toChars()); 424 } 425 426 if (e.ident != Id.compiles && 427 e.ident != Id.isSame && 428 e.ident != Id.identifier && 429 e.ident != Id.getProtection && 430 e.ident != Id.getAttributes) 431 { 432 // Pretend we're in a deprecated scope so that deprecation messages 433 // aren't triggered when checking if a symbol is deprecated 434 const save = sc.stc; 435 if (e.ident == Id.isDeprecated) 436 sc.stc |= STC.deprecated_; 437 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) 438 { 439 sc.stc = save; 440 return new ErrorExp(); 441 } 442 sc.stc = save; 443 } 444 size_t dim = e.args ? e.args.dim : 0; 445 446 Expression dimError(int expected) 447 { 448 e.error("expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim); 449 return new ErrorExp(); 450 } 451 452 static IntegerExp True() 453 { 454 return IntegerExp.createBool(true); 455 } 456 457 static IntegerExp False() 458 { 459 return IntegerExp.createBool(false); 460 } 461 462 /******** 463 * Gets the function type from a given AST node 464 * if the node is a function of some sort. 465 * Params: 466 * o = an AST node to check for a `TypeFunction` 467 * fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null` 468 * Returns: 469 * a type node if `o` is a declaration of 470 * a delegate, function, function-pointer or a variable of the former. 471 * Otherwise, `null`. 472 */ 473 static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp) 474 { 475 Type t; 476 if (auto s = getDsymbolWithoutExpCtx(o)) 477 { 478 if (auto fd = s.isFuncDeclaration()) 479 { 480 t = fd.type; 481 fdp = fd; 482 } 483 else if (auto vd = s.isVarDeclaration()) 484 t = vd.type; 485 else 486 t = isType(o); 487 } 488 else 489 t = isType(o); 490 491 if (t) 492 { 493 if (t.ty == Tfunction) 494 return cast(TypeFunction)t; 495 else if (t.ty == Tdelegate) 496 return cast(TypeFunction)t.nextOf(); 497 else if (t.ty == Tpointer && t.nextOf().ty == Tfunction) 498 return cast(TypeFunction)t.nextOf(); 499 } 500 501 return null; 502 } 503 504 IntegerExp isX(T)(bool delegate(T) fp) 505 { 506 if (!dim) 507 return False(); 508 foreach (o; *e.args) 509 { 510 static if (is(T == Type)) 511 auto y = getType(o); 512 513 static if (is(T : Dsymbol)) 514 { 515 auto s = getDsymbolWithoutExpCtx(o); 516 if (!s) 517 return False(); 518 } 519 static if (is(T == Dsymbol)) 520 alias y = s; 521 static if (is(T == Declaration)) 522 auto y = s.isDeclaration(); 523 static if (is(T == FuncDeclaration)) 524 auto y = s.isFuncDeclaration(); 525 static if (is(T == EnumMember)) 526 auto y = s.isEnumMember(); 527 528 if (!y || !fp(y)) 529 return False(); 530 } 531 return True(); 532 } 533 534 alias isTypeX = isX!Type; 535 alias isDsymX = isX!Dsymbol; 536 alias isDeclX = isX!Declaration; 537 alias isFuncX = isX!FuncDeclaration; 538 alias isEnumMemX = isX!EnumMember; 539 540 Expression isPkgX(bool function(Package) fp) 541 { 542 return isDsymX((Dsymbol sym) { 543 Package p = resolveIsPackage(sym); 544 return (p !is null) && fp(p); 545 }); 546 } 547 548 if (e.ident == Id.isArithmetic) 549 { 550 return isTypeX(t => t.isintegral() || t.isfloating()); 551 } 552 if (e.ident == Id.isFloating) 553 { 554 return isTypeX(t => t.isfloating()); 555 } 556 if (e.ident == Id.isIntegral) 557 { 558 return isTypeX(t => t.isintegral()); 559 } 560 if (e.ident == Id.isScalar) 561 { 562 return isTypeX(t => t.isscalar()); 563 } 564 if (e.ident == Id.isUnsigned) 565 { 566 return isTypeX(t => t.isunsigned()); 567 } 568 if (e.ident == Id.isAssociativeArray) 569 { 570 return isTypeX(t => t.toBasetype().ty == Taarray); 571 } 572 if (e.ident == Id.isDeprecated) 573 { 574 if (global.params.vcomplex) 575 { 576 if (isTypeX(t => t.iscomplex() || t.isimaginary()).isBool(true)) 577 return True(); 578 } 579 return isDsymX(t => t.isDeprecated()); 580 } 581 if (e.ident == Id.isFuture) 582 { 583 return isDeclX(t => t.isFuture()); 584 } 585 if (e.ident == Id.isStaticArray) 586 { 587 return isTypeX(t => t.toBasetype().ty == Tsarray); 588 } 589 if (e.ident == Id.isAbstractClass) 590 { 591 return isTypeX(t => t.toBasetype().ty == Tclass && 592 (cast(TypeClass)t.toBasetype()).sym.isAbstract()); 593 } 594 if (e.ident == Id.isFinalClass) 595 { 596 return isTypeX(t => t.toBasetype().ty == Tclass && 597 ((cast(TypeClass)t.toBasetype()).sym.storage_class & STC.final_) != 0); 598 } 599 if (e.ident == Id.isTemplate) 600 { 601 if (dim != 1) 602 return dimError(1); 603 604 return isDsymX((s) 605 { 606 if (!s.toAlias().isOverloadable()) 607 return false; 608 return overloadApply(s, 609 sm => sm.isTemplateDeclaration() !is null) != 0; 610 }); 611 } 612 if (e.ident == Id.isPOD) 613 { 614 if (dim != 1) 615 return dimError(1); 616 617 auto o = (*e.args)[0]; 618 auto t = isType(o); 619 if (!t) 620 { 621 e.error("type expected as second argument of __traits `%s` instead of `%s`", 622 e.ident.toChars(), o.toChars()); 623 return new ErrorExp(); 624 } 625 626 Type tb = t.baseElemOf(); 627 if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null) 628 { 629 return sd.isPOD() ? True() : False(); 630 } 631 return True(); 632 } 633 if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit) 634 { 635 if (dim != 1) 636 return dimError(1); 637 638 auto o = (*e.args)[0]; 639 auto t = isType(o); 640 if (!t) 641 { 642 e.error("type expected as second argument of __traits `%s` instead of `%s`", 643 e.ident.toChars(), o.toChars()); 644 return new ErrorExp(); 645 } 646 647 Type tb = t.baseElemOf(); 648 if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null) 649 { 650 return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False()) 651 : (sd.hasCopyCtor ? True() : False()); 652 } 653 return False(); 654 } 655 656 if (e.ident == Id.isNested) 657 { 658 if (dim != 1) 659 return dimError(1); 660 661 auto o = (*e.args)[0]; 662 auto s = getDsymbolWithoutExpCtx(o); 663 if (!s) 664 { 665 } 666 else if (auto ad = s.isAggregateDeclaration()) 667 { 668 return ad.isNested() ? True() : False(); 669 } 670 else if (auto fd = s.isFuncDeclaration()) 671 { 672 return fd.isNested() ? True() : False(); 673 } 674 675 e.error("aggregate or function expected instead of `%s`", o.toChars()); 676 return new ErrorExp(); 677 } 678 if (e.ident == Id.isDisabled) 679 { 680 if (dim != 1) 681 return dimError(1); 682 683 return isDeclX(f => f.isDisabled()); 684 } 685 if (e.ident == Id.isAbstractFunction) 686 { 687 if (dim != 1) 688 return dimError(1); 689 690 return isFuncX(f => f.isAbstract()); 691 } 692 if (e.ident == Id.isVirtualFunction) 693 { 694 if (dim != 1) 695 return dimError(1); 696 697 return isFuncX(f => f.isVirtual()); 698 } 699 if (e.ident == Id.isVirtualMethod) 700 { 701 if (dim != 1) 702 return dimError(1); 703 704 return isFuncX(f => f.isVirtualMethod()); 705 } 706 if (e.ident == Id.isFinalFunction) 707 { 708 if (dim != 1) 709 return dimError(1); 710 711 return isFuncX(f => f.isFinalFunc()); 712 } 713 if (e.ident == Id.isOverrideFunction) 714 { 715 if (dim != 1) 716 return dimError(1); 717 718 return isFuncX(f => f.isOverride()); 719 } 720 if (e.ident == Id.isStaticFunction) 721 { 722 if (dim != 1) 723 return dimError(1); 724 725 return isFuncX(f => !f.needThis() && !f.isNested()); 726 } 727 if (e.ident == Id.isModule) 728 { 729 if (dim != 1) 730 return dimError(1); 731 732 return isPkgX(p => p.isModule() || p.isPackageMod()); 733 } 734 if (e.ident == Id.isPackage) 735 { 736 if (dim != 1) 737 return dimError(1); 738 739 return isPkgX(p => p.isModule() is null); 740 } 741 if (e.ident == Id.isRef) 742 { 743 if (dim != 1) 744 return dimError(1); 745 746 return isDeclX(d => d.isRef()); 747 } 748 if (e.ident == Id.isOut) 749 { 750 if (dim != 1) 751 return dimError(1); 752 753 return isDeclX(d => d.isOut()); 754 } 755 if (e.ident == Id.isLazy) 756 { 757 if (dim != 1) 758 return dimError(1); 759 760 return isDeclX(d => (d.storage_class & STC.lazy_) != 0); 761 } 762 if (e.ident == Id.identifier) 763 { 764 // Get identifier for symbol as a string literal 765 /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that 766 * a symbol should not be folded to a constant. 767 * Bit 1 means don't convert Parameter to Type if Parameter has an identifier 768 */ 769 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2)) 770 return new ErrorExp(); 771 if (dim != 1) 772 return dimError(1); 773 774 auto o = (*e.args)[0]; 775 Identifier id; 776 if (auto po = isParameter(o)) 777 { 778 if (!po.ident) 779 { 780 e.error("argument `%s` has no identifier", po.type.toChars()); 781 return new ErrorExp(); 782 } 783 id = po.ident; 784 } 785 else 786 { 787 Dsymbol s = getDsymbolWithoutExpCtx(o); 788 if (!s || !s.ident) 789 { 790 e.error("argument `%s` has no identifier", o.toChars()); 791 return new ErrorExp(); 792 } 793 id = s.ident; 794 } 795 796 auto se = new StringExp(e.loc, id.toString()); 797 return se.expressionSemantic(sc); 798 } 799 if (e.ident == Id.getProtection) 800 { 801 if (dim != 1) 802 return dimError(1); 803 804 Scope* sc2 = sc.push(); 805 sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; 806 bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); 807 sc2.pop(); 808 if (!ok) 809 return new ErrorExp(); 810 811 auto o = (*e.args)[0]; 812 auto s = getDsymbolWithoutExpCtx(o); 813 if (!s) 814 { 815 if (!isError(o)) 816 e.error("argument `%s` has no protection", o.toChars()); 817 return new ErrorExp(); 818 } 819 if (s.semanticRun == PASS.init) 820 s.dsymbolSemantic(null); 821 822 auto protName = protectionToString(s.prot().kind); // TODO: How about package(names) 823 assert(protName); 824 auto se = new StringExp(e.loc, protName); 825 return se.expressionSemantic(sc); 826 } 827 if (e.ident == Id.parent) 828 { 829 if (dim != 1) 830 return dimError(1); 831 832 auto o = (*e.args)[0]; 833 auto s = getDsymbolWithoutExpCtx(o); 834 if (s) 835 { 836 // https://issues.dlang.org/show_bug.cgi?id=12496 837 // Consider: 838 // class T1 839 // { 840 // class C(uint value) { } 841 // } 842 // __traits(parent, T1.C!2) 843 if (auto ad = s.isAggregateDeclaration()) // `s` is `C` 844 { 845 if (ad.isNested()) // `C` is nested 846 { 847 if (auto p = s.toParent()) // `C`'s parent is `C!2`, believe it or not 848 { 849 if (p.isTemplateInstance()) // `C!2` is a template instance 850 { 851 s = p; // `C!2`'s parent is `T1` 852 auto td = (cast(TemplateInstance)p).tempdecl; 853 if (td) 854 s = td; // get the declaration context just in case there's two contexts 855 } 856 } 857 } 858 } 859 860 if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943 861 s = fd.toAliasFunc(); 862 if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922 863 s = s.toParent(); 864 } 865 if (!s || s.isImport()) 866 { 867 e.error("argument `%s` has no parent", o.toChars()); 868 return new ErrorExp(); 869 } 870 871 if (auto f = s.isFuncDeclaration()) 872 { 873 if (auto td = getFuncTemplateDecl(f)) 874 { 875 if (td.overroot) // if not start of overloaded list of TemplateDeclaration's 876 td = td.overroot; // then get the start 877 Expression ex = new TemplateExp(e.loc, td, f); 878 ex = ex.expressionSemantic(sc); 879 return ex; 880 } 881 if (auto fld = f.isFuncLiteralDeclaration()) 882 { 883 // Directly translate to VarExp instead of FuncExp 884 Expression ex = new VarExp(e.loc, fld, true); 885 return ex.expressionSemantic(sc); 886 } 887 } 888 return symbolToExp(s, e.loc, sc, false); 889 } 890 if (e.ident == Id.hasMember || 891 e.ident == Id.getMember || 892 e.ident == Id.getOverloads || 893 e.ident == Id.getVirtualMethods || 894 e.ident == Id.getVirtualFunctions) 895 { 896 if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads)) 897 return dimError(2); 898 899 auto o = (*e.args)[0]; 900 auto ex = isExpression((*e.args)[1]); 901 if (!ex) 902 { 903 e.error("expression expected as second argument of __traits `%s`", e.ident.toChars()); 904 return new ErrorExp(); 905 } 906 ex = ex.ctfeInterpret(); 907 908 bool includeTemplates = false; 909 if (dim == 3 && e.ident == Id.getOverloads) 910 { 911 auto b = isExpression((*e.args)[2]); 912 b = b.ctfeInterpret(); 913 if (!b.type.equals(Type.tbool)) 914 { 915 e.error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars()); 916 return new ErrorExp(); 917 } 918 includeTemplates = b.isBool(true); 919 } 920 921 StringExp se = ex.toStringExp(); 922 if (!se || se.len == 0) 923 { 924 e.error("string expected as second argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars()); 925 return new ErrorExp(); 926 } 927 se = se.toUTF8(sc); 928 929 if (se.sz != 1) 930 { 931 e.error("string must be chars"); 932 return new ErrorExp(); 933 } 934 auto id = Identifier.idPool(se.peekString()); 935 936 /* Prefer dsymbol, because it might need some runtime contexts. 937 */ 938 Dsymbol sym = getDsymbol(o); 939 if (sym) 940 { 941 if (e.ident == Id.hasMember) 942 { 943 if (auto sm = sym.search(e.loc, id)) 944 return True(); 945 } 946 ex = new DsymbolExp(e.loc, sym); 947 ex = new DotIdExp(e.loc, ex, id); 948 } 949 else if (auto t = isType(o)) 950 ex = typeDotIdExp(e.loc, t, id); 951 else if (auto ex2 = isExpression(o)) 952 ex = new DotIdExp(e.loc, ex2, id); 953 else 954 { 955 e.error("invalid first argument"); 956 return new ErrorExp(); 957 } 958 959 // ignore symbol visibility and disable access checks for these traits 960 Scope* scx = sc.push(); 961 scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; 962 scope (exit) scx.pop(); 963 964 if (e.ident == Id.hasMember) 965 { 966 /* Take any errors as meaning it wasn't found 967 */ 968 ex = ex.trySemantic(scx); 969 return ex ? True() : False(); 970 } 971 else if (e.ident == Id.getMember) 972 { 973 if (ex.op == TOK.dotIdentifier) 974 // Prevent semantic() from replacing Symbol with its initializer 975 (cast(DotIdExp)ex).wantsym = true; 976 ex = ex.expressionSemantic(scx); 977 return ex; 978 } 979 else if (e.ident == Id.getVirtualFunctions || 980 e.ident == Id.getVirtualMethods || 981 e.ident == Id.getOverloads) 982 { 983 uint errors = global.errors; 984 Expression eorig = ex; 985 ex = ex.expressionSemantic(scx); 986 if (errors < global.errors) 987 e.error("`%s` cannot be resolved", eorig.toChars()); 988 989 /* Create tuple of functions of ex 990 */ 991 auto exps = new Expressions(); 992 Dsymbol f; 993 if (ex.op == TOK.variable) 994 { 995 VarExp ve = cast(VarExp)ex; 996 f = ve.var.isFuncDeclaration(); 997 ex = null; 998 } 999 else if (ex.op == TOK.dotVariable) 1000 { 1001 DotVarExp dve = cast(DotVarExp)ex; 1002 f = dve.var.isFuncDeclaration(); 1003 if (dve.e1.op == TOK.dotType || dve.e1.op == TOK.this_) 1004 ex = null; 1005 else 1006 ex = dve.e1; 1007 } 1008 else if (ex.op == TOK.template_) 1009 { 1010 VarExp ve = cast(VarExp)ex; 1011 auto td = ve.var.isTemplateDeclaration(); 1012 f = td; 1013 if (td && td.funcroot) 1014 f = td.funcroot; 1015 ex = null; 1016 } 1017 1018 bool[string] funcTypeHash; 1019 1020 /* Compute the function signature and insert it in the 1021 * hashtable, if not present. This is needed so that 1022 * traits(getOverlods, F3, "visit") does not count `int visit(int)` 1023 * twice in the following example: 1024 * 1025 * ============================================= 1026 * interface F1 { int visit(int);} 1027 * interface F2 { int visit(int); void visit(); } 1028 * interface F3 : F2, F1 {} 1029 *============================================== 1030 */ 1031 void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e) 1032 { 1033 auto signature = fd.type.toString(); 1034 //printf("%s - %s\n", fd.toChars, signature); 1035 if (signature !in funcTypeHash) 1036 { 1037 funcTypeHash[signature] = true; 1038 exps.push(e); 1039 } 1040 } 1041 1042 int dg(Dsymbol s) 1043 { 1044 if (includeTemplates) 1045 { 1046 exps.push(new DsymbolExp(Loc.initial, s, false)); 1047 return 0; 1048 } 1049 auto fd = s.isFuncDeclaration(); 1050 if (!fd) 1051 return 0; 1052 if (e.ident == Id.getVirtualFunctions && !fd.isVirtual()) 1053 return 0; 1054 if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod()) 1055 return 0; 1056 1057 auto fa = new FuncAliasDeclaration(fd.ident, fd, false); 1058 fa.protection = fd.protection; 1059 1060 auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false) 1061 : new DsymbolExp(Loc.initial, fa, false); 1062 1063 // if the parent is an interface declaration 1064 // we must check for functions with the same signature 1065 // in different inherited interfaces 1066 if (sym && sym.isInterfaceDeclaration()) 1067 insertInterfaceInheritedFunction(fd, e); 1068 else 1069 exps.push(e); 1070 return 0; 1071 } 1072 1073 InterfaceDeclaration ifd = null; 1074 if (sym) 1075 ifd = sym.isInterfaceDeclaration(); 1076 // If the symbol passed as a parameter is an 1077 // interface that inherits other interfaces 1078 overloadApply(f, &dg); 1079 if (ifd && ifd.interfaces && f) 1080 { 1081 // check the overloads of each inherited interface individually 1082 foreach (bc; ifd.interfaces) 1083 { 1084 if (auto fd = bc.sym.search(e.loc, f.ident)) 1085 overloadApply(fd, &dg); 1086 } 1087 } 1088 1089 auto tup = new TupleExp(e.loc, exps); 1090 return tup.expressionSemantic(scx); 1091 } 1092 else 1093 assert(0); 1094 } 1095 if (e.ident == Id.classInstanceSize) 1096 { 1097 if (dim != 1) 1098 return dimError(1); 1099 1100 auto o = (*e.args)[0]; 1101 auto s = getDsymbol(o); 1102 auto cd = s ? s.isClassDeclaration() : null; 1103 if (!cd) 1104 { 1105 e.error("first argument is not a class"); 1106 return new ErrorExp(); 1107 } 1108 if (cd.sizeok != Sizeok.done) 1109 { 1110 cd.size(e.loc); 1111 } 1112 if (cd.sizeok != Sizeok.done) 1113 { 1114 e.error("%s `%s` is forward referenced", cd.kind(), cd.toChars()); 1115 return new ErrorExp(); 1116 } 1117 1118 return new IntegerExp(e.loc, cd.structsize, Type.tsize_t); 1119 } 1120 if (e.ident == Id.getAliasThis) 1121 { 1122 if (dim != 1) 1123 return dimError(1); 1124 1125 auto o = (*e.args)[0]; 1126 auto s = getDsymbol(o); 1127 auto ad = s ? s.isAggregateDeclaration() : null; 1128 1129 auto exps = new Expressions(); 1130 if (ad && ad.aliasthis) 1131 exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString())); 1132 Expression ex = new TupleExp(e.loc, exps); 1133 ex = ex.expressionSemantic(sc); 1134 return ex; 1135 } 1136 if (e.ident == Id.getAttributes) 1137 { 1138 /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that 1139 * a symbol should not be folded to a constant. 1140 * Bit 1 means don't convert Parameter to Type if Parameter has an identifier 1141 */ 1142 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3)) 1143 return new ErrorExp(); 1144 1145 if (dim != 1) 1146 return dimError(1); 1147 1148 auto o = (*e.args)[0]; 1149 auto po = isParameter(o); 1150 auto s = getDsymbolWithoutExpCtx(o); 1151 UserAttributeDeclaration udad = null; 1152 if (po) 1153 { 1154 udad = po.userAttribDecl; 1155 } 1156 else if (s) 1157 { 1158 if (s.isImport()) 1159 { 1160 s = s.isImport().mod; 1161 } 1162 //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope); 1163 udad = s.userAttribDecl; 1164 } 1165 else 1166 { 1167 version (none) 1168 { 1169 Expression x = isExpression(o); 1170 Type t = isType(o); 1171 if (x) 1172 printf("e = %s %s\n", Token.toChars(x.op), x.toChars()); 1173 if (t) 1174 printf("t = %d %s\n", t.ty, t.toChars()); 1175 } 1176 e.error("first argument is not a symbol"); 1177 return new ErrorExp(); 1178 } 1179 1180 auto exps = udad ? udad.getAttributes() : new Expressions(); 1181 auto tup = new TupleExp(e.loc, exps); 1182 return tup.expressionSemantic(sc); 1183 } 1184 if (e.ident == Id.getFunctionAttributes) 1185 { 1186 /* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs. 1187 * https://dlang.org/spec/traits.html#getFunctionAttributes 1188 */ 1189 if (dim != 1) 1190 return dimError(1); 1191 1192 FuncDeclaration fd; 1193 TypeFunction tf = toTypeFunction((*e.args)[0], fd); 1194 1195 if (!tf) 1196 { 1197 e.error("first argument is not a function"); 1198 return new ErrorExp(); 1199 } 1200 1201 auto mods = new Expressions(); 1202 1203 void addToMods(string str) 1204 { 1205 mods.push(new StringExp(Loc.initial, str)); 1206 } 1207 tf.modifiersApply(&addToMods); 1208 tf.attributesApply(&addToMods, TRUSTformatSystem); 1209 1210 auto tup = new TupleExp(e.loc, mods); 1211 return tup.expressionSemantic(sc); 1212 } 1213 if (e.ident == Id.isReturnOnStack) 1214 { 1215 /* Extract as a boolean if function return value is on the stack 1216 * https://dlang.org/spec/traits.html#isReturnOnStack 1217 */ 1218 if (dim != 1) 1219 return dimError(1); 1220 1221 RootObject o = (*e.args)[0]; 1222 FuncDeclaration fd; 1223 TypeFunction tf = toTypeFunction(o, fd); 1224 1225 if (!tf) 1226 { 1227 e.error("argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars()); 1228 return new ErrorExp(); 1229 } 1230 1231 bool value = target.isReturnOnStack(tf, fd && fd.needThis()); 1232 return IntegerExp.createBool(value); 1233 } 1234 if (e.ident == Id.getFunctionVariadicStyle) 1235 { 1236 /* Accept a symbol or a type. Returns one of the following: 1237 * "none" not a variadic function 1238 * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments` 1239 * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg 1240 * "typesafe" void typesafe(T[] ...) 1241 */ 1242 // get symbol linkage as a string 1243 if (dim != 1) 1244 return dimError(1); 1245 1246 LINK link; 1247 VarArg varargs; 1248 auto o = (*e.args)[0]; 1249 1250 FuncDeclaration fd; 1251 TypeFunction tf = toTypeFunction(o, fd); 1252 1253 if (tf) 1254 { 1255 link = tf.linkage; 1256 varargs = tf.parameterList.varargs; 1257 } 1258 else 1259 { 1260 if (!fd) 1261 { 1262 e.error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars()); 1263 return new ErrorExp(); 1264 } 1265 link = fd.linkage; 1266 varargs = fd.getParameterList().varargs; 1267 } 1268 string style; 1269 final switch (varargs) 1270 { 1271 case VarArg.none: style = "none"; break; 1272 case VarArg.variadic: style = (link == LINK.d) 1273 ? "argptr" 1274 : "stdarg"; break; 1275 case VarArg.typesafe: style = "typesafe"; break; 1276 } 1277 auto se = new StringExp(e.loc, style); 1278 return se.expressionSemantic(sc); 1279 } 1280 if (e.ident == Id.getParameterStorageClasses) 1281 { 1282 /* Accept a function symbol or a type, followed by a parameter index. 1283 * Returns a tuple of strings of the parameter's storage classes. 1284 */ 1285 // get symbol linkage as a string 1286 if (dim != 2) 1287 return dimError(2); 1288 1289 auto o = (*e.args)[0]; 1290 auto o1 = (*e.args)[1]; 1291 1292 FuncDeclaration fd; 1293 TypeFunction tf = toTypeFunction(o, fd); 1294 1295 ParameterList fparams; 1296 if (tf) 1297 fparams = tf.parameterList; 1298 else if (fd) 1299 fparams = fd.getParameterList(); 1300 else 1301 { 1302 e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function", 1303 o.toChars(), o1.toChars()); 1304 return new ErrorExp(); 1305 } 1306 1307 StorageClass stc; 1308 1309 // Set stc to storage class of the ith parameter 1310 auto ex = isExpression((*e.args)[1]); 1311 if (!ex) 1312 { 1313 e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`", 1314 o.toChars(), o1.toChars()); 1315 return new ErrorExp(); 1316 } 1317 ex = ex.ctfeInterpret(); 1318 auto ii = ex.toUInteger(); 1319 if (ii >= fparams.length) 1320 { 1321 e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars()); 1322 return new ErrorExp(); 1323 } 1324 1325 uint n = cast(uint)ii; 1326 Parameter p = fparams[n]; 1327 stc = p.storageClass; 1328 1329 // This mirrors hdrgen.visit(Parameter p) 1330 if (p.type && p.type.mod & MODFlags.shared_) 1331 stc &= ~STC.shared_; 1332 1333 auto exps = new Expressions; 1334 1335 void push(string s) 1336 { 1337 exps.push(new StringExp(e.loc, s)); 1338 } 1339 1340 if (stc & STC.auto_) 1341 push("auto"); 1342 if (stc & STC.return_) 1343 push("return"); 1344 1345 if (stc & STC.out_) 1346 push("out"); 1347 else if (stc & STC.ref_) 1348 push("ref"); 1349 else if (stc & STC.in_) 1350 push("in"); 1351 else if (stc & STC.lazy_) 1352 push("lazy"); 1353 else if (stc & STC.alias_) 1354 push("alias"); 1355 1356 if (stc & STC.const_) 1357 push("const"); 1358 if (stc & STC.immutable_) 1359 push("immutable"); 1360 if (stc & STC.wild) 1361 push("inout"); 1362 if (stc & STC.shared_) 1363 push("shared"); 1364 if (stc & STC.scope_ && !(stc & STC.scopeinferred)) 1365 push("scope"); 1366 1367 auto tup = new TupleExp(e.loc, exps); 1368 return tup.expressionSemantic(sc); 1369 } 1370 if (e.ident == Id.getLinkage) 1371 { 1372 // get symbol linkage as a string 1373 if (dim != 1) 1374 return dimError(1); 1375 1376 LINK link; 1377 auto o = (*e.args)[0]; 1378 1379 FuncDeclaration fd; 1380 TypeFunction tf = toTypeFunction(o, fd); 1381 1382 if (tf) 1383 link = tf.linkage; 1384 else 1385 { 1386 auto s = getDsymbol(o); 1387 Declaration d; 1388 AggregateDeclaration agg; 1389 if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null)) 1390 { 1391 e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars()); 1392 return new ErrorExp(); 1393 } 1394 if (d !is null) 1395 link = d.linkage; 1396 else final switch (agg.classKind) 1397 { 1398 case ClassKind.d: 1399 link = LINK.d; 1400 break; 1401 case ClassKind.cpp: 1402 link = LINK.cpp; 1403 break; 1404 case ClassKind.objc: 1405 link = LINK.objc; 1406 break; 1407 } 1408 } 1409 auto linkage = linkageToChars(link); 1410 auto se = new StringExp(e.loc, linkage.toDString()); 1411 return se.expressionSemantic(sc); 1412 } 1413 if (e.ident == Id.allMembers || 1414 e.ident == Id.derivedMembers) 1415 { 1416 if (dim != 1) 1417 return dimError(1); 1418 1419 auto o = (*e.args)[0]; 1420 auto s = getDsymbol(o); 1421 if (!s) 1422 { 1423 e.error("In expression `%s` `%s` can't have members", e.toChars(), o.toChars()); 1424 e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars()); 1425 1426 return new ErrorExp(); 1427 } 1428 if (auto imp = s.isImport()) 1429 { 1430 // https://issues.dlang.org/show_bug.cgi?id=9692 1431 s = imp.mod; 1432 } 1433 1434 auto sds = s.isScopeDsymbol(); 1435 if (!sds || sds.isTemplateDeclaration()) 1436 { 1437 e.error("In expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars()); 1438 e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars()); 1439 return new ErrorExp(); 1440 } 1441 1442 auto idents = new Identifiers(); 1443 1444 int pushIdentsDg(size_t n, Dsymbol sm) 1445 { 1446 if (!sm) 1447 return 1; 1448 1449 // skip local symbols, such as static foreach loop variables 1450 if (auto decl = sm.isDeclaration()) 1451 { 1452 if (decl.storage_class & STC.local) 1453 { 1454 return 0; 1455 } 1456 } 1457 1458 //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars()); 1459 if (sm.ident) 1460 { 1461 // https://issues.dlang.org/show_bug.cgi?id=10096 1462 // https://issues.dlang.org/show_bug.cgi?id=10100 1463 // Skip over internal members in __traits(allMembers) 1464 if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) || 1465 (sm.isDtorDeclaration() && sm.ident != Id.dtor) || 1466 (sm.isPostBlitDeclaration() && sm.ident != Id.postblit) || 1467 sm.isInvariantDeclaration() || 1468 sm.isUnitTestDeclaration()) 1469 1470 { 1471 return 0; 1472 } 1473 if (sm.ident == Id.empty) 1474 { 1475 return 0; 1476 } 1477 if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177 1478 return 0; 1479 if (!sds.isModule() && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057 1480 return 0; 1481 1482 //printf("\t%s\n", sm.ident.toChars()); 1483 1484 /* Skip if already present in idents[] 1485 */ 1486 foreach (id; *idents) 1487 { 1488 if (id == sm.ident) 1489 return 0; 1490 1491 // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop. 1492 debug 1493 { 1494 import core.stdc.string : strcmp; 1495 assert(strcmp(id.toChars(), sm.ident.toChars()) != 0); 1496 } 1497 } 1498 idents.push(sm.ident); 1499 } 1500 else if (auto ed = sm.isEnumDeclaration()) 1501 { 1502 ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg); 1503 } 1504 return 0; 1505 } 1506 1507 ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg); 1508 auto cd = sds.isClassDeclaration(); 1509 if (cd && e.ident == Id.allMembers) 1510 { 1511 if (cd.semanticRun < PASS.semanticdone) 1512 cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668 1513 // Try to resolve forward reference 1514 1515 void pushBaseMembersDg(ClassDeclaration cd) 1516 { 1517 for (size_t i = 0; i < cd.baseclasses.dim; i++) 1518 { 1519 auto cb = (*cd.baseclasses)[i].sym; 1520 assert(cb); 1521 ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg); 1522 if (cb.baseclasses.dim) 1523 pushBaseMembersDg(cb); 1524 } 1525 } 1526 1527 pushBaseMembersDg(cd); 1528 } 1529 1530 // Turn Identifiers into StringExps reusing the allocated array 1531 assert(Expressions.sizeof == Identifiers.sizeof); 1532 auto exps = cast(Expressions*)idents; 1533 foreach (i, id; *idents) 1534 { 1535 auto se = new StringExp(e.loc, id.toString()); 1536 (*exps)[i] = se; 1537 } 1538 1539 /* Making this a tuple is more flexible, as it can be statically unrolled. 1540 * To make an array literal, enclose __traits in [ ]: 1541 * [ __traits(allMembers, ...) ] 1542 */ 1543 Expression ex = new TupleExp(e.loc, exps); 1544 ex = ex.expressionSemantic(sc); 1545 return ex; 1546 } 1547 if (e.ident == Id.compiles) 1548 { 1549 /* Determine if all the objects - types, expressions, or symbols - 1550 * compile without error 1551 */ 1552 if (!dim) 1553 return False(); 1554 1555 foreach (o; *e.args) 1556 { 1557 uint errors = global.startGagging(); 1558 Scope* sc2 = sc.push(); 1559 sc2.tinst = null; 1560 sc2.minst = null; 1561 sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst; 1562 1563 bool err = false; 1564 1565 auto t = isType(o); 1566 while (t) 1567 { 1568 if (auto tm = t.isTypeMixin()) 1569 { 1570 /* The mixin string could be a type or an expression. 1571 * Have to try compiling it to see. 1572 */ 1573 OutBuffer buf; 1574 if (expressionsToString(buf, sc, tm.exps)) 1575 { 1576 err = true; 1577 break; 1578 } 1579 const olderrors = global.errors; 1580 const len = buf.length; 1581 buf.writeByte(0); 1582 const str = buf.extractSlice()[0 .. len]; 1583 scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false); 1584 p.nextToken(); 1585 //printf("p.loc.linnum = %d\n", p.loc.linnum); 1586 1587 o = p.parseTypeOrAssignExp(TOK.endOfFile); 1588 p.reportDiagnostics(); 1589 if (olderrors != global.errors || p.token.value != TOK.endOfFile) 1590 { 1591 err = true; 1592 break; 1593 } 1594 t = o.isType(); 1595 } 1596 else 1597 break; 1598 } 1599 1600 if (!err) 1601 { 1602 auto ex = t ? t.typeToExpression() : isExpression(o); 1603 if (!ex && t) 1604 { 1605 Dsymbol s; 1606 t.resolve(e.loc, sc2, &ex, &t, &s); 1607 if (t) 1608 { 1609 t.typeSemantic(e.loc, sc2); 1610 if (t.ty == Terror) 1611 err = true; 1612 } 1613 else if (s && s.errors) 1614 err = true; 1615 } 1616 if (ex) 1617 { 1618 ex = ex.expressionSemantic(sc2); 1619 ex = resolvePropertiesOnly(sc2, ex); 1620 ex = ex.optimize(WANTvalue); 1621 if (sc2.func && sc2.func.type.ty == Tfunction) 1622 { 1623 const tf = cast(TypeFunction)sc2.func.type; 1624 err |= tf.isnothrow && canThrow(ex, sc2.func, false); 1625 } 1626 ex = checkGC(sc2, ex); 1627 if (ex.op == TOK.error) 1628 err = true; 1629 } 1630 } 1631 1632 // Carefully detach the scope from the parent and throw it away as 1633 // we only need it to evaluate the expression 1634 // https://issues.dlang.org/show_bug.cgi?id=15428 1635 sc2.detach(); 1636 1637 if (global.endGagging(errors) || err) 1638 { 1639 return False(); 1640 } 1641 } 1642 return True(); 1643 } 1644 if (e.ident == Id.isSame) 1645 { 1646 /* Determine if two symbols are the same 1647 */ 1648 if (dim != 2) 1649 return dimError(2); 1650 1651 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 0)) 1652 return new ErrorExp(); 1653 1654 1655 auto o1 = (*e.args)[0]; 1656 auto o2 = (*e.args)[1]; 1657 1658 static FuncLiteralDeclaration isLambda(RootObject oarg) 1659 { 1660 if (auto t = isDsymbol(oarg)) 1661 { 1662 if (auto td = t.isTemplateDeclaration()) 1663 { 1664 if (td.members && td.members.dim == 1) 1665 { 1666 if (auto fd = (*td.members)[0].isFuncLiteralDeclaration()) 1667 return fd; 1668 } 1669 } 1670 } 1671 else if (auto ea = isExpression(oarg)) 1672 { 1673 if (ea.op == TOK.function_) 1674 { 1675 if (auto fe = cast(FuncExp)ea) 1676 return fe.fd; 1677 } 1678 } 1679 1680 return null; 1681 } 1682 1683 auto l1 = isLambda(o1); 1684 auto l2 = isLambda(o2); 1685 1686 if (l1 && l2) 1687 { 1688 import dmd.lambdacomp : isSameFuncLiteral; 1689 if (isSameFuncLiteral(l1, l2, sc)) 1690 return True(); 1691 } 1692 1693 // issue 12001, allow isSame, <BasicType>, <BasicType> 1694 Type t1 = isType(o1); 1695 Type t2 = isType(o2); 1696 if (t1 && t2 && t1.equals(t2)) 1697 return True(); 1698 1699 auto s1 = getDsymbol(o1); 1700 auto s2 = getDsymbol(o2); 1701 //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars()); 1702 version (none) 1703 { 1704 printf("o1: %p\n", o1); 1705 printf("o2: %p\n", o2); 1706 if (!s1) 1707 { 1708 if (auto ea = isExpression(o1)) 1709 printf("%s\n", ea.toChars()); 1710 if (auto ta = isType(o1)) 1711 printf("%s\n", ta.toChars()); 1712 return False(); 1713 } 1714 else 1715 printf("%s %s\n", s1.kind(), s1.toChars()); 1716 } 1717 if (!s1 && !s2) 1718 { 1719 auto ea1 = isExpression(o1); 1720 auto ea2 = isExpression(o2); 1721 if (ea1 && ea2) 1722 { 1723 if (ea1.equals(ea2)) 1724 return True(); 1725 } 1726 } 1727 if (!s1 || !s2) 1728 return False(); 1729 1730 s1 = s1.toAlias(); 1731 s2 = s2.toAlias(); 1732 1733 if (auto fa1 = s1.isFuncAliasDeclaration()) 1734 s1 = fa1.toAliasFunc(); 1735 if (auto fa2 = s2.isFuncAliasDeclaration()) 1736 s2 = fa2.toAliasFunc(); 1737 1738 // https://issues.dlang.org/show_bug.cgi?id=11259 1739 // compare import symbol to a package symbol 1740 static bool cmp(Dsymbol s1, Dsymbol s2) 1741 { 1742 auto imp = s1.isImport(); 1743 return imp && imp.pkg && imp.pkg == s2.isPackage(); 1744 } 1745 1746 if (cmp(s1,s2) || cmp(s2,s1)) 1747 return True(); 1748 1749 if (s1 == s2) 1750 return True(); 1751 1752 // https://issues.dlang.org/show_bug.cgi?id=18771 1753 // OverloadSets are equal if they contain the same functions 1754 auto overSet1 = s1.isOverloadSet(); 1755 if (!overSet1) 1756 return False(); 1757 1758 auto overSet2 = s2.isOverloadSet(); 1759 if (!overSet2) 1760 return False(); 1761 1762 if (overSet1.a.dim != overSet2.a.dim) 1763 return False(); 1764 1765 // OverloadSets contain array of Dsymbols => O(n*n) 1766 // to compare for equality as the order of overloads 1767 // might not be the same 1768 Lnext: 1769 foreach(overload1; overSet1.a) 1770 { 1771 foreach(overload2; overSet2.a) 1772 { 1773 if (overload1 == overload2) 1774 continue Lnext; 1775 } 1776 return False(); 1777 } 1778 return True(); 1779 } 1780 if (e.ident == Id.getUnitTests) 1781 { 1782 if (dim != 1) 1783 return dimError(1); 1784 1785 auto o = (*e.args)[0]; 1786 auto s = getDsymbolWithoutExpCtx(o); 1787 if (!s) 1788 { 1789 e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate", 1790 o.toChars()); 1791 return new ErrorExp(); 1792 } 1793 if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990 1794 s = imp.mod; 1795 1796 auto sds = s.isScopeDsymbol(); 1797 if (!sds) 1798 { 1799 e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s", 1800 s.toChars(), s.kind()); 1801 return new ErrorExp(); 1802 } 1803 1804 auto exps = new Expressions(); 1805 if (global.params.useUnitTests) 1806 { 1807 bool[void*] uniqueUnitTests; 1808 1809 void symbolDg(Dsymbol s) 1810 { 1811 if (auto ad = s.isAttribDeclaration()) 1812 { 1813 ad.include(null).foreachDsymbol(&symbolDg); 1814 } 1815 else if (auto ud = s.isUnitTestDeclaration()) 1816 { 1817 if (cast(void*)ud in uniqueUnitTests) 1818 return; 1819 1820 uniqueUnitTests[cast(void*)ud] = true; 1821 1822 auto ad = new FuncAliasDeclaration(ud.ident, ud, false); 1823 ad.protection = ud.protection; 1824 1825 auto e = new DsymbolExp(Loc.initial, ad, false); 1826 exps.push(e); 1827 } 1828 } 1829 1830 sds.members.foreachDsymbol(&symbolDg); 1831 } 1832 auto te = new TupleExp(e.loc, exps); 1833 return te.expressionSemantic(sc); 1834 } 1835 if (e.ident == Id.getVirtualIndex) 1836 { 1837 if (dim != 1) 1838 return dimError(1); 1839 1840 auto o = (*e.args)[0]; 1841 auto s = getDsymbolWithoutExpCtx(o); 1842 1843 auto fd = s ? s.isFuncDeclaration() : null; 1844 if (!fd) 1845 { 1846 e.error("first argument to __traits(getVirtualIndex) must be a function"); 1847 return new ErrorExp(); 1848 } 1849 1850 fd = fd.toAliasFunc(); // Necessary to support multiple overloads. 1851 return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t); 1852 } 1853 if (e.ident == Id.getPointerBitmap) 1854 { 1855 return pointerBitmap(e); 1856 } 1857 if (e.ident == Id.isZeroInit) 1858 { 1859 if (dim != 1) 1860 return dimError(1); 1861 1862 auto o = (*e.args)[0]; 1863 Type t = isType(o); 1864 if (!t) 1865 { 1866 e.error("type expected as second argument of __traits `%s` instead of `%s`", 1867 e.ident.toChars(), o.toChars()); 1868 return new ErrorExp(); 1869 } 1870 1871 Type tb = t.baseElemOf(); 1872 return tb.isZeroInit(e.loc) ? True() : False(); 1873 } 1874 if (e.ident == Id.getTargetInfo) 1875 { 1876 if (dim != 1) 1877 return dimError(1); 1878 1879 auto ex = isExpression((*e.args)[0]); 1880 StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null; 1881 if (!ex || !se || se.len == 0) 1882 { 1883 e.error("string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars()); 1884 return new ErrorExp(); 1885 } 1886 se = se.toUTF8(sc); 1887 1888 const slice = se.peekString(); 1889 Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0 1890 if (!r) 1891 { 1892 e.error("`getTargetInfo` key `\"%.*s\"` not supported by this implementation", 1893 cast(int)slice.length, slice.ptr); 1894 return new ErrorExp(); 1895 } 1896 return r.expressionSemantic(sc); 1897 } 1898 if (e.ident == Id.getLocation) 1899 { 1900 if (dim != 1) 1901 return dimError(1); 1902 auto arg0 = (*e.args)[0]; 1903 Dsymbol s = getDsymbolWithoutExpCtx(arg0); 1904 if (!s || !s.loc.isValid()) 1905 { 1906 e.error("can only get the location of a symbol, not `%s`", arg0.toChars()); 1907 return new ErrorExp(); 1908 } 1909 1910 const fd = s.isFuncDeclaration(); 1911 // FIXME:td.overnext is always set, even when using an index on it 1912 //const td = s.isTemplateDeclaration(); 1913 if ((fd && fd.overnext) /*|| (td && td.overnext)*/) 1914 { 1915 e.error("cannot get location of an overload set, " ~ 1916 "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~ 1917 "to get the Nth overload", 1918 arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr); 1919 return new ErrorExp(); 1920 } 1921 1922 auto exps = new Expressions(3); 1923 (*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString()); 1924 (*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32); 1925 (*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32); 1926 auto tup = new TupleExp(e.loc, exps); 1927 return tup.expressionSemantic(sc); 1928 } 1929 1930 static const(char)[] trait_search_fp(const(char)[] seed, ref int cost) 1931 { 1932 //printf("trait_search_fp('%s')\n", seed); 1933 if (!seed.length) 1934 return null; 1935 cost = 0; 1936 const sv = traitsStringTable.lookup(seed); 1937 return sv ? sv.toString() : null; 1938 } 1939 1940 if (auto sub = speller!trait_search_fp(e.ident.toString())) 1941 e.error("unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), sub.length, sub.ptr); 1942 else 1943 e.error("unrecognized trait `%s`", e.ident.toChars()); 1944 return new ErrorExp(); 1945 }