diff --git a/dsymbol.c b/dsymbol.c index 095332f..584b41e 100644 --- a/dsymbol.c +++ b/dsymbol.c @@ -900,7 +900,6 @@ Dsymbol *ScopeDsymbol::symtabInsert(Dsymbol *s) * Determine number of Dsymbols, folding in AttribDeclaration members. */ -#if DMDV2 size_t ScopeDsymbol::dim(Array *members) { size_t n = 0; @@ -920,7 +919,6 @@ size_t ScopeDsymbol::dim(Array *members) } return n; } -#endif /*************************************** * Get nth Dsymbol, folding in AttribDeclaration members. @@ -930,7 +928,6 @@ size_t ScopeDsymbol::dim(Array *members) * of Dsymbols */ -#if DMDV2 Dsymbol *ScopeDsymbol::getNth(Array *members, size_t nth, size_t *pn) { if (!members) @@ -957,7 +954,6 @@ Dsymbol *ScopeDsymbol::getNth(Array *members, size_t nth, size_t *pn) *pn += n; return NULL; } -#endif /******************************************* * Look for member of the form: diff --git a/expression.c b/expression.c index 0bb719c..42400b6 100644 --- a/expression.c +++ b/expression.c @@ -1017,6 +1017,25 @@ Expression *Expression::semantic(Scope *sc) return this; } +/********************************** + * Try to run semantic routines. + * If they fail, return NULL. + */ + +Expression *Expression::trySemantic(Scope *sc) +{ + unsigned errors = global.errors; + global.gag++; + Expression *e = semantic(sc); + global.gag--; + if (errors != global.errors) + { + global.errors = errors; + e = NULL; + } + return e; +} + void Expression::print() { fprintf(stdmsg, "%s\n", toChars()); @@ -4606,7 +4625,6 @@ void TypeidExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) } /************************ TraitsExp ************************************/ -#if DMDV2 /* * __traits(identifier, args...) */ @@ -4640,7 +4658,6 @@ void TraitsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) } buf->writeByte(')'); } -#endif /************************************************************/ diff --git a/expression.h b/expression.h index d098acb..1c91c4d 100644 --- a/expression.h +++ b/expression.h @@ -86,6 +86,7 @@ struct Expression : Object Expression *copy(); virtual Expression *syntaxCopy(); virtual Expression *semantic(Scope *sc); + Expression *trySemantic(Scope *sc); int dyncast() { return DYNCAST_EXPRESSION; } // kludge for template.isExpression() @@ -661,7 +662,6 @@ struct TypeidExp : Expression void toCBuffer(OutBuffer *buf, HdrGenState *hgs); }; -#if DMDV2 struct TraitsExp : Expression { Identifier *ident; @@ -672,7 +672,6 @@ struct TraitsExp : Expression Expression *semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); }; -#endif struct HaltExp : Expression { diff --git a/idgen.c b/idgen.c index 8e3bf4c..18ea757 100644 --- a/idgen.c +++ b/idgen.c @@ -227,6 +227,35 @@ Msgtable msgtable[] = { "main" }, { "WinMain" }, { "DllMain" }, + + + // Traits + { "isAbstractClass" }, + { "isArithmetic" }, + { "isAssociativeArray" }, + { "isFinalClass" }, + { "isFloating" }, + { "isIntegral" }, + { "isScalar" }, + { "isStaticArray" }, + { "isUnsigned" }, + { "isVirtualFunction" }, + { "isAbstractFunction" }, + { "isFinalFunction" }, + { "isStaticFunction" }, + { "isRef" }, + { "isOut" }, + { "isLazy" }, + { "hasMember" }, + { "identifier" }, + { "getMember" }, + { "getOverloads" }, + { "getVirtualFunctions" }, + { "classInstanceSize" }, + { "allMembers" }, + { "derivedMembers" }, + { "isSame" }, + { "compiles" }, }; diff --git a/lexer.c b/lexer.c index e5f8e49..45fac0d 100644 --- a/lexer.c +++ b/lexer.c @@ -2944,11 +2944,11 @@ static Keyword keywords[] = // Added after 1.0 { "ref", TOKref }, { "macro", TOKmacro }, + { "__traits", TOKtraits }, #if DMDV2 { "pure", TOKpure }, { "nothrow", TOKnothrow }, { "__thread", TOKtls }, - { "__traits", TOKtraits }, { "__overloadset", TOKoverloadset }, { "__FILE__", TOKfile }, { "__LINE__", TOKline }, diff --git a/lexer.h b/lexer.h index 9461d66..ee5e063 100644 --- a/lexer.h +++ b/lexer.h @@ -152,8 +152,8 @@ enum TOK // Added after 1.0 TOKref, TOKmacro, -#if DMDV2 TOKtraits, +#if DMDV2 TOKoverloadset, TOKpure, TOKnothrow, diff --git a/parse.c b/parse.c index e136763..58eec4c 100644 --- a/parse.c +++ b/parse.c @@ -1616,11 +1616,16 @@ Dsymbol *Parser::parseMixin() Objects *Parser::parseTemplateArgumentList() { //printf("Parser::parseTemplateArgumentList()\n"); - Objects *tiargs = new Objects(); if (token.value != TOKlparen) { error("!(TemplateArgumentList) expected following TemplateIdentifier"); - return tiargs; + return new Objects(); } + return parseTemplateArgumentList2(); +} + +Objects *Parser::parseTemplateArgumentList2() +{ + Objects *tiargs = new Objects(); nextToken(); // Get TemplateArgumentList @@ -2841,10 +2846,10 @@ Statement *Parser::parseStatement(int flags) case TOKtypeid: case TOKis: case TOKlbracket: + case TOKtraits: #if DMDV2 case TOKtilde: case TOKnot: - case TOKtraits: case TOKfile: case TOKline: #endif @@ -4384,7 +4389,6 @@ Expression *Parser::parsePrimaryExp() break; } -#if DMDV2 case TOKtraits: { /* __traits(identifier, args...) */ @@ -4407,7 +4411,6 @@ Expression *Parser::parsePrimaryExp() e = new TraitsExp(loc, ident, args); break; } -#endif case TOKis: { Type *targ; diff --git a/traits.c b/traits.c index 7adc1b5..4966740 100644 --- a/traits.c +++ b/traits.c @@ -430,3 +430,438 @@ Ltrue: #endif + + +/************************************************ + * 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, FuncDeclaration *f) +{ Ptrait *p = (Ptrait *)param; + + if (p->ident == Id::getVirtualFunctions && !f->isVirtual()) + return 0; + + Expression *e; + + if (p->e1->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)p->e1; + e = new DotVarExp(0, dve->e1, f); + } + else + e = new DsymbolExp(0, f); + p->exps->push(e); + return 0; +} + +/************************ TraitsExp ************************************/ + +Expression *TraitsExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("TraitsExp::semantic() %s\n", toChars()); +#endif + if (ident != Id::compiles && ident != Id::isSame) + TemplateInstance::semanticTiargs(loc, sc, args, 1); + size_t dim = args ? args->dim : 0; + Object *o; + Declaration *d; + FuncDeclaration *f; + +#define ISTYPE(cond) \ + for (size_t i = 0; i < dim; i++) \ + { Type *t = getType((Object *)args->data[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((Object *)args->data[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::isAbstractFunction) + { + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isAbstract()) + } + else if (ident == Id::isVirtualFunction) + { + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isVirtual()) + } + else if (ident == Id::isFinalFunction) + { + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isFinal()) + } + else if (ident == Id::isStaticFunction) + { + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && !f->needThis()) + } + else if (ident == Id::isRef) + { + ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isRef()) + } + else if (ident == Id::isOut) + { + ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isOut()) + } + else if (ident == Id::isLazy) + { + ISDSYMBOL((d = s->isDeclaration()) != NULL && d->storage_class & STClazy) + } + else if (ident == Id::identifier) + { // Get identifier for symbol as a string literal + if (dim != 1) + goto Ldimerror; + Object *o = (Object *)args->data[0]; + Dsymbol *s = getDsymbol(o); + if (!s || !s->ident) + { + error("argument %s has no identifier", o->toChars()); + goto Lfalse; + } + StringExp *se = new StringExp(loc, s->ident->toChars()); + return se->semantic(sc); + } + + else if (ident == Id::hasMember || + ident == Id::getMember || + ident == Id::getOverloads || + ident == Id::getVirtualFunctions) + { + if (dim != 2) + goto Ldimerror; + Object *o = (Object *)args->data[0]; + Expression *e = isExpression((Object *)args->data[1]); + if (!e) + { error("expression expected as second argument of __traits %s", ident->toChars()); + goto Lfalse; + } + e = e->optimize(WANTvalue | WANTinterpret); + if (e->op != TOKstring) + { error("string expected as second argument of __traits %s instead of %s", ident->toChars(), e->toChars()); + goto Lfalse; + } + StringExp *se = (StringExp *)e; + se = se->toUTF8(sc); + if (se->sz != 1) + { error("string must be chars"); + goto Lfalse; + } + Identifier *id = Lexer::idPool((char *)se->string); + + Type *t = isType(o); + e = isExpression(o); + Dsymbol *s = isDsymbol(o); + if (t) + e = typeDotIdExp(loc, t, id); + else if (e) + e = new DotIdExp(loc, e, id); + else if (s) + { e = new DsymbolExp(loc, s); + e = new DotIdExp(loc, e, id); + } + else + { error("invalid first argument"); + goto Lfalse; + } + + if (ident == Id::hasMember) + { /* Take any errors as meaning it wasn't found + */ + e = e->trySemantic(sc); + if (!e) + { if (global.gag) + global.errors++; + goto Lfalse; + } + else + goto Ltrue; + } + else if (ident == Id::getMember) + { + e = e->semantic(sc); + return e; + } + else if (ident == Id::getVirtualFunctions || + ident == Id::getOverloads) + { + unsigned errors = global.errors; + Expression *ex = e; + e = e->semantic(sc); + if (errors < global.errors) + error("%s cannot be resolved", ex->toChars()); + + /* Create tuple of functions of e + */ + //e->dump(0); + Expressions *exps = new Expressions(); + FuncDeclaration *f; + if (e->op == TOKvar) + { VarExp *ve = (VarExp *)e; + f = ve->var->isFuncDeclaration(); + } + else if (e->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)e; + f = dve->var->isFuncDeclaration(); + } + else + f = NULL; + Ptrait p; + p.exps = exps; + p.e1 = e; + p.ident = ident; + overloadApply(f, fptraits, &p); + + TupleExp *tup = new TupleExp(loc, exps); + return tup->semantic(sc); + } + else + assert(0); + } + else if (ident == Id::classInstanceSize) + { + if (dim != 1) + goto Ldimerror; + Object *o = (Object *)args->data[0]; + Dsymbol *s = getDsymbol(o); + ClassDeclaration *cd; + if (!s || (cd = s->isClassDeclaration()) == NULL) + { + error("first argument is not a class"); + goto Lfalse; + } + return new IntegerExp(loc, cd->structsize, Type::tsize_t); + } + else if (ident == Id::allMembers || ident == Id::derivedMembers) + { + if (dim != 1) + goto Ldimerror; + Object *o = (Object *)args->data[0]; + Dsymbol *s = getDsymbol(o); + ScopeDsymbol *sd; + if (!s) + { + error("argument has no members"); + goto Lfalse; + } + if ((sd = s->isScopeDsymbol()) == NULL) + { + error("%s %s has no members", s->kind(), s->toChars()); + goto Lfalse; + } + Expressions *exps = new Expressions; + while (1) + { size_t dim = ScopeDsymbol::dim(sd->members); + for (size_t i = 0; i < dim; i++) + { + Dsymbol *sm = ScopeDsymbol::getNth(sd->members, i); + //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); + if (sm->ident) + { + //printf("\t%s\n", sm->ident->toChars()); + char *str = sm->ident->toChars(); + + /* Skip if already present in exps[] + */ + for (size_t j = 0; j < exps->dim; j++) + { StringExp *se2 = (StringExp *)exps->data[j]; + if (strcmp(str, (char *)se2->string) == 0) + goto Lnext; + } + + StringExp *se = new StringExp(loc, str); + exps->push(se); + } + Lnext: + ; + } + ClassDeclaration *cd = sd->isClassDeclaration(); + if (cd && cd->baseClass && ident == Id::allMembers) + sd = cd->baseClass; // do again with base class + else + break; + } +//#if DMDV1 +// Expression *e = new ArrayLiteralExp(loc, exps); +//#endif +//#if DMDV2 + /* Making this a tuple is more flexible, as it can be statically unrolled. + * To make an array literal, enclose __traits in [ ]: + * [ __traits(allMembers, ...) ] + */ + Expression *e = new TupleExp(loc, exps); +//#endif + e = e->semantic(sc); + return e; + } + else if (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++) + { Object *o = (Object *)args->data[i]; + Expression *e; + + unsigned errors = global.errors; + global.gag++; + + Type *t = isType(o); + if (t) + { Dsymbol *s; + t->resolve(loc, sc, &e, &t, &s); + if (t) + t->semantic(loc, sc); + else if (e) + { e = e->semantic(sc); + e = e->optimize(WANTvalue); + } + } + else + { e = isExpression(o); + if (e) + { e = e->semantic(sc); + e = e->optimize(WANTvalue); + } + } + + global.gag--; + if (errors != global.errors) + { if (global.gag == 0) + global.errors = errors; + goto Lfalse; + } + } + goto Ltrue; + } + else if (ident == Id::isSame) + { /* Determine if two symbols are the same + */ + if (dim != 2) + goto Ldimerror; + TemplateInstance::semanticTiargs(loc, sc, args, 0); + Object *o1 = (Object *)args->data[0]; + Object *o2 = (Object *)args->data[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 == s2) + goto Ltrue; + else + goto Lfalse; + } + else + { error("unrecognized trait %s", ident->toChars()); + goto Lfalse; + } + + return NULL; + +Lnottype: + error("%s is not a type", o->toChars()); + goto Lfalse; + +Ldimerror: + error("wrong number of arguments %d", dim); + goto Lfalse; + + +Lfalse: + return new IntegerExp(loc, 0, Type::tbool); + +Ltrue: + return new IntegerExp(loc, 1, Type::tbool); +}