/* Compiler implementation of the D programming language * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/traits.c */ #include "root/dsystem.h" #include "root/rmem.h" #include "root/aav.h" #include "root/checkedint.h" #include "errors.h" #include "mtype.h" #include "init.h" #include "expression.h" #include "template.h" #include "utf.h" #include "enum.h" #include "scope.h" #include "hdrgen.h" #include "statement.h" #include "declaration.h" #include "aggregate.h" #include "import.h" #include "id.h" #include "dsymbol.h" #include "module.h" #include "attrib.h" #include "parse.h" #include "root/speller.h" #include "target.h" typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s); int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn = NULL); void freeFieldinit(Scope *sc); Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); Package *resolveIsPackage(Dsymbol *sym); Expression *typeToExpression(Type *t); Type *decoToType(const char *deco); bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps); /************************************************ * Delegate to be passed to overloadApply() that looks * for functions matching a trait. */ struct Ptrait { Dsymbol *sym; Expression *e1; Expressions *exps; // collected results Identifier *ident; // which trait we're looking for bool includeTemplates; AA **funcTypeHash; }; /* Compute the function signature and insert it in the * hashtable, if not present. This is needed so that * traits(getOverlods, F3, "visit") does not count `int visit(int)` * twice in the following example: * * ============================================= * interface F1 { int visit(int);} * interface F2 { int visit(int); void visit(); } * interface F3 : F2, F1 {} *============================================== */ static void insertInterfaceInheritedFunction(Ptrait *p, FuncDeclaration *fd, Expression *e) { Identifier *signature = Identifier::idPool(fd->type->toChars()); //printf("%s - %s\n", fd->toChars, signature); if (!dmd_aaGetRvalue(*p->funcTypeHash, (void *)signature)) { bool* value = (bool*) dmd_aaGet(p->funcTypeHash, (void *)signature); *value = true; p->exps->push(e); } } static int fptraits(void *param, Dsymbol *s) { Ptrait *p = (Ptrait *)param; if (p->includeTemplates) { p->exps->push(new DsymbolExp(Loc(),s, false)); return 0; } FuncDeclaration *fd = s->isFuncDeclaration(); if (!fd) return 0; if (p->ident == Id::getVirtualFunctions && !fd->isVirtual()) return 0; if (p->ident == Id::getVirtualMethods && !fd->isVirtualMethod()) return 0; Expression *e; FuncAliasDeclaration* ad = new FuncAliasDeclaration(fd->ident, fd, false); ad->protection = fd->protection; if (p->e1) e = new DotVarExp(Loc(), p->e1, ad, false); else e = new DsymbolExp(Loc(), ad, false); // if the parent is an interface declaration // we must check for functions with the same signature // in different inherited interfaces if (p->sym && p->sym->isInterfaceDeclaration()) insertInterfaceInheritedFunction(p, fd, e); else p->exps->push(e); return 0; } /** * Collects all unit test functions from the given array of symbols. * * This is a helper function used by the implementation of __traits(getUnitTests). * * Input: * symbols array of symbols to collect the functions from * uniqueUnitTests an associative array (should actually be a set) to * keep track of already collected functions. We're * using an AA here to avoid doing a linear search of unitTests * * Output: * unitTests array of DsymbolExp's of the collected unit test functions * uniqueUnitTests updated with symbols from unitTests[ ] */ static void collectUnitTests(Dsymbols *symbols, AA *uniqueUnitTests, Expressions *unitTests) { if (!symbols) return; for (size_t i = 0; i < symbols->length; i++) { Dsymbol *symbol = (*symbols)[i]; UnitTestDeclaration *unitTest = symbol->isUnitTestDeclaration(); if (unitTest) { if (!dmd_aaGetRvalue(uniqueUnitTests, (void *)unitTest)) { FuncAliasDeclaration* ad = new FuncAliasDeclaration(unitTest->ident, unitTest, false); ad->protection = unitTest->protection; Expression* e = new DsymbolExp(Loc(), ad, false); unitTests->push(e); bool* value = (bool*) dmd_aaGet(&uniqueUnitTests, (void *)unitTest); *value = true; } } else { AttribDeclaration *attrDecl = symbol->isAttribDeclaration(); if (attrDecl) { Dsymbols *decl = attrDecl->include(NULL); collectUnitTests(decl, uniqueUnitTests, unitTests); } } } } /*************************************************** * Determine if type t is copyable. * Params: * t = type to check * Returns: * true if we can copy it */ static bool isCopyable(Type *t) { //printf("isCopyable() %s\n", t->toChars()); if (TypeStruct *ts = t->isTypeStruct()) { if (ts->sym->postblit && (ts->sym->postblit->storage_class & STCdisable)) return false; } return true; } /************************ TraitsExp ************************************/ static Expression *True(TraitsExp *e) { return new IntegerExp(e->loc, true, Type::tbool); } static Expression *False(TraitsExp *e) { return new IntegerExp(e->loc, false, Type::tbool); } /************************************** * Convert `Expression` or `Type` to corresponding `Dsymbol`, * additionally strip off expression contexts. * * Some symbol related `__traits` ignore arguments expression contexts. * For example: * struct S { void f() {} } * S s; * pragma(msg, __traits(isNested, s.f)); * // s.f is DotVarExp, but __traits(isNested) needs a FuncDeclaration. * * This is used for that common `__traits` behavior. */ static Dsymbol *getDsymbolWithoutExpCtx(RootObject *oarg) { if (Expression *e = isExpression(oarg)) { if (e->op == TOKdotvar) return ((DotVarExp *)e)->var; if (e->op == TOKdottd) return ((DotTemplateExp *)e)->td; } return getDsymbol(oarg); } /** Gets the function type from a given AST node if the node is a function of some sort. Params: o = an AST node to check for a `TypeFunction` fdp = optional pointer to a function declararion, to be set if `o` is a function declarartion. Returns: a type node if `o` is a declaration of a delegate, function, function-pointer or a variable of the former. Otherwise, `null`. */ static TypeFunction *toTypeFunction(RootObject *o, FuncDeclaration **fdp = NULL) { Dsymbol *s = getDsymbolWithoutExpCtx(o); Type *t = isType(o); TypeFunction *tf = NULL; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { t = fd->type; if (fdp) *fdp = fd; } else if (VarDeclaration *vd = s->isVarDeclaration()) t = vd->type; } if (t) { if (t->ty == Tfunction) tf = (TypeFunction *)t; else if (t->ty == Tdelegate) tf = (TypeFunction *)t->nextOf(); else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction) tf = (TypeFunction *)t->nextOf(); } return tf; } static bool isTypeArithmetic(Type *t) { return t->isintegral() || t->isfloating(); } static bool isTypeFloating(Type *t) { return t->isfloating(); } static bool isTypeIntegral(Type *t) { return t->isintegral(); } static bool isTypeScalar(Type *t) { return t->isscalar(); } static bool isTypeUnsigned(Type *t) { return t->isunsigned(); } static bool isTypeAssociativeArray(Type *t) { return t->toBasetype()->ty == Taarray; } static bool isTypeStaticArray(Type *t) { return t->toBasetype()->ty == Tsarray; } static bool isTypeAbstractClass(Type *t) { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); } static bool isTypeFinalClass(Type *t) { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; } static Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t)) { if (!e->args || !e->args->length) return False(e); for (size_t i = 0; i < e->args->length; i++) { Type *t = getType((*e->args)[i]); if (!t || !fp(t)) return False(e); } return True(e); } static bool isDsymDeprecated(Dsymbol *s) { return s->isDeprecated(); } static int fpisTemplate(void *, Dsymbol *s) { if (s->isTemplateDeclaration()) return 1; return 0; } bool isTemplate(Dsymbol *s) { if (!s->toAlias()->isOverloadable()) return false; return overloadApply(s, NULL, &fpisTemplate) != 0; } static Expression *isDsymX(TraitsExp *e, bool (*fp)(Dsymbol *s)) { if (!e->args || !e->args->length) return False(e); for (size_t i = 0; i < e->args->length; i++) { Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]); if (!s || !fp(s)) return False(e); } return True(e); } static bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); } static bool isFuncVirtualFunction(FuncDeclaration *f) { return f->isVirtual(); } static bool isFuncVirtualMethod(FuncDeclaration *f) { return f->isVirtualMethod(); } static bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); } static bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); } static bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); } static Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f)) { if (!e->args || !e->args->length) return False(e); for (size_t i = 0; i < e->args->length; i++) { Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]); if (!s) return False(e); FuncDeclaration *f = s->isFuncDeclaration(); if (!f || !fp(f)) return False(e); } return True(e); } static bool isDeclDisabled(Declaration *d) { return d->isDisabled(); } static bool isDeclFuture(Declaration *d) { return d->isFuture(); } static bool isDeclRef(Declaration *d) { return d->isRef(); } static bool isDeclOut(Declaration *d) { return d->isOut(); } static bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; } static Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d)) { if (!e->args || !e->args->length) return False(e); for (size_t i = 0; i < e->args->length; i++) { Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]); if (!s) return False(e); Declaration *d = s->isDeclaration(); if (!d || !fp(d)) return False(e); } return True(e); } static bool isPkgModule(Package *p) { return p->isModule() || p->isPackageMod(); } static bool isPkgPackage(Package *p) { return p->isModule() == NULL; } static Expression *isPkgX(TraitsExp *e, bool (*fp)(Package *p)) { if (!e->args || !e->args->length) return False(e); for (size_t i = 0; i < e->args->length; i++) { Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]); if (!s) return False(e); Package *p = resolveIsPackage(s); if (!p || !fp(p)) return False(e); } return True(e); } // callback for TypeFunction::attributesApply struct PushAttributes { Expressions *mods; static int fp(void *param, const char *str) { PushAttributes *p = (PushAttributes *)param; p->mods->push(new StringExp(Loc(), const_cast(str))); return 0; } }; StringTable traitsStringTable; struct TraitsInitializer { TraitsInitializer(); }; static TraitsInitializer traitsinitializer; TraitsInitializer::TraitsInitializer() { const char* traits[] = { "isAbstractClass", "isArithmetic", "isAssociativeArray", "isDisabled", "isDeprecated", "isFuture", "isFinalClass", "isPOD", "isNested", "isFloating", "isIntegral", "isScalar", "isStaticArray", "isUnsigned", "isVirtualFunction", "isVirtualMethod", "isAbstractFunction", "isFinalFunction", "isOverrideFunction", "isStaticFunction", "isModule", "isPackage", "isRef", "isOut", "isLazy", "isReturnOnStack", "hasMember", "identifier", "getProtection", "getVisibility", "parent", "child", "getLinkage", "getMember", "getOverloads", "getVirtualFunctions", "getVirtualMethods", "classInstanceSize", "allMembers", "derivedMembers", "isSame", "compiles", "getAliasThis", "getAttributes", "getFunctionAttributes", "getFunctionVariadicStyle", "getParameterStorageClasses", "getUnitTests", "getVirtualIndex", "getPointerBitmap", "isZeroInit", "getTargetInfo", "getLocation", "hasPostblit", "isCopyable", NULL }; traitsStringTable._init(56); for (size_t idx = 0;; idx++) { const char *s = traits[idx]; if (!s) break; StringValue *sv = traitsStringTable.insert(s, strlen(s), const_cast(s)); assert(sv); } } void *trait_search_fp(void *, const char *seed, int* cost) { //printf("trait_search_fp('%s')\n", seed); size_t len = strlen(seed); if (!len) return NULL; *cost = 0; StringValue *sv = traitsStringTable.lookup(seed, len); return sv ? (void*)sv->ptrvalue : NULL; } /** * get an array of size_t values that indicate possible pointer words in memory * if interpreted as the type given as argument * the first array element is the size of the type for independent interpretation * of the array * following elements bits represent one word (4/8 bytes depending on the target * architecture). If set the corresponding memory might contain a pointer/reference. * * [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...] */ Expression *pointerBitmap(TraitsExp *e) { if (!e->args || e->args->length != 1) { error(e->loc, "a single type expected for trait pointerBitmap"); return new ErrorExp(); } Type *t = getType((*e->args)[0]); if (!t) { error(e->loc, "%s is not a type", (*e->args)[0]->toChars()); return new ErrorExp(); } d_uns64 sz; if (t->ty == Tclass && !((TypeClass*)t)->sym->isInterfaceDeclaration()) sz = ((TypeClass*)t)->sym->AggregateDeclaration::size(e->loc); else sz = t->size(e->loc); if (sz == SIZE_INVALID) return new ErrorExp(); const d_uns64 sz_size_t = Type::tsize_t->size(e->loc); if (sz > UINT64_MAX - sz_size_t) { error(e->loc, "size overflow for type %s", t->toChars()); return new ErrorExp(); } d_uns64 bitsPerWord = sz_size_t * 8; d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t; d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord; Array data; data.setDim((size_t)cntdata); data.zero(); class PointerBitmapVisitor : public Visitor { public: PointerBitmapVisitor(Array* _data, d_uns64 _sz_size_t) : data(_data), offset(0), sz_size_t(_sz_size_t), error(false) {} void setpointer(d_uns64 off) { d_uns64 ptroff = off / sz_size_t; (*data)[(size_t)(ptroff / (8 * sz_size_t))] |= 1LL << (ptroff % (8 * sz_size_t)); } virtual void visit(Type *t) { Type *tb = t->toBasetype(); if (tb != t) tb->accept(this); } virtual void visit(TypeError *t) { visit((Type *)t); } virtual void visit(TypeNext *) { assert(0); } virtual void visit(TypeBasic *t) { if (t->ty == Tvoid) setpointer(offset); } virtual void visit(TypeVector *) { } virtual void visit(TypeArray *) { assert(0); } virtual void visit(TypeSArray *t) { d_uns64 arrayoff = offset; d_uns64 nextsize = t->next->size(); if (nextsize == SIZE_INVALID) error = true; d_uns64 dim = t->dim->toInteger(); for (d_uns64 i = 0; i < dim; i++) { offset = arrayoff + i * nextsize; t->next->accept(this); } offset = arrayoff; } virtual void visit(TypeDArray *) { setpointer(offset + sz_size_t); } // dynamic array is {length,ptr} virtual void visit(TypeAArray *) { setpointer(offset); } virtual void visit(TypePointer *t) { if (t->nextOf()->ty != Tfunction) // don't mark function pointers setpointer(offset); } virtual void visit(TypeReference *) { setpointer(offset); } virtual void visit(TypeClass *) { setpointer(offset); } virtual void visit(TypeFunction *) { } virtual void visit(TypeDelegate *) { setpointer(offset); } // delegate is {context, function} virtual void visit(TypeQualified *) { assert(0); } // assume resolved virtual void visit(TypeIdentifier *) { assert(0); } virtual void visit(TypeInstance *) { assert(0); } virtual void visit(TypeTypeof *) { assert(0); } virtual void visit(TypeReturn *) { assert(0); } virtual void visit(TypeEnum *t) { visit((Type *)t); } virtual void visit(TypeTuple *t) { visit((Type *)t); } virtual void visit(TypeSlice *) { assert(0); } virtual void visit(TypeNull *) { } // always a null pointer virtual void visit(TypeStruct *t) { d_uns64 structoff = offset; for (size_t i = 0; i < t->sym->fields.length; i++) { VarDeclaration *v = t->sym->fields[i]; offset = structoff + v->offset; if (v->type->ty == Tclass) setpointer(offset); else v->type->accept(this); } offset = structoff; } // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references void visitClass(TypeClass* t) { d_uns64 classoff = offset; // skip vtable-ptr and monitor if (t->sym->baseClass) visitClass((TypeClass*)t->sym->baseClass->type); for (size_t i = 0; i < t->sym->fields.length; i++) { VarDeclaration *v = t->sym->fields[i]; offset = classoff + v->offset; v->type->accept(this); } offset = classoff; } Array* data; d_uns64 offset; d_uns64 sz_size_t; bool error; }; PointerBitmapVisitor pbv(&data, sz_size_t); if (t->ty == Tclass) pbv.visitClass((TypeClass*)t); else t->accept(&pbv); if (pbv.error) return new ErrorExp(); Expressions* exps = new Expressions; exps->push(new IntegerExp(e->loc, sz, Type::tsize_t)); for (d_uns64 i = 0; i < cntdata; i++) exps->push(new IntegerExp(e->loc, data[(size_t)i], Type::tsize_t)); ArrayLiteralExp* ale = new ArrayLiteralExp(e->loc, Type::tsize_t->sarrayOf(cntdata + 1), exps); return ale; } static Expression *dimError(TraitsExp *e, int expected, int dim) { e->error("expected %d arguments for `%s` but had %d", expected, e->ident->toChars(), dim); return new ErrorExp(); } Expression *semanticTraits(TraitsExp *e, Scope *sc) { if (e->ident != Id::compiles && e->ident != Id::isSame && e->ident != Id::identifier && e->ident != Id::getProtection && e->ident != Id::getVisibility && e->ident != Id::getAttributes) { // Pretend we're in a deprecated scope so that deprecation messages // aren't triggered when checking if a symbol is deprecated const StorageClass save = sc->stc; if (e->ident == Id::isDeprecated) sc->stc |= STCdeprecated; if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1)) { sc->stc = save; return new ErrorExp(); } sc->stc = save; } size_t dim = e->args ? e->args->length : 0; if (e->ident == Id::isArithmetic) { return isTypeX(e, &isTypeArithmetic); } else if (e->ident == Id::isFloating) { return isTypeX(e, &isTypeFloating); } else if (e->ident == Id::isIntegral) { return isTypeX(e, &isTypeIntegral); } else if (e->ident == Id::isScalar) { return isTypeX(e, &isTypeScalar); } else if (e->ident == Id::isUnsigned) { return isTypeX(e, &isTypeUnsigned); } else if (e->ident == Id::isAssociativeArray) { return isTypeX(e, &isTypeAssociativeArray); } else if (e->ident == Id::isDeprecated) { return isDsymX(e, &isDsymDeprecated); } else if (e->ident == Id::isFuture) { return isDeclX(e, &isDeclFuture); } else if (e->ident == Id::isStaticArray) { return isTypeX(e, &isTypeStaticArray); } else if (e->ident == Id::isAbstractClass) { return isTypeX(e, &isTypeAbstractClass); } else if (e->ident == Id::isFinalClass) { return isTypeX(e, &isTypeFinalClass); } else if (e->ident == Id::isTemplate) { if (dim != 1) return dimError(e, 1, dim); return isDsymX(e, &isTemplate); } else if (e->ident == Id::isPOD) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Type *t = isType(o); if (!t) { e->error("type expected as second argument of __traits %s instead of %s", e->ident->toChars(), o->toChars()); return new ErrorExp(); } Type *tb = t->baseElemOf(); if (StructDeclaration *sd = (tb->ty == Tstruct) ? ((TypeStruct *)tb)->sym : NULL) { return (sd->isPOD()) ? True(e) : False(e); } return True(e); } else if (e->ident == Id::hasPostblit) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Type *t = isType(o); if (!t) { e->error("type expected as second argument of __traits %s instead of %s", e->ident->toChars(), o->toChars()); return new ErrorExp(); } Type *tb = t->baseElemOf(); if (StructDeclaration *sd = (tb->ty == Tstruct) ? ((TypeStruct *)tb)->sym : NULL) { return sd->postblit ? True(e) : False(e); } return False(e); } else if (e->ident == Id::isCopyable) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Type *t = isType(o); if (!t) { e->error("type expected as second argument of __traits %s instead of %s", e->ident->toChars(), o->toChars()); return new ErrorExp(); } return isCopyable(t) ? True(e) : False(e); } else if (e->ident == Id::isNested) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbolWithoutExpCtx(o); if (!s) { } else if (AggregateDeclaration *a = s->isAggregateDeclaration()) { return a->isNested() ? True(e) : False(e); } else if (FuncDeclaration *f = s->isFuncDeclaration()) { return f->isNested() ? True(e) : False(e); } e->error("aggregate or function expected instead of `%s`", o->toChars()); return new ErrorExp(); } else if (e->ident == Id::isDisabled) { if (dim != 1) return dimError(e, 1, dim); return isDeclX(e, &isDeclDisabled); } else if (e->ident == Id::isAbstractFunction) { if (dim != 1) return dimError(e, 1, dim); return isFuncX(e, &isFuncAbstractFunction); } else if (e->ident == Id::isVirtualFunction) { if (dim != 1) return dimError(e, 1, dim); return isFuncX(e, &isFuncVirtualFunction); } else if (e->ident == Id::isVirtualMethod) { if (dim != 1) return dimError(e, 1, dim); return isFuncX(e, &isFuncVirtualMethod); } else if (e->ident == Id::isFinalFunction) { if (dim != 1) return dimError(e, 1, dim); return isFuncX(e, &isFuncFinalFunction); } else if (e->ident == Id::isOverrideFunction) { if (dim != 1) return dimError(e, 1, dim); return isFuncX(e, &isFuncOverrideFunction); } else if (e->ident == Id::isStaticFunction) { if (dim != 1) return dimError(e, 1, dim); return isFuncX(e, &isFuncStaticFunction); } else if (e->ident == Id::isModule) { if (dim != 1) return dimError(e, 1, dim); return isPkgX(e, &isPkgModule); } else if (e->ident == Id::isPackage) { if (dim != 1) return dimError(e, 1, dim); return isPkgX(e, &isPkgPackage); } else if (e->ident == Id::isRef) { if (dim != 1) return dimError(e, 1, dim); return isDeclX(e, &isDeclRef); } else if (e->ident == Id::isOut) { if (dim != 1) return dimError(e, 1, dim); return isDeclX(e, &isDeclOut); } else if (e->ident == Id::isLazy) { if (dim != 1) return dimError(e, 1, dim); return isDeclX(e, &isDeclLazy); } else if (e->ident == Id::identifier) { // Get identifier for symbol as a string literal /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that * a symbol should not be folded to a constant. * Bit 1 means don't convert Parameter to Type if Parameter has an identifier */ if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2)) return new ErrorExp(); if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Identifier *id = NULL; if (Parameter *po = isParameter(o)) { if (!po->ident) { e->error("argument `%s` has no identifier", po->type->toChars()); return new ErrorExp(); } id = po->ident; } else { Dsymbol *s = getDsymbolWithoutExpCtx(o); if (!s || !s->ident) { e->error("argument %s has no identifier", o->toChars()); return new ErrorExp(); } id = s->ident; } StringExp *se = new StringExp(e->loc, const_cast(id->toChars())); return expressionSemantic(se, sc); } else if (e->ident == Id::getProtection || e->ident == Id::getVisibility) { if (dim != 1) return dimError(e, 1, dim); Scope *sc2 = sc->push(); sc2->flags = sc->flags | SCOPEnoaccesscheck | SCOPEignoresymbolvisibility; bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1); sc2->pop(); if (!ok) return new ErrorExp(); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbolWithoutExpCtx(o); if (!s) { if (!isError(o)) e->error("argument %s has no protection", o->toChars()); return new ErrorExp(); } if (s->semanticRun == PASSinit) dsymbolSemantic(s, NULL); const char *protName = protectionToChars(s->prot().kind); // TODO: How about package(names) assert(protName); StringExp *se = new StringExp(e->loc, const_cast(protName)); return expressionSemantic(se, sc); } else if (e->ident == Id::parent) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbolWithoutExpCtx(o); if (s) { if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943 s = fd->toAliasFunc(); if (!s->isImport()) // Bugzilla 8922 s = s->toParent(); } if (!s || s->isImport()) { e->error("argument %s has no parent", o->toChars()); return new ErrorExp(); } if (FuncDeclaration *f = s->isFuncDeclaration()) { if (TemplateDeclaration *td = getFuncTemplateDecl(f)) { if (td->overroot) // if not start of overloaded list of TemplateDeclaration's td = td->overroot; // then get the start Expression *ex = new TemplateExp(e->loc, td, f); ex = expressionSemantic(ex, sc); return ex; } if (FuncLiteralDeclaration *fld = f->isFuncLiteralDeclaration()) { // Directly translate to VarExp instead of FuncExp Expression *ex = new VarExp(e->loc, fld, true); return expressionSemantic(ex, sc); } } return resolve(e->loc, sc, s, false); } else if (e->ident == Id::child) { if (dim != 2) return dimError(e, 2, dim); Expression *ex; RootObject *op = (*e->args)[0]; if (Dsymbol *symp = getDsymbol(op)) ex = new DsymbolExp(e->loc, symp); else if (Expression *exp = isExpression(op)) ex = exp; else { e->error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op->toChars()); return new ErrorExp(); } ex = expressionSemantic(ex, sc); RootObject *oc = (*e->args)[1]; Dsymbol *symc = getDsymbol(oc); if (!symc) { e->error("symbol expected as second argument of __traits `child` instead of `%s`", oc->toChars()); return new ErrorExp(); } if (Declaration *d = symc->isDeclaration()) ex = new DotVarExp(e->loc, ex, d); else if (TemplateDeclaration *td = symc->isTemplateDeclaration()) ex = new DotExp(e->loc, ex, new TemplateExp(e->loc, td)); else if (ScopeDsymbol *ti = symc->isScopeDsymbol()) ex = new DotExp(e->loc, ex, new ScopeExp(e->loc, ti)); else assert(0); ex = expressionSemantic(ex, sc); return ex; } else if (e->ident == Id::toType) { if (dim != 1) return dimError(e, 1, dim); Expression *ex = isExpression((*e->args)[0]); if (!ex) { e->error("expression expected as second argument of __traits `%s`", e->ident->toChars()); return new ErrorExp(); } ex = ex->ctfeInterpret(); StringExp *se = semanticString(sc, ex, "__traits(toType, string)"); if (!se) { return new ErrorExp(); } Type *t = decoToType(se->toUTF8(sc)->toPtr()); if (!t) { e->error("cannot determine `%s`", e->toChars()); return new ErrorExp(); } ex = new TypeExp(e->loc, t); ex = expressionSemantic(ex, sc); return ex; } else if (e->ident == Id::hasMember || e->ident == Id::getMember || e->ident == Id::getOverloads || e->ident == Id::getVirtualMethods || e->ident == Id::getVirtualFunctions) { if (dim != 2 && !(dim == 3 && e->ident == Id::getOverloads)) return dimError(e, 2, dim); RootObject *o = (*e->args)[0]; Expression *ex = isExpression((*e->args)[1]); if (!ex) { e->error("expression expected as second argument of __traits %s", e->ident->toChars()); return new ErrorExp(); } ex = ex->ctfeInterpret(); bool includeTemplates = false; if (dim == 3 && e->ident == Id::getOverloads) { Expression *b = isExpression((*e->args)[2]); b = b->ctfeInterpret(); if (!b->type->equals(Type::tbool)) { e->error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b->toChars(), b->type->toChars()); return new ErrorExp(); } includeTemplates = b->isBool(true); } StringExp *se = ex->toStringExp(); if (!se || se->len == 0) { e->error("string expected as second argument of __traits %s instead of %s", e->ident->toChars(), ex->toChars()); return new ErrorExp(); } se = se->toUTF8(sc); if (se->sz != 1) { e->error("string must be chars"); return new ErrorExp(); } Identifier *id = Identifier::idPool((char *)se->string, se->len); /* Prefer dsymbol, because it might need some runtime contexts. */ Dsymbol *sym = getDsymbol(o); if (sym) { if (e->ident == Id::hasMember) { if (sym->search(e->loc, id) != NULL) return True(e); } ex = new DsymbolExp(e->loc, sym); ex = new DotIdExp(e->loc, ex, id); } else if (Type *t = isType(o)) ex = typeDotIdExp(e->loc, t, id); else if (Expression *ex2 = isExpression(o)) ex = new DotIdExp(e->loc, ex2, id); else { e->error("invalid first argument"); return new ErrorExp(); } // ignore symbol visibility and disable access checks for these traits Scope *scx = sc->push(); scx->flags |= SCOPEignoresymbolvisibility | SCOPEnoaccesscheck; if (e->ident == Id::hasMember) { /* Take any errors as meaning it wasn't found */ ex = trySemantic(ex, scx); scx->pop(); return ex ? True(e) : False(e); } else if (e->ident == Id::getMember) { if (ex->op == TOKdotid) // Prevent semantic() from replacing Symbol with its initializer ((DotIdExp *)ex)->wantsym = true; ex = expressionSemantic(ex, scx); scx->pop(); return ex; } else if (e->ident == Id::getVirtualFunctions || e->ident == Id::getVirtualMethods || e->ident == Id::getOverloads) { unsigned errors = global.errors; Expression *eorig = ex; ex = expressionSemantic(ex, scx); if (errors < global.errors) e->error("%s cannot be resolved", eorig->toChars()); //ex->print(); /* Create tuple of functions of ex */ Expressions *exps = new Expressions(); Dsymbol *f; if (ex->op == TOKvar) { VarExp *ve = (VarExp *)ex; f = ve->var->isFuncDeclaration(); ex = NULL; } else if (ex->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)ex; f = dve->var->isFuncDeclaration(); if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis) ex = NULL; else ex = dve->e1; } else if (ex->op == TOKtemplate) { TemplateExp *te = (TemplateExp *)ex; TemplateDeclaration *td = te->td; f = td; if (td && td->funcroot) f = td->funcroot; ex = NULL; } else f = NULL; Ptrait p; p.sym = sym; p.exps = exps; p.e1 = ex; p.ident = e->ident; p.includeTemplates = includeTemplates; AA *funcTypeHash = NULL; p.funcTypeHash = &funcTypeHash; InterfaceDeclaration *ifd = NULL; if (sym) ifd = sym->isInterfaceDeclaration(); // If the symbol passed as a parameter is an // interface that inherits other interfaces if (ifd && ifd->interfaces.length) { // check the overloads of each inherited interface individually for (size_t i = 0; i < ifd->interfaces.length; i++) { BaseClass *bc = ifd->interfaces.ptr[i]; if (Dsymbol *fd = bc->sym->search(e->loc, f->ident)) overloadApply(fd, &p, &fptraits); } } else overloadApply(f, &p, &fptraits); ex = new TupleExp(e->loc, exps); ex = expressionSemantic(ex, scx); scx->pop(); return ex; } else assert(0); } else if (e->ident == Id::classInstanceSize) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); ClassDeclaration *cd = s ? s->isClassDeclaration() : NULL; if (!cd) { e->error("first argument is not a class"); return new ErrorExp(); } if (cd->sizeok != SIZEOKdone) { cd->size(cd->loc); } if (cd->sizeok != SIZEOKdone) { e->error("%s %s is forward referenced", cd->kind(), cd->toChars()); return new ErrorExp(); } return new IntegerExp(e->loc, cd->structsize, Type::tsize_t); } else if (e->ident == Id::getAliasThis) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL; if (!ad) { e->error("argument is not an aggregate type"); return new ErrorExp(); } Expressions *exps = new Expressions(); if (ad->aliasthis) exps->push(new StringExp(e->loc, const_cast(ad->aliasthis->ident->toChars()))); Expression *ex = new TupleExp(e->loc, exps); ex = expressionSemantic(ex, sc); return ex; } else if (e->ident == Id::getAttributes) { /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that * a symbol should not be folded to a constant. * Bit 1 means don't convert Parameter to Type if Parameter has an identifier */ if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 3)) return new ErrorExp(); if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Parameter *po = isParameter(o); Dsymbol *s = getDsymbolWithoutExpCtx(o); UserAttributeDeclaration *udad = NULL; if (po) { udad = po->userAttribDecl; } else if (s) { if (Import *imp = s->isImport()) { s = imp->mod; } //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->_scope); udad = s->userAttribDecl; } else { e->error("first argument is not a symbol"); return new ErrorExp(); } Expressions *exps = udad ? udad->getAttributes() : new Expressions(); TupleExp *tup = new TupleExp(e->loc, exps); return expressionSemantic(tup, sc); } else if (e->ident == Id::getFunctionAttributes) { /* extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs. * https://dlang.org/spec/traits.html#getFunctionAttributes */ if (dim != 1) return dimError(e, 1, dim); TypeFunction *tf = toTypeFunction((*e->args)[0]); if (!tf) { e->error("first argument is not a function"); return new ErrorExp(); } Expressions *mods = new Expressions(); PushAttributes pa; pa.mods = mods; tf->modifiersApply(&pa, &PushAttributes::fp); tf->attributesApply(&pa, &PushAttributes::fp, TRUSTformatSystem); TupleExp *tup = new TupleExp(e->loc, mods); return expressionSemantic(tup, sc); } else if (e->ident == Id::isReturnOnStack) { /* Extract as a boolean if function return value is on the stack * https://dlang.org/spec/traits.html#isReturnOnStack */ if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; FuncDeclaration *fd = NULL; TypeFunction *tf = toTypeFunction(o, &fd); if (!tf) { e->error("argument to `__traits(isReturnOnStack, %s)` is not a function", o->toChars()); return new ErrorExp(); } bool value = target.isReturnOnStack(tf, fd && fd->needThis()); return new IntegerExp(e->loc, value, Type::tbool); } else if (e->ident == Id::getFunctionVariadicStyle) { /* Accept a symbol or a type. Returns one of the following: * "none" not a variadic function * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments` * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg * "typesafe" void typesafe(T[] ...) */ // get symbol linkage as a string if (dim != 1) return dimError(e, 1, dim); LINK link; VarArg varargs; RootObject *o = (*e->args)[0]; FuncDeclaration *fd = NULL; TypeFunction *tf = toTypeFunction(o, &fd); if (tf) { link = tf->linkage; varargs = tf->parameterList.varargs; } else { if (!fd) { e->error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o->toChars()); return new ErrorExp(); } link = fd->linkage; varargs = fd->getParameterList().varargs; } const char *style; switch (varargs) { case 0: style = "none"; break; case 1: style = (link == LINKd) ? "argptr" : "stdarg"; break; case 2: style = "typesafe"; break; default: assert(0); } StringExp *se = new StringExp(e->loc, const_cast(style)); return expressionSemantic(se, sc); } else if (e->ident == Id::getParameterStorageClasses) { /* Accept a function symbol or a type, followed by a parameter index. * Returns a tuple of strings of the parameter's storage classes. */ // get symbol linkage as a string if (dim != 2) return dimError(e, 2, dim); RootObject *o = (*e->args)[0]; RootObject *o1 = (*e->args)[1]; FuncDeclaration *fd = NULL; TypeFunction *tf = toTypeFunction(o, &fd); ParameterList fparams; if (tf) { fparams = tf->parameterList; } else { if (!fd) { e->error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function", o->toChars(), o1->toChars()); return new ErrorExp(); } fparams = fd->getParameterList(); } StorageClass stc; // Set stc to storage class of the ith parameter Expression *ex = isExpression((*e->args)[1]); if (!ex) { e->error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`", o->toChars(), o1->toChars()); return new ErrorExp(); } ex = ex->ctfeInterpret(); uinteger_t ii = ex->toUInteger(); if (ii >= fparams.length()) { e->error("parameter index must be in range 0..%u not %s", (unsigned)fparams.length(), ex->toChars()); return new ErrorExp(); } unsigned n = (unsigned)ii; Parameter *p = fparams[n]; stc = p->storageClass; // This mirrors hdrgen.visit(Parameter p) if (p->type && p->type->mod & MODshared) stc &= ~STCshared; Expressions *exps = new Expressions; if (stc & STCauto) exps->push(new StringExp(e->loc, const_cast("auto"))); if (stc & STCreturn) exps->push(new StringExp(e->loc, const_cast("return"))); if (stc & STCout) exps->push(new StringExp(e->loc, const_cast("out"))); else if (stc & STCref) exps->push(new StringExp(e->loc, const_cast("ref"))); else if (stc & STCin) exps->push(new StringExp(e->loc, const_cast("in"))); else if (stc & STClazy) exps->push(new StringExp(e->loc, const_cast("lazy"))); else if (stc & STCalias) exps->push(new StringExp(e->loc, const_cast("alias"))); if (stc & STCconst) exps->push(new StringExp(e->loc, const_cast("const"))); if (stc & STCimmutable) exps->push(new StringExp(e->loc, const_cast("immutable"))); if (stc & STCwild) exps->push(new StringExp(e->loc, const_cast("inout"))); if (stc & STCshared) exps->push(new StringExp(e->loc, const_cast("shared"))); if (stc & STCscope && !(stc & STCscopeinferred)) exps->push(new StringExp(e->loc, const_cast("scope"))); TupleExp *tup = new TupleExp(e->loc, exps); return expressionSemantic(tup, sc); } else if (e->ident == Id::getLinkage) { // get symbol linkage as a string if (dim != 1) return dimError(e, 1, dim); LINK link; RootObject *o = (*e->args)[0]; TypeFunction *tf = toTypeFunction(o); if (tf) link = tf->linkage; else { Dsymbol *s = getDsymbol(o); Declaration *d = NULL; AggregateDeclaration *ad = NULL; if (!s || ((d = s->isDeclaration()) == NULL && (ad = s->isAggregateDeclaration()) == NULL)) { e->error("argument to `__traits(getLinkage, %s)` is not a declaration", o->toChars()); return new ErrorExp(); } if (d != NULL) link = d->linkage; else { switch (ad->classKind) { case ClassKind::d: link = LINKd; break; case ClassKind::cpp: link = LINKcpp; break; case ClassKind::objc: link = LINKobjc; break; default: assert(0); } } } const char *linkage = linkageToChars(link); StringExp *se = new StringExp(e->loc, const_cast(linkage)); return expressionSemantic(se, sc); } else if (e->ident == Id::allMembers || e->ident == Id::derivedMembers) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { e->error("argument has no members"); return new ErrorExp(); } if (Import *imp = s->isImport()) { // Bugzilla 9692 s = imp->mod; } // https://issues.dlang.org/show_bug.cgi?id=16044 if (Package *p = s->isPackage()) { if (Module *pm = p->isPackageMod()) s = pm; } ScopeDsymbol *sds = s->isScopeDsymbol(); if (!sds || sds->isTemplateDeclaration()) { e->error("%s %s has no members", s->kind(), s->toChars()); return new ErrorExp(); } // use a struct as local function struct PushIdentsDg { ScopeDsymbol *sds; Identifiers *idents; static int dg(void *ctx, size_t, Dsymbol *sm) { if (!sm) return 1; // skip local symbols, such as static foreach loop variables if (Declaration *decl = sm->isDeclaration()) { if (decl->storage_class & STClocal) { return 0; } } // https://issues.dlang.org/show_bug.cgi?id=20915 // skip version and debug identifiers if (sm->isVersionSymbol() || sm->isDebugSymbol()) return 0; //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); if (sm->ident) { // https://issues.dlang.org/show_bug.cgi?id=10096 // https://issues.dlang.org/show_bug.cgi?id=10100 // Skip over internal members in __traits(allMembers) if ((sm->isCtorDeclaration() && sm->ident != Id::ctor) || (sm->isDtorDeclaration() && sm->ident != Id::dtor) || (sm->isPostBlitDeclaration() && sm->ident != Id::postblit) || sm->isInvariantDeclaration() || sm->isUnitTestDeclaration()) { return 0; } if (sm->ident == Id::empty) { return 0; } if (sm->isTypeInfoDeclaration()) // Bugzilla 15177 return 0; PushIdentsDg *pid = (PushIdentsDg *)ctx; if (!pid->sds->isModule() && sm->isImport()) // Bugzilla 17057 return 0; //printf("\t%s\n", sm->ident->toChars()); Identifiers *idents = pid->idents; /* Skip if already present in idents[] */ for (size_t j = 0; j < idents->length; j++) { Identifier *id = (*idents)[j]; if (id == sm->ident) return 0; } idents->push(sm->ident); } else { EnumDeclaration *ed = sm->isEnumDeclaration(); if (ed) { ScopeDsymbol_foreach(NULL, ed->members, &PushIdentsDg::dg, ctx); } } return 0; } }; Identifiers *idents = new Identifiers; PushIdentsDg ctx; ctx.sds = sds; ctx.idents = idents; ScopeDsymbol_foreach(sc, sds->members, &PushIdentsDg::dg, &ctx); ClassDeclaration *cd = sds->isClassDeclaration(); if (cd && e->ident == Id::allMembers) { if (cd->semanticRun < PASSsemanticdone) dsymbolSemantic(cd, NULL); // Bugzilla 13668: Try to resolve forward reference struct PushBaseMembers { static void dg(ClassDeclaration *cd, PushIdentsDg *ctx) { for (size_t i = 0; i < cd->baseclasses->length; i++) { ClassDeclaration *cb = (*cd->baseclasses)[i]->sym; assert(cb); ScopeDsymbol_foreach(NULL, cb->members, &PushIdentsDg::dg, ctx); if (cb->baseclasses->length) dg(cb, ctx); } } }; PushBaseMembers::dg(cd, &ctx); } // Turn Identifiers into StringExps reusing the allocated array assert(sizeof(Expressions) == sizeof(Identifiers)); Expressions *exps = (Expressions *)idents; for (size_t i = 0; i < idents->length; i++) { Identifier *id = (*idents)[i]; StringExp *se = new StringExp(e->loc, const_cast(id->toChars())); (*exps)[i] = se; } /* Making this a tuple is more flexible, as it can be statically unrolled. * To make an array literal, enclose __traits in [ ]: * [ __traits(allMembers, ...) ] */ Expression *ex = new TupleExp(e->loc, exps); ex = expressionSemantic(ex, sc); return ex; } else if (e->ident == Id::compiles) { /* Determine if all the objects - types, expressions, or symbols - * compile without error */ if (!dim) return False(e); for (size_t i = 0; i < dim; i++) { unsigned errors = global.startGagging(); Scope *sc2 = sc->push(); sc2->tinst = NULL; sc2->minst = NULL; sc2->flags = (sc->flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile | SCOPEfullinst; bool err = false; RootObject *o = (*e->args)[i]; Type *t = isType(o); while (t) { if (TypeMixin *tm = t->isTypeMixin()) { /* The mixin string could be a type or an expression. * Have to try compiling it to see. */ OutBuffer buf; if (expressionsToString(buf, sc, tm->exps)) { err = true; break; } const size_t len = buf.length(); const char *str = buf.extractChars(); Parser p(e->loc, sc->_module, (const utf8_t *)str, len, false); p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); o = p.parseTypeOrAssignExp(TOKeof); if (p.errors || p.token.value != TOKeof) { err = true; break; } t = isType(o); } else break; } if (!err) { Expression *ex = t ? typeToExpression(t) : isExpression(o); if (!ex && t) { Dsymbol *s; t->resolve(e->loc, sc2, &ex, &t, &s); if (t) { typeSemantic(t, e->loc, sc2); if (t->ty == Terror) err = true; } else if (s && s->errors) err = true; } if (ex) { ex = expressionSemantic(ex, sc2); ex = resolvePropertiesOnly(sc2, ex); ex = ex->optimize(WANTvalue); if (sc2->func && sc2->func->type->ty == Tfunction) { TypeFunction *tf = (TypeFunction *)sc2->func->type; canThrow(ex, sc2->func, tf->isnothrow); } ex = checkGC(sc2, ex); if (ex->op == TOKerror) err = true; } } // Carefully detach the scope from the parent and throw it away as // we only need it to evaluate the expression // https://issues.dlang.org/show_bug.cgi?id=15428 freeFieldinit(sc2); sc2->enclosing = NULL; sc2->pop(); if (global.endGagging(errors) || err) { return False(e); } } return True(e); } else if (e->ident == Id::isSame) { /* Determine if two symbols are the same */ if (dim != 2) return dimError(e, 2, dim); if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0)) return new ErrorExp(); RootObject *o1 = (*e->args)[0]; RootObject *o2 = (*e->args)[1]; // issue 12001, allow isSame, , Type *t1 = isType(o1); Type *t2 = isType(o2); if (t1 && t2 && t1->equals(t2)) return True(e); Dsymbol *s1 = getDsymbol(o1); Dsymbol *s2 = getDsymbol(o2); //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars()); if (!s1 && !s2) { Expression *ea1 = isExpression(o1); Expression *ea2 = isExpression(o2); if (ea1 && ea2) { if (ea1->equals(ea2)) return True(e); } } if (!s1 || !s2) return False(e); s1 = s1->toAlias(); s2 = s2->toAlias(); if (s1->isFuncAliasDeclaration()) s1 = ((FuncAliasDeclaration *)s1)->toAliasFunc(); if (s2->isFuncAliasDeclaration()) s2 = ((FuncAliasDeclaration *)s2)->toAliasFunc(); return (s1 == s2) ? True(e) : False(e); } else if (e->ident == Id::getUnitTests) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbolWithoutExpCtx(o); if (!s) { e->error("argument %s to __traits(getUnitTests) must be a module or aggregate", o->toChars()); return new ErrorExp(); } if (Import *imp = s->isImport()) // Bugzilla 10990 s = imp->mod; ScopeDsymbol* sds = s->isScopeDsymbol(); if (!sds) { e->error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", s->toChars(), s->kind()); return new ErrorExp(); } Expressions *exps = new Expressions(); if (global.params.useUnitTests) { // Should actually be a set AA* uniqueUnitTests = NULL; collectUnitTests(sds->members, uniqueUnitTests, exps); } TupleExp *te= new TupleExp(e->loc, exps); return expressionSemantic(te, sc); } else if (e->ident == Id::getVirtualIndex) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbolWithoutExpCtx(o); FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; if (!fd) { e->error("first argument to __traits(getVirtualIndex) must be a function"); return new ErrorExp(); } fd = fd->toAliasFunc(); // Neccessary to support multiple overloads. return new IntegerExp(e->loc, fd->vtblIndex, Type::tptrdiff_t); } else if (e->ident == Id::getPointerBitmap) { return pointerBitmap(e); } else if (e->ident == Id::isZeroInit) { if (dim != 1) return dimError(e, 1, dim); RootObject *o = (*e->args)[0]; Type *t = isType(o); if (!t) { e->error("type expected as second argument of __traits `%s` instead of `%s`", e->ident->toChars(), o->toChars()); return new ErrorExp(); } Type *tb = t->baseElemOf(); return tb->isZeroInit(e->loc) ? True(e) : False(e); } else if (e->ident == Id::getTargetInfo) { if (dim != 1) return dimError(e, 1, dim); Expression *ex = isExpression((*e->args)[0]); StringExp *se = ex ? ex->ctfeInterpret()->toStringExp() : NULL; if (!ex || !se || se->len == 0) { e->error("string expected as argument of __traits `%s` instead of `%s`", e->ident->toChars(), ex->toChars()); return new ErrorExp(); } se = se->toUTF8(sc); Expression *r = target.getTargetInfo(se->toPtr(), e->loc); if (!r) { e->error("`getTargetInfo` key `\"%s\"` not supported by this implementation", se->toPtr()); return new ErrorExp(); } return expressionSemantic(r, sc); } else if (e->ident == Id::getLocation) { if (dim != 1) return dimError(e, 1, dim); RootObject *arg0 = (*e->args)[0]; Dsymbol *s = getDsymbolWithoutExpCtx(arg0); if (!s || !s->loc.filename) { e->error("can only get the location of a symbol, not `%s`", arg0->toChars()); return new ErrorExp(); } const FuncDeclaration *fd = s->isFuncDeclaration(); if (fd && fd->overnext) { e->error("cannot get location of an overload set, " "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " "to get the Nth overload", arg0->toChars(), ""); return new ErrorExp(); } Expressions *exps = new Expressions(); exps->setDim(3); (*exps)[0] = new StringExp(e->loc, const_cast(s->loc.filename), strlen(s->loc.filename)); (*exps)[1] = new IntegerExp(e->loc, s->loc.linnum, Type::tint32); (*exps)[2] = new IntegerExp(e->loc, s->loc.charnum, Type::tint32); TupleExp *tup = new TupleExp(e->loc, exps); return expressionSemantic(tup, sc); } if (const char *sub = (const char *)speller(e->ident->toChars(), &trait_search_fp, NULL, idchars)) e->error("unrecognized trait `%s`, did you mean `%s`?", e->ident->toChars(), sub); else e->error("unrecognized trait `%s`", e->ident->toChars()); return new ErrorExp(); e->error("wrong number of arguments %d", (int)dim); return new ErrorExp(); }