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