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