| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 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" |
| |
| 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); |
| Expression *trySemantic(Expression *e, Scope *sc); |
| Expression *semantic(Expression *e, Scope *sc); |
| Expression *typeToExpression(Type *t); |
| |
| |
| /************************************************ |
| * Delegate to be passed to overloadApply() that looks |
| * for functions matching a trait. |
| */ |
| |
| struct Ptrait |
| { |
| Expression *e1; |
| Expressions *exps; // collected results |
| Identifier *ident; // which trait we're looking for |
| }; |
| |
| static int fptraits(void *param, Dsymbol *s) |
| { |
| FuncDeclaration *f = s->isFuncDeclaration(); |
| if (!f) |
| return 0; |
| |
| Ptrait *p = (Ptrait *)param; |
| if (p->ident == Id::getVirtualFunctions && !f->isVirtual()) |
| return 0; |
| |
| if (p->ident == Id::getVirtualMethods && !f->isVirtualMethod()) |
| return 0; |
| |
| Expression *e; |
| FuncAliasDeclaration* ad = new FuncAliasDeclaration(f->ident, f, false); |
| ad->protection = f->protection; |
| if (p->e1) |
| e = new DotVarExp(Loc(), p->e1, ad, false); |
| else |
| e = new DsymbolExp(Loc(), ad, false); |
| 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->dim; 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, NULL); |
| collectUnitTests(decl, uniqueUnitTests, unitTests); |
| } |
| } |
| } |
| } |
| |
| /************************ 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); } |
| |
| bool isTypeArithmetic(Type *t) { return t->isintegral() || t->isfloating(); } |
| bool isTypeFloating(Type *t) { return t->isfloating(); } |
| bool isTypeIntegral(Type *t) { return t->isintegral(); } |
| bool isTypeScalar(Type *t) { return t->isscalar(); } |
| bool isTypeUnsigned(Type *t) { return t->isunsigned(); } |
| bool isTypeAssociativeArray(Type *t) { return t->toBasetype()->ty == Taarray; } |
| bool isTypeStaticArray(Type *t) { return t->toBasetype()->ty == Tsarray; } |
| bool isTypeAbstractClass(Type *t) { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); } |
| bool isTypeFinalClass(Type *t) { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; } |
| |
| Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t)) |
| { |
| if (!e->args || !e->args->dim) |
| return False(e); |
| for (size_t i = 0; i < e->args->dim; i++) |
| { |
| Type *t = getType((*e->args)[i]); |
| if (!t || !fp(t)) |
| return False(e); |
| } |
| return True(e); |
| } |
| |
| bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); } |
| bool isFuncVirtualFunction(FuncDeclaration *f) { return f->isVirtual(); } |
| bool isFuncVirtualMethod(FuncDeclaration *f) { return f->isVirtualMethod(); } |
| bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); } |
| bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); } |
| bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); } |
| |
| Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f)) |
| { |
| if (!e->args || !e->args->dim) |
| return False(e); |
| for (size_t i = 0; i < e->args->dim; i++) |
| { |
| Dsymbol *s = getDsymbol((*e->args)[i]); |
| if (!s) |
| return False(e); |
| FuncDeclaration *f = s->isFuncDeclaration(); |
| if (!f || !fp(f)) |
| return False(e); |
| } |
| return True(e); |
| } |
| |
| bool isDeclRef(Declaration *d) { return d->isRef(); } |
| bool isDeclOut(Declaration *d) { return d->isOut(); } |
| bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; } |
| |
| Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d)) |
| { |
| if (!e->args || !e->args->dim) |
| return False(e); |
| for (size_t i = 0; i < e->args->dim; i++) |
| { |
| Dsymbol *s = getDsymbol((*e->args)[i]); |
| if (!s) |
| return False(e); |
| Declaration *d = s->isDeclaration(); |
| if (!d || !fp(d)) |
| 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<char *>(str))); |
| return 0; |
| } |
| }; |
| |
| StringTable traitsStringTable; |
| |
| struct TraitsInitializer |
| { |
| TraitsInitializer(); |
| }; |
| |
| static TraitsInitializer traitsinitializer; |
| |
| TraitsInitializer::TraitsInitializer() |
| { |
| const char* traits[] = { |
| "isAbstractClass", |
| "isArithmetic", |
| "isAssociativeArray", |
| "isFinalClass", |
| "isPOD", |
| "isNested", |
| "isFloating", |
| "isIntegral", |
| "isScalar", |
| "isStaticArray", |
| "isUnsigned", |
| "isVirtualFunction", |
| "isVirtualMethod", |
| "isAbstractFunction", |
| "isFinalFunction", |
| "isOverrideFunction", |
| "isStaticFunction", |
| "isRef", |
| "isOut", |
| "isLazy", |
| "hasMember", |
| "identifier", |
| "getProtection", |
| "parent", |
| "getLinkage", |
| "getMember", |
| "getOverloads", |
| "getVirtualFunctions", |
| "getVirtualMethods", |
| "classInstanceSize", |
| "allMembers", |
| "derivedMembers", |
| "isSame", |
| "compiles", |
| "parameters", |
| "getAliasThis", |
| "getAttributes", |
| "getFunctionAttributes", |
| "getFunctionVariadicStyle", |
| "getParameterStorageClasses", |
| "getUnitTests", |
| "getVirtualIndex", |
| "getPointerBitmap", |
| NULL |
| }; |
| |
| traitsStringTable._init(40); |
| |
| for (size_t idx = 0;; idx++) |
| { |
| const char *s = traits[idx]; |
| if (!s) break; |
| StringValue *sv = traitsStringTable.insert(s, strlen(s), const_cast<char *>(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; |
| } |
| |
| 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; |
| } |
| |
| Expression *isSymbolX(TraitsExp *e, bool (*fp)(Dsymbol *s)) |
| { |
| if (!e->args || !e->args->dim) |
| return False(e); |
| for (size_t i = 0; i < e->args->dim; i++) |
| { |
| Dsymbol *s = getDsymbol((*e->args)[i]); |
| if (!s || !fp(s)) |
| return False(e); |
| } |
| return True(e); |
| } |
| |
| /** |
| * 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->dim != 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<d_uns64> data; |
| data.setDim((size_t)cntdata); |
| data.zero(); |
| |
| class PointerBitmapVisitor : public Visitor |
| { |
| public: |
| PointerBitmapVisitor(Array<d_uns64>* _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.dim; 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.dim; i++) |
| { |
| VarDeclaration *v = t->sym->fields[i]; |
| offset = classoff + v->offset; |
| v->type->accept(this); |
| } |
| offset = classoff; |
| } |
| |
| Array<d_uns64>* 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) |
| { |
| if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1)) |
| return new ErrorExp(); |
| } |
| size_t dim = e->args ? e->args->dim : 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::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) |
| { |
| return isSymbolX(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::isNested) |
| { |
| if (dim != 1) |
| return dimError(e, 1, dim); |
| |
| RootObject *o = (*e->args)[0]; |
| Dsymbol *s = getDsymbol(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::isAbstractFunction) |
| { |
| return isFuncX(e, &isFuncAbstractFunction); |
| } |
| else if (e->ident == Id::isVirtualFunction) |
| { |
| return isFuncX(e, &isFuncVirtualFunction); |
| } |
| else if (e->ident == Id::isVirtualMethod) |
| { |
| return isFuncX(e, &isFuncVirtualMethod); |
| } |
| else if (e->ident == Id::isFinalFunction) |
| { |
| return isFuncX(e, &isFuncFinalFunction); |
| } |
| else if (e->ident == Id::isOverrideFunction) |
| { |
| return isFuncX(e, &isFuncOverrideFunction); |
| } |
| else if (e->ident == Id::isStaticFunction) |
| { |
| return isFuncX(e, &isFuncStaticFunction); |
| } |
| else if (e->ident == Id::isRef) |
| { |
| return isDeclX(e, &isDeclRef); |
| } |
| else if (e->ident == Id::isOut) |
| { |
| return isDeclX(e, &isDeclOut); |
| } |
| else if (e->ident == Id::isLazy) |
| { |
| 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)) |
| { |
| id = po->ident; |
| assert(id); |
| } |
| else |
| { |
| Dsymbol *s = getDsymbol(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<char *>(id->toChars())); |
| return semantic(se, sc); |
| } |
| else if (e->ident == Id::getProtection) |
| { |
| if (dim != 1) |
| return dimError(e, 1, dim); |
| |
| Scope *sc2 = sc->push(); |
| sc2->flags = sc->flags | SCOPEnoaccesscheck; |
| bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1); |
| sc2->pop(); |
| if (!ok) |
| return new ErrorExp(); |
| |
| RootObject *o = (*e->args)[0]; |
| Dsymbol *s = getDsymbol(o); |
| if (!s) |
| { |
| if (!isError(o)) |
| e->error("argument %s has no protection", o->toChars()); |
| return new ErrorExp(); |
| } |
| if (s->_scope) |
| s->semantic(s->_scope); |
| |
| const char *protName = protectionToChars(s->prot().kind); // TODO: How about package(names) |
| assert(protName); |
| StringExp *se = new StringExp(e->loc, const_cast<char *>(protName)); |
| return semantic(se, sc); |
| } |
| else if (e->ident == Id::parent) |
| { |
| if (dim != 1) |
| return dimError(e, 1, dim); |
| |
| RootObject *o = (*e->args)[0]; |
| Dsymbol *s = getDsymbol(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 = semantic(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 semantic(ex, sc); |
| } |
| } |
| |
| return resolve(e->loc, sc, s, false); |
| } |
| 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) |
| 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(); |
| |
| 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) |
| { |
| 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(); |
| } |
| |
| if (e->ident == Id::hasMember) |
| { |
| if (sym) |
| { |
| if (sym->search(e->loc, id)) |
| return True(e); |
| } |
| |
| /* Take any errors as meaning it wasn't found |
| */ |
| Scope *scx = sc->push(); |
| scx->flags |= SCOPEignoresymbolvisibility; |
| 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; |
| Scope *scx = sc->push(); |
| scx->flags |= SCOPEignoresymbolvisibility; |
| ex = semantic(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; |
| Scope *scx = sc->push(); |
| scx->flags |= SCOPEignoresymbolvisibility; |
| ex = semantic(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(); |
| FuncDeclaration *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 |
| f = NULL; |
| Ptrait p; |
| p.exps = exps; |
| p.e1 = ex; |
| p.ident = e->ident; |
| overloadApply(f, &p, &fptraits); |
| |
| ex = new TupleExp(e->loc, exps); |
| ex = semantic(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<char *>(ad->aliasthis->ident->toChars()))); |
| Expression *ex = new TupleExp(e->loc, exps); |
| ex = semantic(ex, sc); |
| return ex; |
| } |
| else if (e->ident == Id::getAttributes) |
| { |
| if (dim != 1) |
| return dimError(e, 1, dim); |
| |
| RootObject *o = (*e->args)[0]; |
| Dsymbol *s = getDsymbol(o); |
| if (!s) |
| { |
| e->error("first argument is not a symbol"); |
| return new ErrorExp(); |
| } |
| if (Import *imp = s->isImport()) |
| { |
| s = imp->mod; |
| } |
| |
| //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->_scope); |
| UserAttributeDeclaration *udad = s->userAttribDecl; |
| Expressions *exps = udad ? udad->getAttributes() : new Expressions(); |
| TupleExp *tup = new TupleExp(e->loc, exps); |
| return semantic(tup, sc); |
| } |
| else if (e->ident == Id::getFunctionAttributes) |
| { |
| /// extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs. |
| if (dim != 1) |
| return dimError(e, 1, dim); |
| |
| RootObject *o = (*e->args)[0]; |
| Dsymbol *s = getDsymbol(o); |
| Type *t = isType(o); |
| TypeFunction *tf = NULL; |
| if (s) |
| { |
| if (FuncDeclaration *f = s->isFuncDeclaration()) |
| t = f->type; |
| else if (VarDeclaration *v = s->isVarDeclaration()) |
| t = v->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(); |
| } |
| 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 semantic(tup, sc); |
| } |
| 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; |
| int varargs; |
| RootObject *o = (*e->args)[0]; |
| Type *t = isType(o); |
| TypeFunction *tf = NULL; |
| 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(); |
| } |
| if (tf) |
| { |
| link = tf->linkage; |
| varargs = tf->varargs; |
| } |
| else |
| { |
| Dsymbol *s = getDsymbol(o); |
| FuncDeclaration *fd = NULL; |
| if (!s || (fd = s->isFuncDeclaration()) == NULL) |
| { |
| e->error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o->toChars()); |
| return new ErrorExp(); |
| } |
| link = fd->linkage; |
| fd->getParameters(&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<char*>(style)); |
| return semantic(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 *o1 = (*e->args)[1]; |
| RootObject *o = (*e->args)[0]; |
| Type *t = isType(o); |
| TypeFunction *tf = NULL; |
| 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(); |
| } |
| Parameters* fparams; |
| if (tf) |
| { |
| fparams = tf->parameters; |
| } |
| else |
| { |
| Dsymbol *s = getDsymbol(o); |
| FuncDeclaration *fd = NULL; |
| if (!s || (fd = s->isFuncDeclaration()) == NULL) |
| { |
| e->error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function", |
| o->toChars(), o1->toChars()); |
| return new ErrorExp(); |
| } |
| fparams = fd->getParameters(NULL); |
| } |
| |
| 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 >= Parameter::dim(fparams)) |
| { |
| e->error("parameter index must be in range 0..%u not %s", (unsigned)Parameter::dim(fparams), ex->toChars()); |
| return new ErrorExp(); |
| } |
| |
| unsigned n = (unsigned)ii; |
| Parameter *p = Parameter::getNth(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<char *>("auto"))); |
| if (stc & STCreturn) |
| exps->push(new StringExp(e->loc, const_cast<char *>("return"))); |
| |
| if (stc & STCout) |
| exps->push(new StringExp(e->loc, const_cast<char *>("out"))); |
| else if (stc & STCref) |
| exps->push(new StringExp(e->loc, const_cast<char *>("ref"))); |
| else if (stc & STCin) |
| exps->push(new StringExp(e->loc, const_cast<char *>("in"))); |
| else if (stc & STClazy) |
| exps->push(new StringExp(e->loc, const_cast<char *>("lazy"))); |
| else if (stc & STCalias) |
| exps->push(new StringExp(e->loc, const_cast<char *>("alias"))); |
| |
| if (stc & STCconst) |
| exps->push(new StringExp(e->loc, const_cast<char *>("const"))); |
| if (stc & STCimmutable) |
| exps->push(new StringExp(e->loc, const_cast<char *>("immutable"))); |
| if (stc & STCwild) |
| exps->push(new StringExp(e->loc, const_cast<char *>("inout"))); |
| if (stc & STCshared) |
| exps->push(new StringExp(e->loc, const_cast<char *>("shared"))); |
| if (stc & STCscope && !(stc & STCscopeinferred)) |
| exps->push(new StringExp(e->loc, const_cast<char *>("scope"))); |
| |
| TupleExp *tup = new TupleExp(e->loc, exps); |
| return semantic(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]; |
| Type *t = isType(o); |
| TypeFunction *tf = NULL; |
| 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(); |
| } |
| if (tf) |
| link = tf->linkage; |
| else |
| { |
| Dsymbol *s = getDsymbol(o); |
| Declaration *d = NULL; |
| if (!s || (d = s->isDeclaration()) == NULL) |
| { |
| e->error("argument to `__traits(getLinkage, %s)` is not a declaration", o->toChars()); |
| return new ErrorExp(); |
| } |
| link = d->linkage; |
| } |
| const char *linkage = linkageToChars(link); |
| StringExp *se = new StringExp(e->loc, const_cast<char *>(linkage)); |
| return semantic(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; |
| } |
| |
| 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; |
| //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); |
| if (sm->ident) |
| { |
| const char *idx = sm->ident->toChars(); |
| if (idx[0] == '_' && idx[1] == '_' && |
| sm->ident != Id::ctor && |
| sm->ident != Id::dtor && |
| sm->ident != Id::__xdtor && |
| sm->ident != Id::postblit && |
| sm->ident != Id::__xpostblit) |
| { |
| 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->dim; 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->_scope) |
| cd->semantic(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->dim; i++) |
| { |
| ClassDeclaration *cb = (*cd->baseclasses)[i]->sym; |
| assert(cb); |
| ScopeDsymbol_foreach(NULL, cb->members, &PushIdentsDg::dg, ctx); |
| if (cb->baseclasses->dim) |
| 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->dim; i++) |
| { |
| Identifier *id = (*idents)[i]; |
| StringExp *se = new StringExp(e->loc, const_cast<char *>(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 = semantic(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); |
| Expression *ex = t ? typeToExpression(t) : isExpression(o); |
| if (!ex && t) |
| { |
| Dsymbol *s; |
| t->resolve(e->loc, sc2, &ex, &t, &s); |
| if (t) |
| { |
| t->semantic(e->loc, sc2); |
| if (t->ty == Terror) |
| err = true; |
| } |
| else if (s && s->errors) |
| err = true; |
| } |
| if (ex) |
| { |
| ex = semantic(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]; |
| 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 = getDsymbol(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 semantic(te, sc); |
| } |
| else if(e->ident == Id::getVirtualIndex) |
| { |
| if (dim != 1) |
| return dimError(e, 1, dim); |
| |
| RootObject *o = (*e->args)[0]; |
| Dsymbol *s = getDsymbol(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); |
| } |
| |
| 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(); |
| } |