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