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