1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _traits.d) 9 */ 10 11 module ddmd.traits; 12 13 import core.stdc.stdio; 14 import core.stdc..string; 15 import ddmd.aggregate; 16 import ddmd.arraytypes; 17 import ddmd.canthrow; 18 import ddmd.dclass; 19 import ddmd.declaration; 20 import ddmd.dscope; 21 import ddmd.dsymbol; 22 import ddmd.dtemplate; 23 import ddmd.errors; 24 import ddmd.expression; 25 import ddmd.func; 26 import ddmd.globals; 27 import ddmd.hdrgen; 28 import ddmd.id; 29 import ddmd.identifier; 30 import ddmd.mtype; 31 import ddmd.nogc; 32 import ddmd.root.array; 33 import ddmd.root.speller; 34 import ddmd.root.stringtable; 35 import ddmd.tokens; 36 import ddmd.visitor; 37 38 enum LOGSEMANTIC = false; 39 40 /************************ TraitsExp ************************************/ 41 42 // callback for TypeFunction::attributesApply 43 struct PushAttributes 44 { 45 Expressions* mods; 46 47 extern (C++) static int fp(void* param, const(char)* str) 48 { 49 PushAttributes* p = cast(PushAttributes*)param; 50 p.mods.push(new StringExp(Loc(), cast(char*)str)); 51 return 0; 52 } 53 } 54 55 extern (C++) __gshared StringTable traitsStringTable; 56 57 static this() 58 { 59 static immutable string[] names = 60 [ 61 "isAbstractClass", 62 "isArithmetic", 63 "isAssociativeArray", 64 "isFinalClass", 65 "isPOD", 66 "isNested", 67 "isFloating", 68 "isIntegral", 69 "isScalar", 70 "isStaticArray", 71 "isUnsigned", 72 "isVirtualFunction", 73 "isVirtualMethod", 74 "isAbstractFunction", 75 "isFinalFunction", 76 "isOverrideFunction", 77 "isStaticFunction", 78 "isRef", 79 "isOut", 80 "isLazy", 81 "hasMember", 82 "identifier", 83 "getProtection", 84 "parent", 85 "getMember", 86 "getOverloads", 87 "getVirtualFunctions", 88 "getVirtualMethods", 89 "classInstanceSize", 90 "allMembers", 91 "derivedMembers", 92 "isSame", 93 "compiles", 94 "parameters", 95 "getAliasThis", 96 "getAttributes", 97 "getFunctionAttributes", 98 "getUnitTests", 99 "getVirtualIndex", 100 "getPointerBitmap", 101 ]; 102 103 traitsStringTable._init(40); 104 105 foreach (s; names) 106 { 107 auto sv = traitsStringTable.insert(s.ptr, s.length, cast(void*)s.ptr); 108 assert(sv); 109 } 110 } 111 112 /** 113 * get an array of size_t values that indicate possible pointer words in memory 114 * if interpreted as the type given as argument 115 * the first array element is the size of the type for independent interpretation 116 * of the array 117 * following elements bits represent one word (4/8 bytes depending on the target 118 * architecture). If set the corresponding memory might contain a pointer/reference. 119 * 120 * [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...] 121 */ 122 extern (C++) Expression pointerBitmap(TraitsExp e) 123 { 124 if (!e.args || e.args.dim != 1) 125 { 126 error(e.loc, "a single type expected for trait pointerBitmap"); 127 return new ErrorExp(); 128 } 129 130 Type t = getType((*e.args)[0]); 131 if (!t) 132 { 133 error(e.loc, "%s is not a type", (*e.args)[0].toChars()); 134 return new ErrorExp(); 135 } 136 137 d_uns64 sz; 138 if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration()) 139 sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(e.loc); 140 else 141 sz = t.size(e.loc); 142 if (sz == SIZE_INVALID) 143 return new ErrorExp(); 144 145 const sz_size_t = Type.tsize_t.size(e.loc); 146 if (sz > sz.max - sz_size_t) 147 { 148 error(e.loc, "size overflow for type %s", t.toChars()); 149 return new ErrorExp(); 150 } 151 152 d_uns64 bitsPerWord = sz_size_t * 8; 153 d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t; 154 d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord; 155 156 Array!(d_uns64) data; 157 data.setDim(cast(size_t)cntdata); 158 data.zero(); 159 160 extern (C++) final class PointerBitmapVisitor : Visitor 161 { 162 alias visit = super.visit; 163 public: 164 extern (D) this(Array!(d_uns64)* _data, d_uns64 _sz_size_t) 165 { 166 this.data = _data; 167 this.sz_size_t = _sz_size_t; 168 } 169 170 void setpointer(d_uns64 off) 171 { 172 d_uns64 ptroff = off / sz_size_t; 173 (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t)); 174 } 175 176 override void visit(Type t) 177 { 178 Type tb = t.toBasetype(); 179 if (tb != t) 180 tb.accept(this); 181 } 182 183 override void visit(TypeError t) 184 { 185 visit(cast(Type)t); 186 } 187 188 override void visit(TypeNext t) 189 { 190 assert(0); 191 } 192 193 override void visit(TypeBasic t) 194 { 195 if (t.ty == Tvoid) 196 setpointer(offset); 197 } 198 199 override void visit(TypeVector t) 200 { 201 } 202 203 override void visit(TypeArray t) 204 { 205 assert(0); 206 } 207 208 override void visit(TypeSArray t) 209 { 210 d_uns64 arrayoff = offset; 211 d_uns64 nextsize = t.next.size(); 212 if (nextsize == SIZE_INVALID) 213 error = true; 214 d_uns64 dim = t.dim.toInteger(); 215 for (d_uns64 i = 0; i < dim; i++) 216 { 217 offset = arrayoff + i * nextsize; 218 t.next.accept(this); 219 } 220 offset = arrayoff; 221 } 222 223 override void visit(TypeDArray t) 224 { 225 setpointer(offset + sz_size_t); 226 } 227 228 // dynamic array is {length,ptr} 229 override void visit(TypeAArray t) 230 { 231 setpointer(offset); 232 } 233 234 override void visit(TypePointer t) 235 { 236 if (t.nextOf().ty != Tfunction) // don't mark function pointers 237 setpointer(offset); 238 } 239 240 override void visit(TypeReference t) 241 { 242 setpointer(offset); 243 } 244 245 override void visit(TypeClass t) 246 { 247 setpointer(offset); 248 } 249 250 override void visit(TypeFunction t) 251 { 252 } 253 254 override void visit(TypeDelegate t) 255 { 256 setpointer(offset); 257 } 258 259 // delegate is {context, function} 260 override void visit(TypeQualified t) 261 { 262 assert(0); 263 } 264 265 // assume resolved 266 override void visit(TypeIdentifier t) 267 { 268 assert(0); 269 } 270 271 override void visit(TypeInstance t) 272 { 273 assert(0); 274 } 275 276 override void visit(TypeTypeof t) 277 { 278 assert(0); 279 } 280 281 override void visit(TypeReturn t) 282 { 283 assert(0); 284 } 285 286 override void visit(TypeEnum t) 287 { 288 visit(cast(Type)t); 289 } 290 291 override void visit(TypeTuple t) 292 { 293 visit(cast(Type)t); 294 } 295 296 override void visit(TypeSlice t) 297 { 298 assert(0); 299 } 300 301 override void visit(TypeNull t) 302 { 303 // always a null pointer 304 } 305 306 override void visit(TypeStruct t) 307 { 308 d_uns64 structoff = offset; 309 foreach (v; t.sym.fields) 310 { 311 offset = structoff + v.offset; 312 if (v.type.ty == Tclass) 313 setpointer(offset); 314 else 315 v.type.accept(this); 316 } 317 offset = structoff; 318 } 319 320 // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references 321 void visitClass(TypeClass t) 322 { 323 d_uns64 classoff = offset; 324 // skip vtable-ptr and monitor 325 if (t.sym.baseClass) 326 visitClass(cast(TypeClass)t.sym.baseClass.type); 327 foreach (v; t.sym.fields) 328 { 329 offset = classoff + v.offset; 330 v.type.accept(this); 331 } 332 offset = classoff; 333 } 334 335 Array!(d_uns64)* data; 336 d_uns64 offset; 337 d_uns64 sz_size_t; 338 bool error; 339 } 340 341 scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(&data, sz_size_t); 342 if (t.ty == Tclass) 343 pbv.visitClass(cast(TypeClass)t); 344 else 345 t.accept(pbv); 346 if (pbv.error) 347 return new ErrorExp(); 348 349 auto exps = new Expressions(); 350 exps.push(new IntegerExp(e.loc, sz, Type.tsize_t)); 351 foreach (d_uns64 i; 0 .. cntdata) 352 exps.push(new IntegerExp(e.loc, data[cast(size_t)i], Type.tsize_t)); 353 354 auto ale = new ArrayLiteralExp(e.loc, exps); 355 ale.type = Type.tsize_t.sarrayOf(cntdata + 1); 356 return ale; 357 } 358 359 extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) 360 { 361 static if (LOGSEMANTIC) 362 { 363 printf("TraitsExp::semantic() %s\n", e.toChars()); 364 } 365 366 if (e.ident != Id.compiles && 367 e.ident != Id.isSame && 368 e.ident != Id.identifier && 369 e.ident != Id.getProtection) 370 { 371 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) 372 return new ErrorExp(); 373 } 374 size_t dim = e.args ? e.args.dim : 0; 375 376 Expression dimError(int expected) 377 { 378 e.error("expected %d arguments for %s but had %d", expected, e.ident.toChars(), cast(int)dim); 379 return new ErrorExp(); 380 } 381 382 Expression isX(T)(bool function(T) fp) 383 { 384 int result = 0; 385 if (!dim) 386 goto Lfalse; 387 foreach (o; *e.args) 388 { 389 static if (is(T == Type)) 390 auto y = getType(o); 391 392 static if (is(T : Dsymbol)) 393 { 394 auto s = getDsymbol(o); 395 if (!s) 396 goto Lfalse; 397 } 398 static if (is(T == Dsymbol)) 399 alias y = s; 400 static if (is(T == Declaration)) 401 auto y = s.isDeclaration(); 402 static if (is(T == FuncDeclaration)) 403 auto y = s.isFuncDeclaration(); 404 405 if (!y || !fp(y)) 406 goto Lfalse; 407 } 408 result = 1; 409 410 Lfalse: 411 return new IntegerExp(e.loc, result, Type.tbool); 412 } 413 414 alias isTypeX = isX!Type; 415 alias isDsymX = isX!Dsymbol; 416 alias isDeclX = isX!Declaration; 417 alias isFuncX = isX!FuncDeclaration; 418 419 if (e.ident == Id.isArithmetic) 420 { 421 return isTypeX(t => t.isintegral() || t.isfloating()); 422 } 423 if (e.ident == Id.isFloating) 424 { 425 return isTypeX(t => t.isfloating()); 426 } 427 if (e.ident == Id.isIntegral) 428 { 429 return isTypeX(t => t.isintegral()); 430 } 431 if (e.ident == Id.isScalar) 432 { 433 return isTypeX(t => t.isscalar()); 434 } 435 if (e.ident == Id.isUnsigned) 436 { 437 return isTypeX(t => t.isunsigned()); 438 } 439 if (e.ident == Id.isAssociativeArray) 440 { 441 return isTypeX(t => t.toBasetype().ty == Taarray); 442 } 443 if (e.ident == Id.isStaticArray) 444 { 445 return isTypeX(t => t.toBasetype().ty == Tsarray); 446 } 447 if (e.ident == Id.isAbstractClass) 448 { 449 return isTypeX(t => t.toBasetype().ty == Tclass && 450 (cast(TypeClass)t.toBasetype()).sym.isAbstract()); 451 } 452 if (e.ident == Id.isFinalClass) 453 { 454 return isTypeX(t => t.toBasetype().ty == Tclass && 455 ((cast(TypeClass)t.toBasetype()).sym.storage_class & STCfinal) != 0); 456 } 457 if (e.ident == Id.isTemplate) 458 { 459 return isDsymX((s) 460 { 461 if (!s.toAlias().isOverloadable()) 462 return false; 463 return overloadApply(s, 464 sm => sm.isTemplateDeclaration() !is null) != 0; 465 }); 466 } 467 if (e.ident == Id.isPOD) 468 { 469 if (dim != 1) 470 return dimError(1); 471 472 auto o = (*e.args)[0]; 473 auto t = isType(o); 474 if (!t) 475 { 476 e.error("type expected as second argument of __traits %s instead of %s", 477 e.ident.toChars(), o.toChars()); 478 return new ErrorExp(); 479 } 480 481 Type tb = t.baseElemOf(); 482 if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null) 483 { 484 if (sd.isPOD()) 485 goto Ltrue; 486 else 487 goto Lfalse; 488 } 489 goto Ltrue; 490 } 491 if (e.ident == Id.isNested) 492 { 493 if (dim != 1) 494 return dimError(1); 495 496 auto o = (*e.args)[0]; 497 auto s = getDsymbol(o); 498 if (!s) 499 { 500 } 501 else if (auto ad = s.isAggregateDeclaration()) 502 { 503 if (ad.isNested()) 504 goto Ltrue; 505 else 506 goto Lfalse; 507 } 508 else if (auto fd = s.isFuncDeclaration()) 509 { 510 if (fd.isNested()) 511 goto Ltrue; 512 else 513 goto Lfalse; 514 } 515 516 e.error("aggregate or function expected instead of '%s'", o.toChars()); 517 return new ErrorExp(); 518 } 519 if (e.ident == Id.isAbstractFunction) 520 { 521 return isFuncX(f => f.isAbstract()); 522 } 523 if (e.ident == Id.isVirtualFunction) 524 { 525 return isFuncX(f => f.isVirtual()); 526 } 527 if (e.ident == Id.isVirtualMethod) 528 { 529 return isFuncX(f => f.isVirtualMethod()); 530 } 531 if (e.ident == Id.isFinalFunction) 532 { 533 return isFuncX(f => f.isFinalFunc()); 534 } 535 if (e.ident == Id.isOverrideFunction) 536 { 537 return isFuncX(f => f.isOverride()); 538 } 539 if (e.ident == Id.isStaticFunction) 540 { 541 return isFuncX(f => !f.needThis() && !f.isNested()); 542 } 543 if (e.ident == Id.isRef) 544 { 545 return isDeclX(d => d.isRef()); 546 } 547 if (e.ident == Id.isOut) 548 { 549 return isDeclX(d => d.isOut()); 550 } 551 if (e.ident == Id.isLazy) 552 { 553 return isDeclX(d => (d.storage_class & STClazy) != 0); 554 } 555 if (e.ident == Id.identifier) 556 { 557 // Get identifier for symbol as a string literal 558 /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that 559 * a symbol should not be folded to a constant. 560 * Bit 1 means don't convert Parameter to Type if Parameter has an identifier 561 */ 562 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2)) 563 return new ErrorExp(); 564 if (dim != 1) 565 return dimError(1); 566 567 auto o = (*e.args)[0]; 568 Identifier id; 569 if (auto po = isParameter(o)) 570 { 571 id = po.ident; 572 assert(id); 573 } 574 else 575 { 576 Dsymbol s = getDsymbol(o); 577 if (!s || !s.ident) 578 { 579 e.error("argument %s has no identifier", o.toChars()); 580 return new ErrorExp(); 581 } 582 id = s.ident; 583 } 584 585 auto se = new StringExp(e.loc, cast(char*)id.toChars()); 586 return se.semantic(sc); 587 } 588 if (e.ident == Id.getProtection) 589 { 590 if (dim != 1) 591 return dimError(1); 592 593 Scope* sc2 = sc.push(); 594 sc2.flags = sc.flags | SCOPEnoaccesscheck; 595 bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); 596 sc2.pop(); 597 if (!ok) 598 return new ErrorExp(); 599 600 auto o = (*e.args)[0]; 601 auto s = getDsymbol(o); 602 if (!s) 603 { 604 if (!isError(o)) 605 e.error("argument %s has no protection", o.toChars()); 606 return new ErrorExp(); 607 } 608 if (s._scope) 609 s.semantic(s._scope); 610 611 auto protName = protectionToChars(s.prot().kind); // TODO: How about package(names) 612 assert(protName); 613 auto se = new StringExp(e.loc, cast(char*)protName); 614 return se.semantic(sc); 615 } 616 if (e.ident == Id.parent) 617 { 618 if (dim != 1) 619 return dimError(1); 620 621 auto o = (*e.args)[0]; 622 auto s = getDsymbol(o); 623 if (s) 624 { 625 if (auto fd = s.isFuncDeclaration()) // Bugzilla 8943 626 s = fd.toAliasFunc(); 627 if (!s.isImport()) // Bugzilla 8922 628 s = s.toParent(); 629 } 630 if (!s || s.isImport()) 631 { 632 e.error("argument %s has no parent", o.toChars()); 633 return new ErrorExp(); 634 } 635 636 if (auto f = s.isFuncDeclaration()) 637 { 638 if (auto td = getFuncTemplateDecl(f)) 639 { 640 if (td.overroot) // if not start of overloaded list of TemplateDeclaration's 641 td = td.overroot; // then get the start 642 Expression ex = new TemplateExp(e.loc, td, f); 643 ex = ex.semantic(sc); 644 return ex; 645 } 646 if (auto fld = f.isFuncLiteralDeclaration()) 647 { 648 // Directly translate to VarExp instead of FuncExp 649 Expression ex = new VarExp(e.loc, fld, true); 650 return ex.semantic(sc); 651 } 652 } 653 return DsymbolExp.resolve(e.loc, sc, s, false); 654 } 655 if (e.ident == Id.hasMember || 656 e.ident == Id.getMember || 657 e.ident == Id.getOverloads || 658 e.ident == Id.getVirtualMethods || 659 e.ident == Id.getVirtualFunctions) 660 { 661 if (dim != 2) 662 return dimError(2); 663 664 auto o = (*e.args)[0]; 665 auto ex = isExpression((*e.args)[1]); 666 if (!ex) 667 { 668 e.error("expression expected as second argument of __traits %s", e.ident.toChars()); 669 return new ErrorExp(); 670 } 671 ex = ex.ctfeInterpret(); 672 673 StringExp se = ex.toStringExp(); 674 if (!se || se.len == 0) 675 { 676 e.error("string expected as second argument of __traits %s instead of %s", e.ident.toChars(), ex.toChars()); 677 return new ErrorExp(); 678 } 679 se = se.toUTF8(sc); 680 681 if (se.sz != 1) 682 { 683 e.error("string must be chars"); 684 return new ErrorExp(); 685 } 686 auto id = Identifier.idPool(se.peekSlice()); 687 688 /* Prefer dsymbol, because it might need some runtime contexts. 689 */ 690 Dsymbol sym = getDsymbol(o); 691 if (sym) 692 { 693 ex = new DsymbolExp(e.loc, sym); 694 ex = new DotIdExp(e.loc, ex, id); 695 } 696 else if (auto t = isType(o)) 697 ex = typeDotIdExp(e.loc, t, id); 698 else if (auto ex2 = isExpression(o)) 699 ex = new DotIdExp(e.loc, ex2, id); 700 else 701 { 702 e.error("invalid first argument"); 703 return new ErrorExp(); 704 } 705 if (e.ident == Id.hasMember) 706 { 707 if (sym) 708 { 709 if (auto sm = sym.search(e.loc, id)) 710 goto Ltrue; 711 } 712 713 /* Take any errors as meaning it wasn't found 714 */ 715 Scope* sc2 = sc.push(); 716 ex = ex.trySemantic(sc2); 717 sc2.pop(); 718 if (!ex) 719 goto Lfalse; 720 else 721 goto Ltrue; 722 } 723 else if (e.ident == Id.getMember) 724 { 725 ex = ex.semantic(sc); 726 return ex; 727 } 728 else if (e.ident == Id.getVirtualFunctions || 729 e.ident == Id.getVirtualMethods || 730 e.ident == Id.getOverloads) 731 { 732 uint errors = global.errors; 733 Expression eorig = ex; 734 ex = ex.semantic(sc); 735 if (errors < global.errors) 736 e.error("%s cannot be resolved", eorig.toChars()); 737 //ex->print(); 738 739 /* Create tuple of functions of ex 740 */ 741 auto exps = new Expressions(); 742 FuncDeclaration f; 743 if (ex.op == TOKvar) 744 { 745 VarExp ve = cast(VarExp)ex; 746 f = ve.var.isFuncDeclaration(); 747 ex = null; 748 } 749 else if (ex.op == TOKdotvar) 750 { 751 DotVarExp dve = cast(DotVarExp)ex; 752 f = dve.var.isFuncDeclaration(); 753 if (dve.e1.op == TOKdottype || dve.e1.op == TOKthis) 754 ex = null; 755 else 756 ex = dve.e1; 757 } 758 759 overloadApply(f, (Dsymbol s) 760 { 761 auto fd = s.isFuncDeclaration(); 762 if (!fd) 763 return 0; 764 if (e.ident == Id.getVirtualFunctions && !fd.isVirtual()) 765 return 0; 766 if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod()) 767 return 0; 768 769 auto fa = new FuncAliasDeclaration(fd.ident, fd, false); 770 fa.protection = fd.protection; 771 772 auto e = ex ? new DotVarExp(Loc(), ex, fa, false) 773 : new DsymbolExp(Loc(), fa, false); 774 775 exps.push(e); 776 return 0; 777 }); 778 779 auto tup = new TupleExp(e.loc, exps); 780 return tup.semantic(sc); 781 } 782 else 783 assert(0); 784 } 785 if (e.ident == Id.classInstanceSize) 786 { 787 if (dim != 1) 788 return dimError(1); 789 790 auto o = (*e.args)[0]; 791 auto s = getDsymbol(o); 792 auto cd = s ? s.isClassDeclaration() : null; 793 if (!cd) 794 { 795 e.error("first argument is not a class"); 796 return new ErrorExp(); 797 } 798 if (cd.sizeok != SIZEOKdone) 799 { 800 cd.size(e.loc); 801 } 802 if (cd.sizeok != SIZEOKdone) 803 { 804 e.error("%s %s is forward referenced", cd.kind(), cd.toChars()); 805 return new ErrorExp(); 806 } 807 808 return new IntegerExp(e.loc, cd.structsize, Type.tsize_t); 809 } 810 if (e.ident == Id.getAliasThis) 811 { 812 if (dim != 1) 813 return dimError(1); 814 815 auto o = (*e.args)[0]; 816 auto s = getDsymbol(o); 817 auto ad = s ? s.isAggregateDeclaration() : null; 818 if (!ad) 819 { 820 e.error("argument is not an aggregate type"); 821 return new ErrorExp(); 822 } 823 824 auto exps = new Expressions(); 825 if (ad.aliasthis) 826 exps.push(new StringExp(e.loc, cast(char*)ad.aliasthis.ident.toChars())); 827 Expression ex = new TupleExp(e.loc, exps); 828 ex = ex.semantic(sc); 829 return ex; 830 } 831 if (e.ident == Id.getAttributes) 832 { 833 if (dim != 1) 834 return dimError(1); 835 836 auto o = (*e.args)[0]; 837 auto s = getDsymbol(o); 838 if (!s) 839 { 840 version (none) 841 { 842 Expression x = isExpression(o); 843 Type t = isType(o); 844 if (x) 845 printf("e = %s %s\n", Token.toChars(x.op), x.toChars()); 846 if (t) 847 printf("t = %d %s\n", t.ty, t.toChars()); 848 } 849 e.error("first argument is not a symbol"); 850 return new ErrorExp(); 851 } 852 if (auto imp = s.isImport()) 853 { 854 s = imp.mod; 855 } 856 857 //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->scope); 858 auto udad = s.userAttribDecl; 859 auto exps = udad ? udad.getAttributes() : new Expressions(); 860 auto tup = new TupleExp(e.loc, exps); 861 return tup.semantic(sc); 862 } 863 if (e.ident == Id.getFunctionAttributes) 864 { 865 // extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs. 866 if (dim != 1) 867 return dimError(1); 868 869 auto o = (*e.args)[0]; 870 auto s = getDsymbol(o); 871 auto t = isType(o); 872 TypeFunction tf = null; 873 if (s) 874 { 875 if (auto fd = s.isFuncDeclaration()) 876 t = fd.type; 877 else if (auto vd = s.isVarDeclaration()) 878 t = vd.type; 879 } 880 if (t) 881 { 882 if (t.ty == Tfunction) 883 tf = cast(TypeFunction)t; 884 else if (t.ty == Tdelegate) 885 tf = cast(TypeFunction)t.nextOf(); 886 else if (t.ty == Tpointer && t.nextOf().ty == Tfunction) 887 tf = cast(TypeFunction)t.nextOf(); 888 } 889 if (!tf) 890 { 891 e.error("first argument is not a function"); 892 return new ErrorExp(); 893 } 894 895 auto mods = new Expressions(); 896 PushAttributes pa; 897 pa.mods = mods; 898 tf.modifiersApply(&pa, &PushAttributes.fp); 899 tf.attributesApply(&pa, &PushAttributes.fp, TRUSTformatSystem); 900 901 auto tup = new TupleExp(e.loc, mods); 902 return tup.semantic(sc); 903 } 904 if (e.ident == Id.allMembers || 905 e.ident == Id.derivedMembers) 906 { 907 if (dim != 1) 908 return dimError(1); 909 910 auto o = (*e.args)[0]; 911 auto s = getDsymbol(o); 912 if (!s) 913 { 914 e.error("argument has no members"); 915 return new ErrorExp(); 916 } 917 if (auto imp = s.isImport()) 918 { 919 // Bugzilla 9692 920 s = imp.mod; 921 } 922 923 auto sds = s.isScopeDsymbol(); 924 if (!sds || sds.isTemplateDeclaration()) 925 { 926 e.error("%s %s has no members", s.kind(), s.toChars()); 927 return new ErrorExp(); 928 } 929 930 auto idents = new Identifiers(); 931 932 int pushIdentsDg(size_t n, Dsymbol sm) 933 { 934 if (!sm) 935 return 1; 936 //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); 937 if (sm.ident) 938 { 939 const idx = sm.ident.toChars(); 940 if (idx[0] == '_' && 941 idx[1] == '_' && 942 sm.ident != Id.ctor && 943 sm.ident != Id.dtor && 944 sm.ident != Id.__xdtor && 945 sm.ident != Id.postblit && 946 sm.ident != Id.__xpostblit) 947 { 948 return 0; 949 } 950 if (sm.ident == Id.empty) 951 { 952 return 0; 953 } 954 if (sm.isTypeInfoDeclaration()) // Bugzilla 15177 955 return 0; 956 957 //printf("\t%s\n", sm->ident->toChars()); 958 959 /* Skip if already present in idents[] 960 */ 961 foreach (id; *idents) 962 { 963 if (id == sm.ident) 964 return 0; 965 966 // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop. 967 debug assert(strcmp(id.toChars(), sm.ident.toChars()) != 0); 968 } 969 idents.push(sm.ident); 970 } 971 else if (auto ed = sm.isEnumDeclaration()) 972 { 973 ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg); 974 } 975 return 0; 976 } 977 978 ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg); 979 auto cd = sds.isClassDeclaration(); 980 if (cd && e.ident == Id.allMembers) 981 { 982 if (cd._scope) 983 cd.semantic(null); // Bugzilla 13668: Try to resolve forward reference 984 985 void pushBaseMembersDg(ClassDeclaration cd) 986 { 987 for (size_t i = 0; i < cd.baseclasses.dim; i++) 988 { 989 auto cb = (*cd.baseclasses)[i].sym; 990 assert(cb); 991 ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg); 992 if (cb.baseclasses.dim) 993 pushBaseMembersDg(cb); 994 } 995 } 996 997 pushBaseMembersDg(cd); 998 } 999 1000 // Turn Identifiers into StringExps reusing the allocated array 1001 assert(Expressions.sizeof == Identifiers.sizeof); 1002 auto exps = cast(Expressions*)idents; 1003 foreach (i, id; *idents) 1004 { 1005 auto se = new StringExp(e.loc, cast(char*)id.toChars()); 1006 (*exps)[i] = se; 1007 } 1008 1009 /* Making this a tuple is more flexible, as it can be statically unrolled. 1010 * To make an array literal, enclose __traits in [ ]: 1011 * [ __traits(allMembers, ...) ] 1012 */ 1013 Expression ex = new TupleExp(e.loc, exps); 1014 ex = ex.semantic(sc); 1015 return ex; 1016 } 1017 if (e.ident == Id.compiles) 1018 { 1019 /* Determine if all the objects - types, expressions, or symbols - 1020 * compile without error 1021 */ 1022 if (!dim) 1023 goto Lfalse; 1024 1025 foreach (o; *e.args) 1026 { 1027 uint errors = global.startGagging(); 1028 Scope* sc2 = sc.push(); 1029 sc2.tinst = null; 1030 sc2.minst = null; 1031 sc2.flags = (sc.flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile | SCOPEfullinst; 1032 1033 bool err = false; 1034 1035 auto t = isType(o); 1036 auto ex = t ? t.toExpression() : isExpression(o); 1037 if (!ex && t) 1038 { 1039 Dsymbol s; 1040 t.resolve(e.loc, sc2, &ex, &t, &s); 1041 if (t) 1042 { 1043 t.semantic(e.loc, sc2); 1044 if (t.ty == Terror) 1045 err = true; 1046 } 1047 else if (s && s.errors) 1048 err = true; 1049 } 1050 if (ex) 1051 { 1052 ex = ex.semantic(sc2); 1053 ex = resolvePropertiesOnly(sc2, ex); 1054 ex = ex.optimize(WANTvalue); 1055 if (sc2.func && sc2.func.type.ty == Tfunction) 1056 { 1057 auto tf = cast(TypeFunction)sc2.func.type; 1058 canThrow(ex, sc2.func, tf.isnothrow); 1059 } 1060 ex = checkGC(sc2, ex); 1061 if (ex.op == TOKerror) 1062 err = true; 1063 } 1064 1065 sc2.pop(); 1066 1067 if (global.endGagging(errors) || err) 1068 { 1069 goto Lfalse; 1070 } 1071 } 1072 goto Ltrue; 1073 } 1074 if (e.ident == Id.isSame) 1075 { 1076 /* Determine if two symbols are the same 1077 */ 1078 if (dim != 2) 1079 return dimError(2); 1080 1081 if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 0)) 1082 return new ErrorExp(); 1083 1084 auto o1 = (*e.args)[0]; 1085 auto o2 = (*e.args)[1]; 1086 auto s1 = getDsymbol(o1); 1087 auto s2 = getDsymbol(o2); 1088 //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars()); 1089 version (none) 1090 { 1091 printf("o1: %p\n", o1); 1092 printf("o2: %p\n", o2); 1093 if (!s1) 1094 { 1095 if (auto ea = isExpression(o1)) 1096 printf("%s\n", ea.toChars()); 1097 if (auto ta = isType(o1)) 1098 printf("%s\n", ta.toChars()); 1099 goto Lfalse; 1100 } 1101 else 1102 printf("%s %s\n", s1.kind(), s1.toChars()); 1103 } 1104 if (!s1 && !s2) 1105 { 1106 auto ea1 = isExpression(o1); 1107 auto ea2 = isExpression(o2); 1108 if (ea1 && ea2) 1109 { 1110 if (ea1.equals(ea2)) 1111 goto Ltrue; 1112 } 1113 } 1114 if (!s1 || !s2) 1115 goto Lfalse; 1116 s1 = s1.toAlias(); 1117 s2 = s2.toAlias(); 1118 1119 if (auto fa1 = s1.isFuncAliasDeclaration()) 1120 s1 = fa1.toAliasFunc(); 1121 if (auto fa2 = s2.isFuncAliasDeclaration()) 1122 s2 = fa2.toAliasFunc(); 1123 1124 if (s1 == s2) 1125 goto Ltrue; 1126 else 1127 goto Lfalse; 1128 } 1129 if (e.ident == Id.getUnitTests) 1130 { 1131 if (dim != 1) 1132 return dimError(1); 1133 1134 auto o = (*e.args)[0]; 1135 auto s = getDsymbol(o); 1136 if (!s) 1137 { 1138 e.error("argument %s to __traits(getUnitTests) must be a module or aggregate", 1139 o.toChars()); 1140 return new ErrorExp(); 1141 } 1142 if (auto imp = s.isImport()) // Bugzilla 10990 1143 s = imp.mod; 1144 1145 auto sds = s.isScopeDsymbol(); 1146 if (!sds) 1147 { 1148 e.error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", 1149 s.toChars(), s.kind()); 1150 return new ErrorExp(); 1151 } 1152 1153 auto exps = new Expressions(); 1154 if (global.params.useUnitTests) 1155 { 1156 bool[void*] uniqueUnitTests; 1157 1158 void collectUnitTests(Dsymbols* a) 1159 { 1160 if (!a) 1161 return; 1162 foreach (s; *a) 1163 { 1164 if (auto atd = s.isAttribDeclaration()) 1165 { 1166 collectUnitTests(atd.include(null, null)); 1167 continue; 1168 } 1169 if (auto ud = s.isUnitTestDeclaration()) 1170 { 1171 if (cast(void*)ud in uniqueUnitTests) 1172 continue; 1173 1174 auto ad = new FuncAliasDeclaration(ud.ident, ud, false); 1175 ad.protection = ud.protection; 1176 1177 auto e = new DsymbolExp(Loc(), ad, false); 1178 exps.push(e); 1179 1180 uniqueUnitTests[cast(void*)ud] = true; 1181 } 1182 } 1183 } 1184 1185 collectUnitTests(sds.members); 1186 } 1187 auto te = new TupleExp(e.loc, exps); 1188 return te.semantic(sc); 1189 } 1190 if (e.ident == Id.getVirtualIndex) 1191 { 1192 if (dim != 1) 1193 return dimError(1); 1194 1195 auto o = (*e.args)[0]; 1196 auto s = getDsymbol(o); 1197 1198 auto fd = s ? s.isFuncDeclaration() : null; 1199 if (!fd) 1200 { 1201 e.error("first argument to __traits(getVirtualIndex) must be a function"); 1202 return new ErrorExp(); 1203 } 1204 1205 fd = fd.toAliasFunc(); // Neccessary to support multiple overloads. 1206 return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t); 1207 } 1208 if (e.ident == Id.getPointerBitmap) 1209 { 1210 return pointerBitmap(e); 1211 } 1212 1213 extern (D) void* trait_search_fp(const(char)* seed, ref int cost) 1214 { 1215 //printf("trait_search_fp('%s')\n", seed); 1216 size_t len = strlen(seed); 1217 if (!len) 1218 return null; 1219 cost = 0; 1220 StringValue* sv = traitsStringTable.lookup(seed, len); 1221 return sv ? sv.ptrvalue : null; 1222 } 1223 1224 if (auto sub = cast(const(char)*)speller(e.ident.toChars(), &trait_search_fp, idchars)) 1225 e.error("unrecognized trait '%s', did you mean '%s'?", e.ident.toChars(), sub); 1226 else 1227 e.error("unrecognized trait '%s'", e.ident.toChars()); 1228 return new ErrorExp(); 1229 1230 Lfalse: 1231 return new IntegerExp(e.loc, 0, Type.tbool); 1232 1233 Ltrue: 1234 return new IntegerExp(e.loc, 1, Type.tbool); 1235 }