1
2/* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/traits.c
9 */
10
11#include "root/dsystem.h"
12#include "root/rmem.h"
13#include "root/aav.h"
14#include "root/checkedint.h"
15
16#include "errors.h"
17#include "mtype.h"
18#include "init.h"
19#include "expression.h"
20#include "template.h"
21#include "utf.h"
22#include "enum.h"
23#include "scope.h"
24#include "hdrgen.h"
25#include "statement.h"
26#include "declaration.h"
27#include "aggregate.h"
28#include "import.h"
29#include "id.h"
30#include "dsymbol.h"
31#include "module.h"
32#include "attrib.h"
33#include "parse.h"
34#include "root/speller.h"
35
36typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s);
37int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn = NULL);
38void freeFieldinit(Scope *sc);
39Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
40Expression *trySemantic(Expression *e, Scope *sc);
41Expression *semantic(Expression *e, Scope *sc);
42Expression *typeToExpression(Type *t);
43
44
45/************************************************
46 * Delegate to be passed to overloadApply() that looks
47 * for functions matching a trait.
48 */
49
50struct Ptrait
51{
52    Expression *e1;
53    Expressions *exps;          // collected results
54    Identifier *ident;          // which trait we're looking for
55};
56
57static int fptraits(void *param, Dsymbol *s)
58{
59    FuncDeclaration *f = s->isFuncDeclaration();
60    if (!f)
61        return 0;
62
63    Ptrait *p = (Ptrait *)param;
64    if (p->ident == Id::getVirtualFunctions && !f->isVirtual())
65        return 0;
66
67    if (p->ident == Id::getVirtualMethods && !f->isVirtualMethod())
68        return 0;
69
70    Expression *e;
71    FuncAliasDeclaration* ad = new FuncAliasDeclaration(f->ident, f, false);
72    ad->protection = f->protection;
73    if (p->e1)
74        e = new DotVarExp(Loc(), p->e1, ad, false);
75    else
76        e = new DsymbolExp(Loc(), ad, false);
77    p->exps->push(e);
78    return 0;
79}
80
81/**
82 * Collects all unit test functions from the given array of symbols.
83 *
84 * This is a helper function used by the implementation of __traits(getUnitTests).
85 *
86 * Input:
87 *      symbols             array of symbols to collect the functions from
88 *      uniqueUnitTests     an associative array (should actually be a set) to
89 *                          keep track of already collected functions. We're
90 *                          using an AA here to avoid doing a linear search of unitTests
91 *
92 * Output:
93 *      unitTests           array of DsymbolExp's of the collected unit test functions
94 *      uniqueUnitTests     updated with symbols from unitTests[ ]
95 */
96static void collectUnitTests(Dsymbols *symbols, AA *uniqueUnitTests, Expressions *unitTests)
97{
98    if (!symbols)
99        return;
100    for (size_t i = 0; i < symbols->dim; i++)
101    {
102        Dsymbol *symbol = (*symbols)[i];
103        UnitTestDeclaration *unitTest = symbol->isUnitTestDeclaration();
104        if (unitTest)
105        {
106            if (!dmd_aaGetRvalue(uniqueUnitTests, (void *)unitTest))
107            {
108                FuncAliasDeclaration* ad = new FuncAliasDeclaration(unitTest->ident, unitTest, false);
109                ad->protection = unitTest->protection;
110                Expression* e = new DsymbolExp(Loc(), ad, false);
111                unitTests->push(e);
112                bool* value = (bool*) dmd_aaGet(&uniqueUnitTests, (void *)unitTest);
113                *value = true;
114            }
115        }
116        else
117        {
118            AttribDeclaration *attrDecl = symbol->isAttribDeclaration();
119
120            if (attrDecl)
121            {
122                Dsymbols *decl = attrDecl->include(NULL, NULL);
123                collectUnitTests(decl, uniqueUnitTests, unitTests);
124            }
125        }
126    }
127}
128
129/************************ TraitsExp ************************************/
130
131static Expression *True(TraitsExp *e)  { return new IntegerExp(e->loc, true, Type::tbool); }
132static Expression *False(TraitsExp *e) { return new IntegerExp(e->loc, false, Type::tbool); }
133
134bool isTypeArithmetic(Type *t)       { return t->isintegral() || t->isfloating(); }
135bool isTypeFloating(Type *t)         { return t->isfloating(); }
136bool isTypeIntegral(Type *t)         { return t->isintegral(); }
137bool isTypeScalar(Type *t)           { return t->isscalar(); }
138bool isTypeUnsigned(Type *t)         { return t->isunsigned(); }
139bool isTypeAssociativeArray(Type *t) { return t->toBasetype()->ty == Taarray; }
140bool isTypeStaticArray(Type *t)      { return t->toBasetype()->ty == Tsarray; }
141bool isTypeAbstractClass(Type *t)    { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); }
142bool isTypeFinalClass(Type *t)       { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; }
143
144Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t))
145{
146    if (!e->args || !e->args->dim)
147        return False(e);
148    for (size_t i = 0; i < e->args->dim; i++)
149    {
150        Type *t = getType((*e->args)[i]);
151        if (!t || !fp(t))
152            return False(e);
153    }
154    return True(e);
155}
156
157bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); }
158bool isFuncVirtualFunction(FuncDeclaration *f) { return f->isVirtual(); }
159bool isFuncVirtualMethod(FuncDeclaration *f) { return f->isVirtualMethod(); }
160bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); }
161bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); }
162bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); }
163
164Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f))
165{
166    if (!e->args || !e->args->dim)
167        return False(e);
168    for (size_t i = 0; i < e->args->dim; i++)
169    {
170        Dsymbol *s = getDsymbol((*e->args)[i]);
171        if (!s)
172            return False(e);
173        FuncDeclaration *f = s->isFuncDeclaration();
174        if (!f || !fp(f))
175            return False(e);
176    }
177    return True(e);
178}
179
180bool isDeclRef(Declaration *d) { return d->isRef(); }
181bool isDeclOut(Declaration *d) { return d->isOut(); }
182bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; }
183
184Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d))
185{
186    if (!e->args || !e->args->dim)
187        return False(e);
188    for (size_t i = 0; i < e->args->dim; i++)
189    {
190        Dsymbol *s = getDsymbol((*e->args)[i]);
191        if (!s)
192            return False(e);
193        Declaration *d = s->isDeclaration();
194        if (!d || !fp(d))
195            return False(e);
196    }
197    return True(e);
198}
199
200// callback for TypeFunction::attributesApply
201struct PushAttributes
202{
203    Expressions *mods;
204
205    static int fp(void *param, const char *str)
206    {
207        PushAttributes *p = (PushAttributes *)param;
208        p->mods->push(new StringExp(Loc(), const_cast<char *>(str)));
209        return 0;
210    }
211};
212
213StringTable traitsStringTable;
214
215struct TraitsInitializer
216{
217    TraitsInitializer();
218};
219
220static TraitsInitializer traitsinitializer;
221
222TraitsInitializer::TraitsInitializer()
223{
224    const char* traits[] = {
225        "isAbstractClass",
226        "isArithmetic",
227        "isAssociativeArray",
228        "isFinalClass",
229        "isPOD",
230        "isNested",
231        "isFloating",
232        "isIntegral",
233        "isScalar",
234        "isStaticArray",
235        "isUnsigned",
236        "isVirtualFunction",
237        "isVirtualMethod",
238        "isAbstractFunction",
239        "isFinalFunction",
240        "isOverrideFunction",
241        "isStaticFunction",
242        "isRef",
243        "isOut",
244        "isLazy",
245        "hasMember",
246        "identifier",
247        "getProtection",
248        "parent",
249        "getLinkage",
250        "getMember",
251        "getOverloads",
252        "getVirtualFunctions",
253        "getVirtualMethods",
254        "classInstanceSize",
255        "allMembers",
256        "derivedMembers",
257        "isSame",
258        "compiles",
259        "parameters",
260        "getAliasThis",
261        "getAttributes",
262        "getFunctionAttributes",
263        "getFunctionVariadicStyle",
264        "getParameterStorageClasses",
265        "getUnitTests",
266        "getVirtualIndex",
267        "getPointerBitmap",
268        NULL
269    };
270
271    traitsStringTable._init(40);
272
273    for (size_t idx = 0;; idx++)
274    {
275        const char *s = traits[idx];
276        if (!s) break;
277        StringValue *sv = traitsStringTable.insert(s, strlen(s), const_cast<char *>(s));
278        assert(sv);
279    }
280}
281
282void *trait_search_fp(void *, const char *seed, int* cost)
283{
284    //printf("trait_search_fp('%s')\n", seed);
285    size_t len = strlen(seed);
286    if (!len)
287        return NULL;
288
289    *cost = 0;
290    StringValue *sv = traitsStringTable.lookup(seed, len);
291    return sv ? (void*)sv->ptrvalue : NULL;
292}
293
294static int fpisTemplate(void *, Dsymbol *s)
295{
296    if (s->isTemplateDeclaration())
297        return 1;
298
299    return 0;
300}
301
302bool isTemplate(Dsymbol *s)
303{
304    if (!s->toAlias()->isOverloadable())
305        return false;
306
307    return overloadApply(s, NULL, &fpisTemplate) != 0;
308}
309
310Expression *isSymbolX(TraitsExp *e, bool (*fp)(Dsymbol *s))
311{
312    if (!e->args || !e->args->dim)
313        return False(e);
314    for (size_t i = 0; i < e->args->dim; i++)
315    {
316        Dsymbol *s = getDsymbol((*e->args)[i]);
317        if (!s || !fp(s))
318            return False(e);
319    }
320    return True(e);
321}
322
323/**
324 * get an array of size_t values that indicate possible pointer words in memory
325 *  if interpreted as the type given as argument
326 * the first array element is the size of the type for independent interpretation
327 *  of the array
328 * following elements bits represent one word (4/8 bytes depending on the target
329 *  architecture). If set the corresponding memory might contain a pointer/reference.
330 *
331 *  [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
332 */
333Expression *pointerBitmap(TraitsExp *e)
334{
335    if (!e->args || e->args->dim != 1)
336    {
337        error(e->loc, "a single type expected for trait pointerBitmap");
338        return new ErrorExp();
339    }
340    Type *t = getType((*e->args)[0]);
341    if (!t)
342    {
343        error(e->loc, "%s is not a type", (*e->args)[0]->toChars());
344        return new ErrorExp();
345    }
346    d_uns64 sz;
347    if (t->ty == Tclass && !((TypeClass*)t)->sym->isInterfaceDeclaration())
348        sz = ((TypeClass*)t)->sym->AggregateDeclaration::size(e->loc);
349    else
350        sz = t->size(e->loc);
351    if (sz == SIZE_INVALID)
352        return new ErrorExp();
353
354    const d_uns64 sz_size_t = Type::tsize_t->size(e->loc);
355    if (sz > UINT64_MAX - sz_size_t)
356    {
357        error(e->loc, "size overflow for type %s", t->toChars());
358        return new ErrorExp();
359    }
360
361    d_uns64 bitsPerWord = sz_size_t * 8;
362    d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t;
363    d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
364    Array<d_uns64> data;
365    data.setDim((size_t)cntdata);
366    data.zero();
367
368    class PointerBitmapVisitor : public Visitor
369    {
370    public:
371        PointerBitmapVisitor(Array<d_uns64>* _data, d_uns64 _sz_size_t)
372            : data(_data), offset(0), sz_size_t(_sz_size_t), error(false)
373        {}
374
375        void setpointer(d_uns64 off)
376        {
377            d_uns64 ptroff = off / sz_size_t;
378            (*data)[(size_t)(ptroff / (8 * sz_size_t))] |= 1LL << (ptroff % (8 * sz_size_t));
379        }
380        virtual void visit(Type *t)
381        {
382            Type *tb = t->toBasetype();
383            if (tb != t)
384                tb->accept(this);
385        }
386        virtual void visit(TypeError *t) { visit((Type *)t); }
387        virtual void visit(TypeNext *) { assert(0); }
388        virtual void visit(TypeBasic *t)
389        {
390            if (t->ty == Tvoid)
391                setpointer(offset);
392        }
393        virtual void visit(TypeVector *) { }
394        virtual void visit(TypeArray *) { assert(0); }
395        virtual void visit(TypeSArray *t)
396        {
397            d_uns64 arrayoff = offset;
398            d_uns64 nextsize = t->next->size();
399            if (nextsize == SIZE_INVALID)
400                error = true;
401            d_uns64 dim = t->dim->toInteger();
402            for (d_uns64 i = 0; i < dim; i++)
403            {
404                offset = arrayoff + i * nextsize;
405                t->next->accept(this);
406            }
407            offset = arrayoff;
408        }
409        virtual void visit(TypeDArray *) { setpointer(offset + sz_size_t); } // dynamic array is {length,ptr}
410        virtual void visit(TypeAArray *) { setpointer(offset); }
411        virtual void visit(TypePointer *t)
412        {
413            if (t->nextOf()->ty != Tfunction) // don't mark function pointers
414                setpointer(offset);
415        }
416        virtual void visit(TypeReference *) { setpointer(offset); }
417        virtual void visit(TypeClass *) { setpointer(offset); }
418        virtual void visit(TypeFunction *) { }
419        virtual void visit(TypeDelegate *) { setpointer(offset); } // delegate is {context, function}
420        virtual void visit(TypeQualified *) { assert(0); } // assume resolved
421        virtual void visit(TypeIdentifier *) { assert(0); }
422        virtual void visit(TypeInstance *) { assert(0); }
423        virtual void visit(TypeTypeof *) { assert(0); }
424        virtual void visit(TypeReturn *) { assert(0); }
425        virtual void visit(TypeEnum *t) { visit((Type *)t); }
426        virtual void visit(TypeTuple *t) { visit((Type *)t); }
427        virtual void visit(TypeSlice *) { assert(0); }
428        virtual void visit(TypeNull *) { } // always a null pointer
429
430        virtual void visit(TypeStruct *t)
431        {
432            d_uns64 structoff = offset;
433            for (size_t i = 0; i < t->sym->fields.dim; i++)
434            {
435                VarDeclaration *v = t->sym->fields[i];
436                offset = structoff + v->offset;
437                if (v->type->ty == Tclass)
438                    setpointer(offset);
439                else
440                    v->type->accept(this);
441            }
442            offset = structoff;
443        }
444
445        // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
446        void visitClass(TypeClass* t)
447        {
448            d_uns64 classoff = offset;
449
450            // skip vtable-ptr and monitor
451            if (t->sym->baseClass)
452                visitClass((TypeClass*)t->sym->baseClass->type);
453
454            for (size_t i = 0; i < t->sym->fields.dim; i++)
455            {
456                VarDeclaration *v = t->sym->fields[i];
457                offset = classoff + v->offset;
458                v->type->accept(this);
459            }
460            offset = classoff;
461        }
462
463        Array<d_uns64>* data;
464        d_uns64 offset;
465        d_uns64 sz_size_t;
466        bool error;
467    };
468
469    PointerBitmapVisitor pbv(&data, sz_size_t);
470    if (t->ty == Tclass)
471        pbv.visitClass((TypeClass*)t);
472    else
473        t->accept(&pbv);
474    if (pbv.error)
475        return new ErrorExp();
476
477    Expressions* exps = new Expressions;
478    exps->push(new IntegerExp(e->loc, sz, Type::tsize_t));
479    for (d_uns64 i = 0; i < cntdata; i++)
480        exps->push(new IntegerExp(e->loc, data[(size_t)i], Type::tsize_t));
481
482    ArrayLiteralExp* ale = new ArrayLiteralExp(e->loc, Type::tsize_t->sarrayOf(cntdata + 1), exps);
483    return ale;
484}
485
486static Expression *dimError(TraitsExp *e, int expected, int dim)
487{
488    e->error("expected %d arguments for `%s` but had %d", expected, e->ident->toChars(), dim);
489    return new ErrorExp();
490}
491
492Expression *semanticTraits(TraitsExp *e, Scope *sc)
493{
494    if (e->ident != Id::compiles && e->ident != Id::isSame &&
495        e->ident != Id::identifier && e->ident != Id::getProtection)
496    {
497        if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1))
498            return new ErrorExp();
499    }
500    size_t dim = e->args ? e->args->dim : 0;
501
502    if (e->ident == Id::isArithmetic)
503    {
504        return isTypeX(e, &isTypeArithmetic);
505    }
506    else if (e->ident == Id::isFloating)
507    {
508        return isTypeX(e, &isTypeFloating);
509    }
510    else if (e->ident == Id::isIntegral)
511    {
512        return isTypeX(e, &isTypeIntegral);
513    }
514    else if (e->ident == Id::isScalar)
515    {
516        return isTypeX(e, &isTypeScalar);
517    }
518    else if (e->ident == Id::isUnsigned)
519    {
520        return isTypeX(e, &isTypeUnsigned);
521    }
522    else if (e->ident == Id::isAssociativeArray)
523    {
524        return isTypeX(e, &isTypeAssociativeArray);
525    }
526    else if (e->ident == Id::isStaticArray)
527    {
528        return isTypeX(e, &isTypeStaticArray);
529    }
530    else if (e->ident == Id::isAbstractClass)
531    {
532        return isTypeX(e, &isTypeAbstractClass);
533    }
534    else if (e->ident == Id::isFinalClass)
535    {
536        return isTypeX(e, &isTypeFinalClass);
537    }
538    else if (e->ident == Id::isTemplate)
539    {
540        return isSymbolX(e, &isTemplate);
541    }
542    else if (e->ident == Id::isPOD)
543    {
544        if (dim != 1)
545            return dimError(e, 1, dim);
546
547        RootObject *o = (*e->args)[0];
548        Type *t = isType(o);
549        if (!t)
550        {
551            e->error("type expected as second argument of __traits %s instead of %s",
552                e->ident->toChars(), o->toChars());
553            return new ErrorExp();
554        }
555
556        Type *tb = t->baseElemOf();
557        if (StructDeclaration *sd = (tb->ty == Tstruct) ? ((TypeStruct *)tb)->sym : NULL)
558        {
559            return (sd->isPOD()) ? True(e) : False(e);
560        }
561        return True(e);
562    }
563    else if (e->ident == Id::isNested)
564    {
565        if (dim != 1)
566            return dimError(e, 1, dim);
567
568        RootObject *o = (*e->args)[0];
569        Dsymbol *s = getDsymbol(o);
570        if (!s)
571        {
572        }
573        else if (AggregateDeclaration *a = s->isAggregateDeclaration())
574        {
575            return a->isNested() ? True(e) : False(e);
576        }
577        else if (FuncDeclaration *f = s->isFuncDeclaration())
578        {
579            return f->isNested() ? True(e) : False(e);
580        }
581
582        e->error("aggregate or function expected instead of '%s'", o->toChars());
583        return new ErrorExp();
584    }
585    else if (e->ident == Id::isAbstractFunction)
586    {
587        return isFuncX(e, &isFuncAbstractFunction);
588    }
589    else if (e->ident == Id::isVirtualFunction)
590    {
591        return isFuncX(e, &isFuncVirtualFunction);
592    }
593    else if (e->ident == Id::isVirtualMethod)
594    {
595        return isFuncX(e, &isFuncVirtualMethod);
596    }
597    else if (e->ident == Id::isFinalFunction)
598    {
599        return isFuncX(e, &isFuncFinalFunction);
600    }
601    else if (e->ident == Id::isOverrideFunction)
602    {
603        return isFuncX(e, &isFuncOverrideFunction);
604    }
605    else if (e->ident == Id::isStaticFunction)
606    {
607        return isFuncX(e, &isFuncStaticFunction);
608    }
609    else if (e->ident == Id::isRef)
610    {
611        return isDeclX(e, &isDeclRef);
612    }
613    else if (e->ident == Id::isOut)
614    {
615        return isDeclX(e, &isDeclOut);
616    }
617    else if (e->ident == Id::isLazy)
618    {
619        return isDeclX(e, &isDeclLazy);
620    }
621    else if (e->ident == Id::identifier)
622    {
623        // Get identifier for symbol as a string literal
624        /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
625         * a symbol should not be folded to a constant.
626         * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
627         */
628        if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2))
629            return new ErrorExp();
630        if (dim != 1)
631            return dimError(e, 1, dim);
632
633        RootObject *o = (*e->args)[0];
634        Identifier *id = NULL;
635        if (Parameter *po = isParameter(o))
636        {
637            id = po->ident;
638            assert(id);
639        }
640        else
641        {
642            Dsymbol *s = getDsymbol(o);
643            if (!s || !s->ident)
644            {
645                e->error("argument %s has no identifier", o->toChars());
646                return new ErrorExp();
647            }
648            id = s->ident;
649        }
650
651        StringExp *se = new StringExp(e->loc, const_cast<char *>(id->toChars()));
652        return semantic(se, sc);
653    }
654    else if (e->ident == Id::getProtection)
655    {
656        if (dim != 1)
657            return dimError(e, 1, dim);
658
659        Scope *sc2 = sc->push();
660        sc2->flags = sc->flags | SCOPEnoaccesscheck;
661        bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1);
662        sc2->pop();
663        if (!ok)
664            return new ErrorExp();
665
666        RootObject *o = (*e->args)[0];
667        Dsymbol *s = getDsymbol(o);
668        if (!s)
669        {
670            if (!isError(o))
671                e->error("argument %s has no protection", o->toChars());
672            return new ErrorExp();
673        }
674        if (s->semanticRun == PASSinit)
675            s->semantic(NULL);
676
677        const char *protName = protectionToChars(s->prot().kind);   // TODO: How about package(names)
678        assert(protName);
679        StringExp *se = new StringExp(e->loc, const_cast<char *>(protName));
680        return semantic(se, sc);
681    }
682    else if (e->ident == Id::parent)
683    {
684        if (dim != 1)
685            return dimError(e, 1, dim);
686
687        RootObject *o = (*e->args)[0];
688        Dsymbol *s = getDsymbol(o);
689        if (s)
690        {
691            if (FuncDeclaration *fd = s->isFuncDeclaration())   // Bugzilla 8943
692                s = fd->toAliasFunc();
693            if (!s->isImport())  // Bugzilla 8922
694                s = s->toParent();
695        }
696        if (!s || s->isImport())
697        {
698            e->error("argument %s has no parent", o->toChars());
699            return new ErrorExp();
700        }
701
702        if (FuncDeclaration *f = s->isFuncDeclaration())
703        {
704            if (TemplateDeclaration *td = getFuncTemplateDecl(f))
705            {
706                if (td->overroot)       // if not start of overloaded list of TemplateDeclaration's
707                    td = td->overroot;  // then get the start
708                Expression *ex = new TemplateExp(e->loc, td, f);
709                ex = semantic(ex, sc);
710                return ex;
711            }
712
713            if (FuncLiteralDeclaration *fld = f->isFuncLiteralDeclaration())
714            {
715                // Directly translate to VarExp instead of FuncExp
716                Expression *ex = new VarExp(e->loc, fld, true);
717                return semantic(ex, sc);
718            }
719        }
720
721        return resolve(e->loc, sc, s, false);
722    }
723    else if (e->ident == Id::hasMember ||
724             e->ident == Id::getMember ||
725             e->ident == Id::getOverloads ||
726             e->ident == Id::getVirtualMethods ||
727             e->ident == Id::getVirtualFunctions)
728    {
729        if (dim != 2)
730            return dimError(e, 2, dim);
731
732        RootObject *o = (*e->args)[0];
733        Expression *ex = isExpression((*e->args)[1]);
734        if (!ex)
735        {
736            e->error("expression expected as second argument of __traits %s", e->ident->toChars());
737            return new ErrorExp();
738        }
739        ex = ex->ctfeInterpret();
740
741        StringExp *se = ex->toStringExp();
742        if (!se || se->len == 0)
743        {
744            e->error("string expected as second argument of __traits %s instead of %s", e->ident->toChars(), ex->toChars());
745            return new ErrorExp();
746        }
747        se = se->toUTF8(sc);
748
749        if (se->sz != 1)
750        {
751            e->error("string must be chars");
752            return new ErrorExp();
753        }
754        Identifier *id = Identifier::idPool((char *)se->string, se->len);
755
756        /* Prefer dsymbol, because it might need some runtime contexts.
757         */
758        Dsymbol *sym = getDsymbol(o);
759        if (sym)
760        {
761            ex = new DsymbolExp(e->loc, sym);
762            ex = new DotIdExp(e->loc, ex, id);
763        }
764        else if (Type *t = isType(o))
765            ex = typeDotIdExp(e->loc, t, id);
766        else if (Expression *ex2 = isExpression(o))
767            ex = new DotIdExp(e->loc, ex2, id);
768        else
769        {
770            e->error("invalid first argument");
771            return new ErrorExp();
772        }
773
774        if (e->ident == Id::hasMember)
775        {
776            if (sym)
777            {
778                if (sym->search(e->loc, id))
779                    return True(e);
780            }
781
782            /* Take any errors as meaning it wasn't found
783             */
784            Scope *scx = sc->push();
785            scx->flags |= SCOPEignoresymbolvisibility;
786            ex = trySemantic(ex, scx);
787            scx->pop();
788            return ex ? True(e) : False(e);
789        }
790        else if (e->ident == Id::getMember)
791        {
792            if (ex->op == TOKdotid)
793                // Prevent semantic() from replacing Symbol with its initializer
794                ((DotIdExp *)ex)->wantsym = true;
795            Scope *scx = sc->push();
796            scx->flags |= SCOPEignoresymbolvisibility;
797            ex = semantic(ex, scx);
798            scx->pop();
799            return ex;
800        }
801        else if (e->ident == Id::getVirtualFunctions ||
802                 e->ident == Id::getVirtualMethods ||
803                 e->ident == Id::getOverloads)
804        {
805            unsigned errors = global.errors;
806            Expression *eorig = ex;
807            Scope *scx = sc->push();
808            scx->flags |= SCOPEignoresymbolvisibility;
809            ex = semantic(ex, scx);
810            if (errors < global.errors)
811                e->error("%s cannot be resolved", eorig->toChars());
812            //ex->print();
813
814            /* Create tuple of functions of ex
815             */
816            Expressions *exps = new Expressions();
817            FuncDeclaration *f;
818            if (ex->op == TOKvar)
819            {
820                VarExp *ve = (VarExp *)ex;
821                f = ve->var->isFuncDeclaration();
822                ex = NULL;
823            }
824            else if (ex->op == TOKdotvar)
825            {
826                DotVarExp *dve = (DotVarExp *)ex;
827                f = dve->var->isFuncDeclaration();
828                if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis)
829                    ex = NULL;
830                else
831                    ex = dve->e1;
832            }
833            else
834                f = NULL;
835            Ptrait p;
836            p.exps = exps;
837            p.e1 = ex;
838            p.ident = e->ident;
839            overloadApply(f, &p, &fptraits);
840
841            ex = new TupleExp(e->loc, exps);
842            ex = semantic(ex, scx);
843            scx->pop();
844            return ex;
845        }
846        else
847            assert(0);
848    }
849    else if (e->ident == Id::classInstanceSize)
850    {
851        if (dim != 1)
852            return dimError(e, 1, dim);
853
854        RootObject *o = (*e->args)[0];
855        Dsymbol *s = getDsymbol(o);
856        ClassDeclaration *cd = s ? s->isClassDeclaration() : NULL;
857        if (!cd)
858        {
859            e->error("first argument is not a class");
860            return new ErrorExp();
861        }
862        if (cd->sizeok != SIZEOKdone)
863        {
864            cd->size(cd->loc);
865        }
866        if (cd->sizeok != SIZEOKdone)
867        {
868            e->error("%s %s is forward referenced", cd->kind(), cd->toChars());
869            return new ErrorExp();
870        }
871
872        return new IntegerExp(e->loc, cd->structsize, Type::tsize_t);
873    }
874    else if (e->ident == Id::getAliasThis)
875    {
876        if (dim != 1)
877            return dimError(e, 1, dim);
878
879        RootObject *o = (*e->args)[0];
880        Dsymbol *s = getDsymbol(o);
881        AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL;
882        if (!ad)
883        {
884            e->error("argument is not an aggregate type");
885            return new ErrorExp();
886        }
887
888        Expressions *exps = new Expressions();
889        if (ad->aliasthis)
890            exps->push(new StringExp(e->loc, const_cast<char *>(ad->aliasthis->ident->toChars())));
891        Expression *ex = new TupleExp(e->loc, exps);
892        ex = semantic(ex, sc);
893        return ex;
894    }
895    else if (e->ident == Id::getAttributes)
896    {
897        if (dim != 1)
898            return dimError(e, 1, dim);
899
900        RootObject *o = (*e->args)[0];
901        Dsymbol *s = getDsymbol(o);
902        if (!s)
903        {
904            e->error("first argument is not a symbol");
905            return new ErrorExp();
906        }
907        if (Import *imp = s->isImport())
908        {
909            s = imp->mod;
910        }
911
912        //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->_scope);
913        UserAttributeDeclaration *udad = s->userAttribDecl;
914        Expressions *exps = udad ? udad->getAttributes() : new Expressions();
915        TupleExp *tup = new TupleExp(e->loc, exps);
916        return semantic(tup, sc);
917    }
918    else if (e->ident == Id::getFunctionAttributes)
919    {
920        /// extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
921        if (dim != 1)
922            return dimError(e, 1, dim);
923
924        RootObject *o = (*e->args)[0];
925        Dsymbol *s = getDsymbol(o);
926        Type *t = isType(o);
927        TypeFunction *tf = NULL;
928        if (s)
929        {
930            if (FuncDeclaration *f = s->isFuncDeclaration())
931                t = f->type;
932            else if (VarDeclaration *v = s->isVarDeclaration())
933                t = v->type;
934        }
935        if (t)
936        {
937            if (t->ty == Tfunction)
938                tf = (TypeFunction *)t;
939            else if (t->ty == Tdelegate)
940                tf = (TypeFunction *)t->nextOf();
941            else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
942                tf = (TypeFunction *)t->nextOf();
943        }
944        if (!tf)
945        {
946            e->error("first argument is not a function");
947            return new ErrorExp();
948        }
949
950        Expressions *mods = new Expressions();
951        PushAttributes pa;
952        pa.mods = mods;
953        tf->modifiersApply(&pa, &PushAttributes::fp);
954        tf->attributesApply(&pa, &PushAttributes::fp, TRUSTformatSystem);
955
956        TupleExp *tup = new TupleExp(e->loc, mods);
957        return semantic(tup, sc);
958    }
959    else if (e->ident == Id::getFunctionVariadicStyle)
960    {
961        /* Accept a symbol or a type. Returns one of the following:
962         *  "none"      not a variadic function
963         *  "argptr"    extern(D) void dstyle(...), use `__argptr` and `__arguments`
964         *  "stdarg"    extern(C) void cstyle(int, ...), use core.stdc.stdarg
965         *  "typesafe"  void typesafe(T[] ...)
966         */
967        // get symbol linkage as a string
968        if (dim != 1)
969            return dimError(e, 1, dim);
970
971        LINK link;
972        int varargs;
973        RootObject *o = (*e->args)[0];
974        Type *t = isType(o);
975        TypeFunction *tf = NULL;
976        if (t)
977        {
978            if (t->ty == Tfunction)
979                tf = (TypeFunction *)t;
980            else if (t->ty == Tdelegate)
981                tf = (TypeFunction *)t->nextOf();
982            else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
983                tf = (TypeFunction *)t->nextOf();
984        }
985        if (tf)
986        {
987            link = tf->linkage;
988            varargs = tf->varargs;
989        }
990        else
991        {
992            Dsymbol *s = getDsymbol(o);
993            FuncDeclaration *fd = NULL;
994            if (!s || (fd = s->isFuncDeclaration()) == NULL)
995            {
996                e->error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o->toChars());
997                return new ErrorExp();
998            }
999            link = fd->linkage;
1000            fd->getParameters(&varargs);
1001        }
1002        const char *style;
1003        switch (varargs)
1004        {
1005            case 0: style = "none";                      break;
1006            case 1: style = (link == LINKd) ? "argptr"
1007                                            : "stdarg";  break;
1008            case 2:     style = "typesafe";              break;
1009            default:
1010                assert(0);
1011        }
1012        StringExp *se = new StringExp(e->loc, const_cast<char*>(style));
1013        return semantic(se, sc);
1014    }
1015    else if (e->ident == Id::getParameterStorageClasses)
1016    {
1017        /* Accept a function symbol or a type, followed by a parameter index.
1018         * Returns a tuple of strings of the parameter's storage classes.
1019         */
1020        // get symbol linkage as a string
1021        if (dim != 2)
1022            return dimError(e, 2, dim);
1023
1024        RootObject *o1 = (*e->args)[1];
1025        RootObject *o = (*e->args)[0];
1026        Type *t = isType(o);
1027        TypeFunction *tf = NULL;
1028        if (t)
1029        {
1030            if (t->ty == Tfunction)
1031                tf = (TypeFunction *)t;
1032            else if (t->ty == Tdelegate)
1033                tf = (TypeFunction *)t->nextOf();
1034            else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
1035                tf = (TypeFunction *)t->nextOf();
1036        }
1037        Parameters* fparams;
1038        if (tf)
1039        {
1040            fparams = tf->parameters;
1041        }
1042        else
1043        {
1044            Dsymbol *s = getDsymbol(o);
1045            FuncDeclaration *fd = NULL;
1046            if (!s || (fd = s->isFuncDeclaration()) == NULL)
1047            {
1048                e->error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
1049                    o->toChars(), o1->toChars());
1050                return new ErrorExp();
1051            }
1052            fparams = fd->getParameters(NULL);
1053        }
1054
1055        StorageClass stc;
1056
1057        // Set stc to storage class of the ith parameter
1058        Expression *ex = isExpression((*e->args)[1]);
1059        if (!ex)
1060        {
1061            e->error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
1062                o->toChars(), o1->toChars());
1063            return new ErrorExp();
1064        }
1065        ex = ex->ctfeInterpret();
1066        uinteger_t ii = ex->toUInteger();
1067        if (ii >= Parameter::dim(fparams))
1068        {
1069            e->error("parameter index must be in range 0..%u not %s", (unsigned)Parameter::dim(fparams), ex->toChars());
1070            return new ErrorExp();
1071        }
1072
1073        unsigned n = (unsigned)ii;
1074        Parameter *p = Parameter::getNth(fparams, n);
1075        stc = p->storageClass;
1076
1077        // This mirrors hdrgen.visit(Parameter p)
1078        if (p->type && p->type->mod & MODshared)
1079            stc &= ~STCshared;
1080
1081        Expressions *exps = new Expressions;
1082
1083        if (stc & STCauto)
1084            exps->push(new StringExp(e->loc, const_cast<char *>("auto")));
1085        if (stc & STCreturn)
1086            exps->push(new StringExp(e->loc, const_cast<char *>("return")));
1087
1088        if (stc & STCout)
1089            exps->push(new StringExp(e->loc, const_cast<char *>("out")));
1090        else if (stc & STCref)
1091            exps->push(new StringExp(e->loc, const_cast<char *>("ref")));
1092        else if (stc & STCin)
1093            exps->push(new StringExp(e->loc, const_cast<char *>("in")));
1094        else if (stc & STClazy)
1095            exps->push(new StringExp(e->loc, const_cast<char *>("lazy")));
1096        else if (stc & STCalias)
1097            exps->push(new StringExp(e->loc, const_cast<char *>("alias")));
1098
1099        if (stc & STCconst)
1100            exps->push(new StringExp(e->loc, const_cast<char *>("const")));
1101        if (stc & STCimmutable)
1102            exps->push(new StringExp(e->loc, const_cast<char *>("immutable")));
1103        if (stc & STCwild)
1104            exps->push(new StringExp(e->loc, const_cast<char *>("inout")));
1105        if (stc & STCshared)
1106            exps->push(new StringExp(e->loc, const_cast<char *>("shared")));
1107        if (stc & STCscope && !(stc & STCscopeinferred))
1108            exps->push(new StringExp(e->loc, const_cast<char *>("scope")));
1109
1110        TupleExp *tup = new TupleExp(e->loc, exps);
1111        return semantic(tup, sc);
1112    }
1113    else if (e->ident == Id::getLinkage)
1114    {
1115        // get symbol linkage as a string
1116        if (dim != 1)
1117            return dimError(e, 1, dim);
1118
1119        LINK link;
1120        RootObject *o = (*e->args)[0];
1121        Type *t = isType(o);
1122        TypeFunction *tf = NULL;
1123        if (t)
1124        {
1125            if (t->ty == Tfunction)
1126                tf = (TypeFunction *)t;
1127            else if (t->ty == Tdelegate)
1128                tf = (TypeFunction *)t->nextOf();
1129            else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
1130                tf = (TypeFunction *)t->nextOf();
1131        }
1132        if (tf)
1133            link = tf->linkage;
1134        else
1135        {
1136            Dsymbol *s = getDsymbol(o);
1137            Declaration *d = NULL;
1138            AggregateDeclaration *ad = NULL;
1139            if (!s || ((d = s->isDeclaration()) == NULL
1140                       && (ad = s->isAggregateDeclaration()) == NULL))
1141            {
1142                e->error("argument to `__traits(getLinkage, %s)` is not a declaration", o->toChars());
1143                return new ErrorExp();
1144            }
1145            if (d != NULL)
1146                link = d->linkage;
1147            else
1148            {
1149                switch (ad->classKind)
1150                {
1151                    case ClassKind::d:
1152                        link = LINKd;
1153                        break;
1154                    case ClassKind::cpp:
1155                        link = LINKcpp;
1156                        break;
1157                    case ClassKind::objc:
1158                        link = LINKobjc;
1159                        break;
1160                    default:
1161                        assert(0);
1162                }
1163            }
1164        }
1165        const char *linkage = linkageToChars(link);
1166        StringExp *se = new StringExp(e->loc, const_cast<char *>(linkage));
1167        return semantic(se, sc);
1168    }
1169    else if (e->ident == Id::allMembers ||
1170             e->ident == Id::derivedMembers)
1171    {
1172        if (dim != 1)
1173            return dimError(e, 1, dim);
1174
1175        RootObject *o = (*e->args)[0];
1176        Dsymbol *s = getDsymbol(o);
1177        if (!s)
1178        {
1179            e->error("argument has no members");
1180            return new ErrorExp();
1181        }
1182        if (Import *imp = s->isImport())
1183        {
1184            // Bugzilla 9692
1185            s = imp->mod;
1186        }
1187
1188        ScopeDsymbol *sds = s->isScopeDsymbol();
1189        if (!sds || sds->isTemplateDeclaration())
1190        {
1191            e->error("%s %s has no members", s->kind(), s->toChars());
1192            return new ErrorExp();
1193        }
1194
1195        // use a struct as local function
1196        struct PushIdentsDg
1197        {
1198            ScopeDsymbol *sds;
1199            Identifiers *idents;
1200
1201            static int dg(void *ctx, size_t, Dsymbol *sm)
1202            {
1203                if (!sm)
1204                    return 1;
1205
1206                // skip local symbols, such as static foreach loop variables
1207                if (Declaration *decl = sm->isDeclaration())
1208                {
1209                    if (decl->storage_class & STClocal)
1210                    {
1211                        return 0;
1212                    }
1213                }
1214
1215                //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars());
1216                if (sm->ident)
1217                {
1218                    // https://issues.dlang.org/show_bug.cgi?id=10096
1219                    // https://issues.dlang.org/show_bug.cgi?id=10100
1220                    // Skip over internal members in __traits(allMembers)
1221                    if ((sm->isCtorDeclaration() && sm->ident != Id::ctor) ||
1222                        (sm->isDtorDeclaration() && sm->ident != Id::dtor) ||
1223                        (sm->isPostBlitDeclaration() && sm->ident != Id::postblit) ||
1224                        sm->isInvariantDeclaration() ||
1225                        sm->isUnitTestDeclaration())
1226                    {
1227                        return 0;
1228                    }
1229
1230                    if (sm->ident == Id::empty)
1231                    {
1232                        return 0;
1233                    }
1234                    if (sm->isTypeInfoDeclaration()) // Bugzilla 15177
1235                        return 0;
1236                    PushIdentsDg *pid = (PushIdentsDg *)ctx;
1237                    if (!pid->sds->isModule() && sm->isImport()) // Bugzilla 17057
1238                        return 0;
1239
1240                    //printf("\t%s\n", sm->ident->toChars());
1241                    Identifiers *idents = pid->idents;
1242
1243                    /* Skip if already present in idents[]
1244                     */
1245                    for (size_t j = 0; j < idents->dim; j++)
1246                    {
1247                        Identifier *id = (*idents)[j];
1248                        if (id == sm->ident)
1249                            return 0;
1250                    }
1251
1252                    idents->push(sm->ident);
1253                }
1254                else
1255                {
1256                    EnumDeclaration *ed = sm->isEnumDeclaration();
1257                    if (ed)
1258                    {
1259                        ScopeDsymbol_foreach(NULL, ed->members, &PushIdentsDg::dg, ctx);
1260                    }
1261                }
1262                return 0;
1263            }
1264        };
1265
1266        Identifiers *idents = new Identifiers;
1267        PushIdentsDg ctx;
1268        ctx.sds = sds;
1269        ctx.idents = idents;
1270        ScopeDsymbol_foreach(sc, sds->members, &PushIdentsDg::dg, &ctx);
1271        ClassDeclaration *cd = sds->isClassDeclaration();
1272        if (cd && e->ident == Id::allMembers)
1273        {
1274            if (cd->semanticRun < PASSsemanticdone)
1275                cd->semantic(NULL);    // Bugzilla 13668: Try to resolve forward reference
1276
1277            struct PushBaseMembers
1278            {
1279                static void dg(ClassDeclaration *cd, PushIdentsDg *ctx)
1280                {
1281                    for (size_t i = 0; i < cd->baseclasses->dim; i++)
1282                    {
1283                        ClassDeclaration *cb = (*cd->baseclasses)[i]->sym;
1284                        assert(cb);
1285                        ScopeDsymbol_foreach(NULL, cb->members, &PushIdentsDg::dg, ctx);
1286                        if (cb->baseclasses->dim)
1287                            dg(cb, ctx);
1288                    }
1289                }
1290            };
1291            PushBaseMembers::dg(cd, &ctx);
1292        }
1293
1294        // Turn Identifiers into StringExps reusing the allocated array
1295        assert(sizeof(Expressions) == sizeof(Identifiers));
1296        Expressions *exps = (Expressions *)idents;
1297        for (size_t i = 0; i < idents->dim; i++)
1298        {
1299            Identifier *id = (*idents)[i];
1300            StringExp *se = new StringExp(e->loc, const_cast<char *>(id->toChars()));
1301            (*exps)[i] = se;
1302        }
1303
1304        /* Making this a tuple is more flexible, as it can be statically unrolled.
1305         * To make an array literal, enclose __traits in [ ]:
1306         *   [ __traits(allMembers, ...) ]
1307         */
1308        Expression *ex = new TupleExp(e->loc, exps);
1309        ex = semantic(ex, sc);
1310        return ex;
1311    }
1312    else if (e->ident == Id::compiles)
1313    {
1314        /* Determine if all the objects - types, expressions, or symbols -
1315         * compile without error
1316         */
1317        if (!dim)
1318            return False(e);
1319
1320        for (size_t i = 0; i < dim; i++)
1321        {
1322            unsigned errors = global.startGagging();
1323            Scope *sc2 = sc->push();
1324            sc2->tinst = NULL;
1325            sc2->minst = NULL;
1326            sc2->flags = (sc->flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile | SCOPEfullinst;
1327            bool err = false;
1328
1329            RootObject *o = (*e->args)[i];
1330            Type *t = isType(o);
1331            Expression *ex = t ? typeToExpression(t) : isExpression(o);
1332            if (!ex && t)
1333            {
1334                Dsymbol *s;
1335                t->resolve(e->loc, sc2, &ex, &t, &s);
1336                if (t)
1337                {
1338                    t->semantic(e->loc, sc2);
1339                    if (t->ty == Terror)
1340                        err = true;
1341                }
1342                else if (s && s->errors)
1343                    err = true;
1344            }
1345            if (ex)
1346            {
1347                ex = semantic(ex, sc2);
1348                ex = resolvePropertiesOnly(sc2, ex);
1349                ex = ex->optimize(WANTvalue);
1350                if (sc2->func && sc2->func->type->ty == Tfunction)
1351                {
1352                    TypeFunction *tf = (TypeFunction *)sc2->func->type;
1353                    canThrow(ex, sc2->func, tf->isnothrow);
1354                }
1355                ex = checkGC(sc2, ex);
1356                if (ex->op == TOKerror)
1357                    err = true;
1358            }
1359
1360            // Carefully detach the scope from the parent and throw it away as
1361            // we only need it to evaluate the expression
1362            // https://issues.dlang.org/show_bug.cgi?id=15428
1363            freeFieldinit(sc2);
1364            sc2->enclosing = NULL;
1365            sc2->pop();
1366
1367            if (global.endGagging(errors) || err)
1368            {
1369                return False(e);
1370            }
1371        }
1372        return True(e);
1373    }
1374    else if (e->ident == Id::isSame)
1375    {
1376        /* Determine if two symbols are the same
1377         */
1378        if (dim != 2)
1379            return dimError(e, 2, dim);
1380
1381        if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0))
1382            return new ErrorExp();
1383
1384        RootObject *o1 = (*e->args)[0];
1385        RootObject *o2 = (*e->args)[1];
1386
1387        // issue 12001, allow isSame, <BasicType>, <BasicType>
1388        Type *t1 = isType(o1);
1389        Type *t2 = isType(o2);
1390        if (t1 && t2 && t1->equals(t2))
1391            return True(e);
1392
1393        Dsymbol *s1 = getDsymbol(o1);
1394        Dsymbol *s2 = getDsymbol(o2);
1395        //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars());
1396        if (!s1 && !s2)
1397        {
1398            Expression *ea1 = isExpression(o1);
1399            Expression *ea2 = isExpression(o2);
1400            if (ea1 && ea2)
1401            {
1402                if (ea1->equals(ea2))
1403                    return True(e);
1404            }
1405        }
1406        if (!s1 || !s2)
1407            return False(e);
1408        s1 = s1->toAlias();
1409        s2 = s2->toAlias();
1410
1411        if (s1->isFuncAliasDeclaration())
1412            s1 = ((FuncAliasDeclaration *)s1)->toAliasFunc();
1413        if (s2->isFuncAliasDeclaration())
1414            s2 = ((FuncAliasDeclaration *)s2)->toAliasFunc();
1415
1416        return (s1 == s2) ? True(e) : False(e);
1417    }
1418    else if (e->ident == Id::getUnitTests)
1419    {
1420        if (dim != 1)
1421            return dimError(e, 1, dim);
1422
1423        RootObject *o = (*e->args)[0];
1424        Dsymbol *s = getDsymbol(o);
1425        if (!s)
1426        {
1427            e->error("argument %s to __traits(getUnitTests) must be a module or aggregate",
1428                o->toChars());
1429            return new ErrorExp();
1430        }
1431        if (Import *imp = s->isImport())  // Bugzilla 10990
1432            s = imp->mod;
1433
1434        ScopeDsymbol* sds = s->isScopeDsymbol();
1435        if (!sds)
1436        {
1437            e->error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s",
1438                s->toChars(), s->kind());
1439            return new ErrorExp();
1440        }
1441
1442        Expressions *exps = new Expressions();
1443        if (global.params.useUnitTests)
1444        {
1445            // Should actually be a set
1446            AA* uniqueUnitTests = NULL;
1447            collectUnitTests(sds->members, uniqueUnitTests, exps);
1448        }
1449        TupleExp *te= new TupleExp(e->loc, exps);
1450        return semantic(te, sc);
1451    }
1452    else if (e->ident == Id::getVirtualIndex)
1453    {
1454        if (dim != 1)
1455            return dimError(e, 1, dim);
1456
1457        RootObject *o = (*e->args)[0];
1458        Dsymbol *s = getDsymbol(o);
1459
1460        FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
1461        if (!fd)
1462        {
1463            e->error("first argument to __traits(getVirtualIndex) must be a function");
1464            return new ErrorExp();
1465        }
1466
1467        fd = fd->toAliasFunc(); // Neccessary to support multiple overloads.
1468        return new IntegerExp(e->loc, fd->vtblIndex, Type::tptrdiff_t);
1469    }
1470    else if (e->ident == Id::getPointerBitmap)
1471    {
1472        return pointerBitmap(e);
1473    }
1474
1475    if (const char *sub = (const char *)speller(e->ident->toChars(), &trait_search_fp, NULL, idchars))
1476        e->error("unrecognized trait '%s', did you mean '%s'?", e->ident->toChars(), sub);
1477    else
1478        e->error("unrecognized trait '%s'", e->ident->toChars());
1479    return new ErrorExp();
1480
1481    e->error("wrong number of arguments %d", (int)dim);
1482    return new ErrorExp();
1483}
1484