void visit(NewExp *e) { //printf("NewExp::inlineCost3() %s\n", e->toChars()); AggregateDeclaration *ad = isAggregate(e->newtype); if (ad && ad->isNested()) cost = COST_MAX; else cost++; }
Expression *TraitsExp::semantic(Scope *sc) { #if LOGSEMANTIC printf("TraitsExp::semantic() %s\n", toChars()); #endif if (ident != Id::compiles && ident != Id::isSame && ident != Id::identifier) { TemplateInstance::semanticTiargs(loc, sc, args, 1); } size_t dim = args ? args->dim : 0; Declaration *d; #define ISTYPE(cond) \ for (size_t i = 0; i < dim; i++) \ { Type *t = getType((*args)[i]); \ if (!t) \ goto Lfalse; \ if (!(cond)) \ goto Lfalse; \ } \ if (!dim) \ goto Lfalse; \ goto Ltrue; #define ISDSYMBOL(cond) \ for (size_t i = 0; i < dim; i++) \ { Dsymbol *s = getDsymbol((*args)[i]); \ if (!s) \ goto Lfalse; \ if (!(cond)) \ goto Lfalse; \ } \ if (!dim) \ goto Lfalse; \ goto Ltrue; if (ident == Id::isArithmetic) { ISTYPE(t->isintegral() || t->isfloating()) } else if (ident == Id::isFloating) { ISTYPE(t->isfloating()) } else if (ident == Id::isIntegral) { ISTYPE(t->isintegral()) } else if (ident == Id::isScalar) { ISTYPE(t->isscalar()) } else if (ident == Id::isUnsigned) { ISTYPE(t->isunsigned()) } else if (ident == Id::isAssociativeArray) { ISTYPE(t->toBasetype()->ty == Taarray) } else if (ident == Id::isStaticArray) { ISTYPE(t->toBasetype()->ty == Tsarray) } else if (ident == Id::isAbstractClass) { ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract()) } else if (ident == Id::isFinalClass) { ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) } else if (ident == Id::isPOD) { if (dim != 1) goto Ldimerror; Object *o = (*args)[0]; Type *t = isType(o); StructDeclaration *sd; if (!t) { error("type expected as second argument of __traits %s instead of %s", ident->toChars(), o->toChars()); goto Lfalse; } if (t->toBasetype()->ty == Tstruct && ((sd = (StructDeclaration *)(((TypeStruct *)t->toBasetype())->sym)) != NULL)) { if (sd->isPOD()) goto Ltrue; else goto Lfalse; } goto Ltrue; } else if (ident == Id::isNested) { if (dim != 1) goto Ldimerror; Object *o = (*args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *a; FuncDeclaration *f; if (!s) { } else if ((a = s->isAggregateDeclaration()) != NULL) { if (a->isNested()) goto Ltrue; else goto Lfalse; } else if ((f = s->isFuncDeclaration()) != NULL) { if (f->isNested()) goto Ltrue; else goto Lfalse; } error("aggregate or function expected instead of '%s'", o->toChars()); goto Lfalse; } else if (ident == Id::isAbstractFunction) { FuncDeclaration *f; ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isAbstract()) }
/****************************************** * Return elem that evaluates to the static frame pointer for function fd. * If fd is a member function, the returned expression will compute the value * of fd's 'this' variable. * This routine is critical for implementing nested functions. */ elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd) { elem *ethis; FuncDeclaration *thisfd = irs->getFunc(); Dsymbol *fdparent = fd->toParent2(); Dsymbol *fdp = fdparent; /* These two are compiler generated functions for the in and out contracts, * and are called from an overriding function, not just the one they're * nested inside, so this hack is so they'll pass */ if (fdparent != thisfd && (fd->ident == Id::require || fd->ident == Id::ensure)) { FuncDeclaration *fdthis = thisfd; for (size_t i = 0; ; ) { if (i == fdthis->foverrides.dim) { if (i == 0) break; fdthis = fdthis->foverrides[0]; i = 0; continue; } if (fdthis->foverrides[i] == fdp) { fdparent = thisfd; break; } i++; } } //printf("[%s] getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", loc.toChars(), thisfd->toPrettyChars(), fd->toPrettyChars(), fdparent->toPrettyChars()); if (fdparent == thisfd) { /* Going down one nesting level, i.e. we're calling * a nested function from its enclosing function. */ if (irs->sclosure && !(fd->ident == Id::require || fd->ident == Id::ensure)) { ethis = el_var(irs->sclosure); } else if (irs->sthis) { // We have a 'this' pointer for the current function /* If no variables in the current function's frame are * referenced by nested functions, then we can 'skip' * adding this frame into the linked list of stack * frames. */ if (thisfd->hasNestedFrameRefs()) { /* Local variables are referenced, can't skip. * Address of 'sthis' gives the 'this' for the nested * function */ ethis = el_ptr(irs->sthis); } else { ethis = el_var(irs->sthis); } } else { /* No 'this' pointer for current function, */ if (thisfd->hasNestedFrameRefs()) { /* OPframeptr is an operator that gets the frame pointer * for the current function, i.e. for the x86 it gets * the value of EBP */ ethis = el_long(TYnptr, 0); ethis->Eoper = OPframeptr; } else { /* Use NULL if no references to the current function's frame */ ethis = el_long(TYnptr, 0); } } } else { if (!irs->sthis) // if no frame pointer for this function { fd->error(loc, "is a nested function and cannot be accessed from %s", irs->getFunc()->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } /* Go up a nesting level, i.e. we need to find the 'this' * of an enclosing function. * Our 'enclosing function' may also be an inner class. */ ethis = el_var(irs->sthis); Dsymbol *s = thisfd; while (fd != s) { FuncDeclaration *fdp = s->toParent2()->isFuncDeclaration(); //printf("\ts = '%s'\n", s->toChars()); thisfd = s->isFuncDeclaration(); if (thisfd) { /* Enclosing function is a function. */ // Error should have been caught by front end assert(thisfd->isNested() || thisfd->vthis); } else { /* Enclosed by an aggregate. That means the current * function must be a member function of that aggregate. */ AggregateDeclaration *ad = s->isAggregateDeclaration(); if (!ad) { Lnoframe: irs->getFunc()->error(loc, "cannot get frame pointer to %s", fd->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } ClassDeclaration *cd = ad->isClassDeclaration(); ClassDeclaration *cdx = fd->isClassDeclaration(); if (cd && cdx && cdx->isBaseOf(cd, NULL)) break; StructDeclaration *sd = ad->isStructDeclaration(); if (fd == sd) break; if (!ad->isNested() || !ad->vthis) goto Lnoframe; ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, ad->vthis->offset)); ethis = el_una(OPind, TYnptr, ethis); } if (fdparent == s->toParent2()) break; /* Remember that frames for functions that have no * nested references are skipped in the linked list * of frames. */ if (fdp && fdp->hasNestedFrameRefs()) ethis = el_una(OPind, TYnptr, ethis); s = s->toParent2(); assert(s); } } #if 0 printf("ethis:\n"); elem_print(ethis); printf("\n"); #endif return ethis; }
Expression *semanticTraits(TraitsExp *e, Scope *sc) { #if LOGSEMANTIC printf("TraitsExp::semantic() %s\n", e->toChars()); #endif 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::isPOD) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Type *t = isType(o); StructDeclaration *sd; if (!t) { e->error("type expected as second argument of __traits %s instead of %s", e->ident->toChars(), o->toChars()); goto Lfalse; } Type *tb = t->baseElemOf(); if (tb->ty == Tstruct && ((sd = (StructDeclaration *)(((TypeStruct *)tb)->sym)) != NULL)) { if (sd->isPOD()) goto Ltrue; else goto Lfalse; } goto Ltrue; } else if (e->ident == Id::isNested) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *a; FuncDeclaration *f; if (!s) { } else if ((a = s->isAggregateDeclaration()) != NULL) { if (a->isNested()) goto Ltrue; else goto Lfalse; } else if ((f = s->isFuncDeclaration()) != NULL) { if (f->isNested()) goto Ltrue; else goto Lfalse; } e->error("aggregate or function expected instead of '%s'", o->toChars()); goto Lfalse; } 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) goto Ldimerror; RootObject *o = (*e->args)[0]; Parameter *po = isParameter(o); Identifier *id; if (po) { id = po->ident; assert(id); } else { Dsymbol *s = getDsymbol(o); if (!s || !s->ident) { e->error("argument %s has no identifier", o->toChars()); goto Lfalse; } id = s->ident; } StringExp *se = new StringExp(e->loc, id->toChars()); return se->semantic(sc); } else if (e->ident == Id::getProtection) { if (dim != 1) goto Ldimerror; 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()); goto Lfalse; } if (s->scope) s->semantic(s->scope); PROT protection = s->prot(); const char *protName = Pprotectionnames[protection]; assert(protName); StringExp *se = new StringExp(e->loc, (char *) protName); return se->semantic(sc); } else if (e->ident == Id::parent) { if (dim != 1) goto Ldimerror; 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()); goto Lfalse; } 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 = ex->semantic(sc); return ex; } if (FuncLiteralDeclaration *fld = f->isFuncLiteralDeclaration()) { // Directly translate to VarExp instead of FuncExp Expression *ex = new VarExp(e->loc, fld, 1); return ex->semantic(sc); } } return (new DsymbolExp(e->loc, s))->semantic(sc); } 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) goto Ldimerror; 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()); goto Lfalse; } ex = ex->ctfeInterpret(); StringExp *se = ex->toStringExp(); if (!se || se->length() == 0) { e->error("string expected as second argument of __traits %s instead of %s", e->ident->toChars(), ex->toChars()); goto Lfalse; } se = se->toUTF8(sc); if (se->sz != 1) { e->error("string must be chars"); goto Lfalse; } Identifier *id = Lexer::idPool((char *)se->string); /* 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"); goto Lfalse; } if (e->ident == Id::hasMember) { if (sym) { Dsymbol *sm = sym->search(e->loc, id); if (sm) goto Ltrue; } /* Take any errors as meaning it wasn't found */ Scope *sc2 = sc->push(); ex = ex->trySemantic(sc2); sc2->pop(); if (!ex) goto Lfalse; else goto Ltrue; } else if (e->ident == Id::getMember) { ex = ex->semantic(sc); return ex; } else if (e->ident == Id::getVirtualFunctions || e->ident == Id::getVirtualMethods || e->ident == Id::getOverloads) { unsigned errors = global.errors; Expression *eorig = ex; ex = ex->semantic(sc); if (errors < global.errors) e->error("%s cannot be resolved", eorig->toChars()); /* Create tuple of functions of ex */ //ex->print(); 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); TupleExp *tup = new TupleExp(e->loc, exps); return tup->semantic(sc); } else assert(0); } else if (e->ident == Id::classInstanceSize) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); ClassDeclaration *cd; if (!s || (cd = s->isClassDeclaration()) == NULL) { e->error("first argument is not a class"); goto Lfalse; } if (cd->sizeok == SIZEOKnone) { if (cd->scope) cd->semantic(cd->scope); } if (cd->sizeok != SIZEOKdone) { e->error("%s %s is forward referenced", cd->kind(), cd->toChars()); goto Lfalse; } return new IntegerExp(e->loc, cd->structsize, Type::tsize_t); } else if (e->ident == Id::getAliasThis) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *ad; if (!s || (ad = s->isAggregateDeclaration()) == NULL) { e->error("argument is not an aggregate type"); goto Lfalse; } Expressions *exps = new Expressions(); if (ad->aliasthis) exps->push(new StringExp(e->loc, ad->aliasthis->ident->toChars())); Expression *ex = new TupleExp(e->loc, exps); ex = ex->semantic(sc); return ex; } else if (e->ident == Id::getAttributes) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { #if 0 Expression *x = isExpression(o); Type *t = isType(o); if (x) printf("e = %s %s\n", Token::toChars(x->op), x->toChars()); if (t) printf("t = %d %s\n", t->ty, t->toChars()); #endif e->error("first argument is not a symbol"); goto Lfalse; } //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttributes, s->userAttributesScope); UserAttributeDeclaration *udad = s->userAttribDecl; TupleExp *tup = new TupleExp(e->loc, udad ? udad->getAttributes() : new Expressions()); return tup->semantic(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) goto Ldimerror; 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"); goto Lfalse; } 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 tup->semantic(sc); } else if (e->ident == Id::allMembers || e->ident == Id::derivedMembers) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); ScopeDsymbol *sds; if (!s) { e->error("argument has no members"); goto Lfalse; } Import *import; if ((import = s->isImport()) != NULL) { // Bugzilla 9692 sds = import->mod; } else if ((sds = s->isScopeDsymbol()) == NULL) { e->error("%s %s has no members", s->kind(), s->toChars()); goto Lfalse; } // use a struct as local function struct PushIdentsDg { static int dg(void *ctx, size_t n, Dsymbol *sm) { if (!sm) return 1; //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); if (sm->ident) { if (sm->ident != Id::ctor && sm->ident != Id::dtor && sm->ident != Id::_postblit && memcmp(sm->ident->string, "__", 2) == 0) { return 0; } //printf("\t%s\n", sm->ident->toChars()); Identifiers *idents = (Identifiers *)ctx; /* 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; #ifdef DEBUG // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop. assert(strcmp(id->toChars(), sm->ident->toChars()) != 0); #endif } idents->push(sm->ident); } else { EnumDeclaration *ed = sm->isEnumDeclaration(); if (ed) { ScopeDsymbol::foreach(NULL, ed->members, &PushIdentsDg::dg, (Identifiers *)ctx); } } return 0; } }; Identifiers *idents = new Identifiers; ScopeDsymbol::foreach(sc, sds->members, &PushIdentsDg::dg, idents); ClassDeclaration *cd = sds->isClassDeclaration(); if (cd && e->ident == Id::allMembers) { struct PushBaseMembers { static void dg(ClassDeclaration *cd, Identifiers *idents) { for (size_t i = 0; i < cd->baseclasses->dim; i++) { ClassDeclaration *cb = (*cd->baseclasses)[i]->base; ScopeDsymbol::foreach(NULL, cb->members, &PushIdentsDg::dg, idents); if (cb->baseclasses->dim) dg(cb, idents); } } }; PushBaseMembers::dg(cd, idents); } // 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, 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 = ex->semantic(sc); return ex; } else if (e->ident == Id::compiles) { /* Determine if all the objects - types, expressions, or symbols - * compile without error */ if (!dim) goto Lfalse; for (size_t i = 0; i < dim; i++) { unsigned errors = global.startGagging(); unsigned oldspec = global.speculativeGag; global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->speculative = true; sc2->flags = sc->flags & ~SCOPEctfe | SCOPEcompile; bool err = false; RootObject *o = (*e->args)[i]; Type *t = isType(o); Expression *ex = t ? t->toExpression() : 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 = ex->semantic(sc2); ex = resolvePropertiesOnly(sc2, ex); ex = ex->optimize(WANTvalue); ex = checkGC(sc2, ex); if (ex->op == TOKerror) err = true; } sc2->pop(); global.speculativeGag = oldspec; if (global.endGagging(errors) || err) { goto Lfalse; } } goto Ltrue; } else if (e->ident == Id::isSame) { /* Determine if two symbols are the same */ if (dim != 2) goto Ldimerror; 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 0 printf("o1: %p\n", o1); printf("o2: %p\n", o2); if (!s1) { Expression *ea = isExpression(o1); if (ea) printf("%s\n", ea->toChars()); Type *ta = isType(o1); if (ta) printf("%s\n", ta->toChars()); goto Lfalse; } else printf("%s %s\n", s1->kind(), s1->toChars()); #endif if (!s1 && !s2) { Expression *ea1 = isExpression(o1); Expression *ea2 = isExpression(o2); if (ea1 && ea2) { if (ea1->equals(ea2)) goto Ltrue; } } if (!s1 || !s2) goto Lfalse; s1 = s1->toAlias(); s2 = s2->toAlias(); if (s1->isFuncAliasDeclaration()) s1 = ((FuncAliasDeclaration *)s1)->toAliasFunc(); if (s2->isFuncAliasDeclaration()) s2 = ((FuncAliasDeclaration *)s2)->toAliasFunc(); if (s1 == s2) goto Ltrue; else goto Lfalse; } else if (e->ident == Id::getUnitTests) { if (dim != 1) goto Ldimerror; 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()); goto Lfalse; } Import *imp = s->isImport(); if (imp) // Bugzilla 10990 s = imp->mod; ScopeDsymbol* scope = s->isScopeDsymbol(); if (!scope) { e->error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", s->toChars(), s->kind()); goto Lfalse; } Expressions* unitTests = new Expressions(); Dsymbols* symbols = scope->members; if (global.params.useUnitTests && symbols) { // Should actually be a set AA* uniqueUnitTests = NULL; collectUnitTests(symbols, uniqueUnitTests, unitTests); } TupleExp *tup = new TupleExp(e->loc, unitTests); return tup->semantic(sc); } else if(e->ident == Id::getVirtualIndex) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); FuncDeclaration *fd; if (!s || (fd = s->isFuncDeclaration()) == NULL) { e->error("first argument to __traits(getVirtualIndex) must be a function"); goto Lfalse; } fd = fd->toAliasFunc(); // Neccessary to support multiple overloads. return new IntegerExp(e->loc, fd->vtblIndex, Type::tptrdiff_t); } else { 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()); goto Lfalse; } return NULL; Ldimerror: e->error("wrong number of arguments %d", (int)dim); goto Lfalse; Lfalse: return new IntegerExp(e->loc, 0, Type::tbool); Ltrue: return new IntegerExp(e->loc, 1, Type::tbool); }