diff --git a/dmd2/aggregate.h b/dmd2/aggregate.h index 5d0ca770..69f470a0 100644 --- a/dmd2/aggregate.h +++ b/dmd2/aggregate.h @@ -241,6 +241,7 @@ struct ClassDeclaration : AggregateDeclaration static ClassDeclaration *classinfo; static ClassDeclaration *throwable; static ClassDeclaration *exception; + static ClassDeclaration *errorException; ClassDeclaration *baseClass; // NULL only if this is Object #if DMDV1 diff --git a/dmd2/aliasthis.c b/dmd2/aliasthis.c index 76965162..f8a74b48 100644 --- a/dmd2/aliasthis.c +++ b/dmd2/aliasthis.c @@ -47,8 +47,6 @@ void AliasThis::semantic(Scope *sc) ad = parent->isAggregateDeclaration(); if (ad) { - if (ad->aliasthis) - error("there can be only one alias this"); assert(ad->members); Dsymbol *s = ad->search(loc, ident, 0); if (!s) @@ -58,6 +56,8 @@ void AliasThis::semantic(Scope *sc) else ::error(loc, "undefined identifier %s", ident->toChars()); } + else if (ad->aliasthis && s != ad->aliasthis) + error("there can be only one alias this"); ad->aliasthis = s; } else diff --git a/dmd2/attrib.c b/dmd2/attrib.c index 01a09a12..e3166823 100644 --- a/dmd2/attrib.c +++ b/dmd2/attrib.c @@ -155,7 +155,7 @@ void AttribDeclaration::semantic(Scope *sc) //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); if (d) { - for (unsigned i = 0; i < d->dim; i++) + for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = d->tdata()[i]; @@ -170,7 +170,7 @@ void AttribDeclaration::semantic2(Scope *sc) if (d) { - for (unsigned i = 0; i < d->dim; i++) + for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = d->tdata()[i]; s->semantic2(sc); } @@ -183,7 +183,7 @@ void AttribDeclaration::semantic3(Scope *sc) if (d) { - for (unsigned i = 0; i < d->dim; i++) + for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = d->tdata()[i]; s->semantic3(sc); } @@ -297,6 +297,22 @@ int AttribDeclaration::hasPointers() return 0; } +bool AttribDeclaration::hasStaticCtorOrDtor() +{ + Dsymbols *d = include(NULL, NULL); + + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + if (s->hasStaticCtorOrDtor()) + return TRUE; + } + } + return FALSE; +} + const char *AttribDeclaration::kind() { return "attribute"; diff --git a/dmd2/attrib.h b/dmd2/attrib.h index 894e88df..bfe579fe 100644 --- a/dmd2/attrib.h +++ b/dmd2/attrib.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -49,6 +49,7 @@ struct AttribDeclaration : Dsymbol const char *kind(); int oneMember(Dsymbol **ps); int hasPointers(); + bool hasStaticCtorOrDtor(); void checkCtorConstInit(); void addLocalClass(ClassDeclarations *); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); diff --git a/dmd2/cast.c b/dmd2/cast.c index 6e242d7c..90fd6380 100644 --- a/dmd2/cast.c +++ b/dmd2/cast.c @@ -395,23 +395,13 @@ MATCH NullExp::implicitConvTo(Type *t) if (this->type->equals(t)) return MATCHexact; - /* Allow implicit conversions from invariant to mutable|const, - * and mutable to invariant. It works because, after all, a null + /* Allow implicit conversions from immutable to mutable|const, + * and mutable to immutable. It works because, after all, a null * doesn't actually point to anything. */ if (t->invariantOf()->equals(type->invariantOf())) return MATCHconst; - // NULL implicitly converts to any pointer type or dynamic array - if (type->ty == Tpointer && type->nextOf()->ty == Tvoid) - { - if (t->ty == Ttypedef) - t = ((TypeTypedef *)t)->sym->basetype; - if (t->ty == Tpointer || t->ty == Tarray || - t->ty == Taarray || t->ty == Tclass || - t->ty == Tdelegate) - return committed ? MATCHconvert : MATCHexact; - } return Expression::implicitConvTo(t); } @@ -923,18 +913,18 @@ Expression *ComplexExp::castTo(Scope *sc, Type *t) Expression *NullExp::castTo(Scope *sc, Type *t) -{ NullExp *e; - Type *tb; - +{ //printf("NullExp::castTo(t = %p)\n", t); if (type == t) { committed = 1; return this; } - e = (NullExp *)copy(); + + NullExp *e = (NullExp *)copy(); e->committed = 1; - tb = t->toBasetype(); + Type *tb = t->toBasetype(); +#if 0 e->type = type->toBasetype(); if (tb != e->type) { @@ -943,7 +933,6 @@ Expression *NullExp::castTo(Scope *sc, Type *t) (tb->ty == Tpointer || tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tdelegate)) { -#if 0 if (tb->ty == Tdelegate) { TypeDelegate *td = (TypeDelegate *)tb; TypeFunction *tf = (TypeFunction *)td->nextOf(); @@ -955,13 +944,19 @@ Expression *NullExp::castTo(Scope *sc, Type *t) return Expression::castTo(sc, t); } } -#endif } else { - return e->Expression::castTo(sc, t); + //return e->Expression::castTo(sc, t); } } +#else + if (tb->ty == Tvoid) + { + e->type = type->toBasetype(); + return e->Expression::castTo(sc, t); + } +#endif e->type = t; return e; } @@ -1946,6 +1941,15 @@ Lagain: } else if (t1->ty == Tstruct && t2->ty == Tstruct) { + if (t1->mod != t2->mod) + { + unsigned char mod = MODmerge(t1->mod, t2->mod); + t1 = t1->castMod(mod); + t2 = t2->castMod(mod); + t = t1; + goto Lagain; + } + TypeStruct *ts1 = (TypeStruct *)t1; TypeStruct *ts2 = (TypeStruct *)t2; if (ts1->sym != ts2->sym) @@ -1972,7 +1976,8 @@ Lagain: e1b = resolveProperties(sc, e1b); i2 = e1b->implicitConvTo(t2); } - assert(!(i1 && i2)); + if (i1 && i2) + goto Lincompatible; if (i1) goto Lt1; @@ -1987,9 +1992,41 @@ Lagain: { e2 = e2b; t2 = e2b->type->toBasetype(); } + t = t1; goto Lagain; } } + else if (t1->ty == Tstruct || t2->ty == Tstruct) + { + if (t1->mod != t2->mod) + { + unsigned char mod = MODmerge(t1->mod, t2->mod); + t1 = t1->castMod(mod); + t2 = t2->castMod(mod); + t = t1; + goto Lagain; + } + + if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) + { + e1 = new DotIdExp(e1->loc, e1, ((TypeStruct *)t1)->sym->aliasthis->ident); + e1 = e1->semantic(sc); + e1 = resolveProperties(sc, e1); + t1 = e1->type; + t = t1; + goto Lagain; + } + if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis) + { + e2 = new DotIdExp(e2->loc, e2, ((TypeStruct *)t2)->sym->aliasthis->ident); + e2 = e2->semantic(sc); + e2 = resolveProperties(sc, e2); + t2 = e2->type; + t = t2; + goto Lagain; + } + goto Lincompatible; + } else if ((e1->op == TOKstring || e1->op == TOKnull) && e1->implicitConvTo(t2)) { goto Lt2; @@ -2091,18 +2128,11 @@ Expression *BinExp::typeCombine(Scope *sc) if (op == TOKmin || op == TOKadd) { - // struct+struct, where the structs are the same type, and class+class are errors - if (t1->ty == Tstruct) - { - if (t2->ty == Tstruct && - ((TypeStruct *)t1)->sym == ((TypeStruct *)t2)->sym) - goto Lerror; - } - else if (t1->ty == Tclass) - { - if (t2->ty == Tclass) - goto Lerror; - } + // struct+struct, and class+class are errors + if (t1->ty == Tstruct && t2->ty == Tstruct) + goto Lerror; + else if (t1->ty == Tclass && t2->ty == Tclass) + goto Lerror; } if (!typeMerge(sc, this, &type, &e1, &e2)) diff --git a/dmd2/class.c b/dmd2/class.c index bb178317..8ad01e8e 100644 --- a/dmd2/class.c +++ b/dmd2/class.c @@ -33,6 +33,7 @@ ClassDeclaration *ClassDeclaration::classinfo; ClassDeclaration *ClassDeclaration::object; ClassDeclaration *ClassDeclaration::throwable; ClassDeclaration *ClassDeclaration::exception; +ClassDeclaration *ClassDeclaration::errorException; ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) : AggregateDeclaration(loc, id) @@ -197,6 +198,12 @@ ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *basecla exception = this; } + if (id == Id::Error) + { if (errorException) + errorException->error("%s", msg); + errorException = this; + } + //if (id == Id::ClassInfo) if (id == Id::TypeInfo_Class) { if (classinfo) diff --git a/dmd2/clone.c b/dmd2/clone.c index 24fb1e61..2664f864 100644 --- a/dmd2/clone.c +++ b/dmd2/clone.c @@ -93,9 +93,8 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) FuncDeclaration *fop = NULL; - Parameter *param = new Parameter(STCnodtor, type, Id::p, NULL); Parameters *fparams = new Parameters; - fparams->push(param); + fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd); #if STRUCTTHISREF ((TypeFunction *)ftype)->isref = 1; @@ -428,14 +427,17 @@ FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) { //printf("generating cpctor\n"); - Parameter *param = new Parameter(STCref, type->constOf(), Id::p, NULL); + StorageClass stc = postblit->storage_class & + (STCdisable | STCsafe | STCtrusted | STCsystem | STCpure | STCnothrow); + if (stc & (STCsafe | STCtrusted)) + stc = stc & ~STCsafe | STCtrusted; + Parameters *fparams = new Parameters; - fparams->push(param); - Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd); + fparams->push(new Parameter(STCref, type->constOf(), Id::p, NULL)); + Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd, stc); ftype->mod = MODconst; - fcp = new FuncDeclaration(loc, 0, Id::cpctor, STCundefined, ftype); - fcp->storage_class |= postblit->storage_class & STCdisable; + fcp = new FuncDeclaration(loc, 0, Id::cpctor, stc, ftype); if (!(fcp->storage_class & STCdisable)) { diff --git a/dmd2/constfold.c b/dmd2/constfold.c index dd596a9c..5581658e 100644 --- a/dmd2/constfold.c +++ b/dmd2/constfold.c @@ -1551,7 +1551,17 @@ Expression *Cat(Type *type, Expression *e1, Expression *e2) else if (e1->op == TOKnull && e2->op == TOKnull) { if (type == e1->type) - return e1; + { + // Handle null ~= null + if (t1->ty == Tarray && t2 == t1->nextOf()) + { + e = new ArrayLiteralExp(e1->loc, e2); + e->type = type; + return e; + } + else + return e1; + } if (type == e2->type) return e2; return new NullExp(e1->loc, type); diff --git a/dmd2/declaration.c b/dmd2/declaration.c index 4e47867a..757e43bb 100644 --- a/dmd2/declaration.c +++ b/dmd2/declaration.c @@ -21,6 +21,7 @@ #include "module.h" #include "id.h" #include "expression.h" +#include "statement.h" #include "hdrgen.h" /********************************* Declaration ****************************/ @@ -700,7 +701,7 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer aliassym = NULL; onstack = 0; canassign = 0; - setValueNull(); + ctfeAdrOnStack = (size_t)(-1); #if DMDV2 rundtor = NULL; edtor = NULL; @@ -1162,9 +1163,15 @@ Lnomatch: { error("only parameters or stack based variables can be inout"); } - if (sc->func && !sc->func->type->hasWild()) + FuncDeclaration *func = sc->func; + if (func) { - error("inout variables can only be declared inside inout functions"); + if (func->fes) + func = func->fes->func; + if (!func->type->hasWild()) + { + error("inout variables can only be declared inside inout functions"); + } } } @@ -1701,8 +1708,12 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) // The current function FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); - if (fdv && fdthis && fdv != fdthis) + if (fdv && fdthis && fdv != fdthis/* && fdthis->ident != Id::ensure*/) { + /* __ensure is always called directly, + * so it never becomes closure. + */ + if (loc.filename) fdthis->getLevel(loc, fdv); @@ -1720,7 +1731,6 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) if (s == this) goto L2; } - fdv->closureVars.push(this); L2: ; diff --git a/dmd2/declaration.h b/dmd2/declaration.h index 1c3431df..ffeca869 100644 --- a/dmd2/declaration.h +++ b/dmd2/declaration.h @@ -286,13 +286,15 @@ struct VarDeclaration : Declaration int canassign; // it can be assigned to Dsymbol *aliassym; // if redone as alias to another symbol - // When interpreting, these hold the value (NULL if value not determinable) + // When interpreting, these point to the value (NULL if value not determinable) + // The index of this variable on the CTFE stack, -1 if not allocated + size_t ctfeAdrOnStack; // The various functions are used only to detect compiler CTFE bugs - Expression *literalvalue; - Expression *getValue() { return literalvalue; } + Expression *getValue(); + bool hasValue(); void setValueNull(); void setValueWithoutChecking(Expression *newval); - void createRefValue(Expression *newval); // struct or array literal + void createRefValue(Expression *newval); void setRefValue(Expression *newval); void setStackValue(Expression *newval); void createStackValue(Expression *newval); @@ -782,6 +784,7 @@ struct FuncDeclaration : Declaration void semantic3(Scope *sc); // called from semantic3 void varArgs(Scope *sc, TypeFunction*, VarDeclaration *&, VarDeclaration *&); + VarDeclaration *declareThis(Scope *sc, AggregateDeclaration *ad); int equals(Object *o); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -932,7 +935,7 @@ struct CtorDeclaration : FuncDeclaration #if DMDV2 struct PostBlitDeclaration : FuncDeclaration { - PostBlitDeclaration(Loc loc, Loc endloc); + PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc = STCundefined); PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); @@ -977,6 +980,7 @@ struct StaticCtorDeclaration : FuncDeclaration int isVirtual(); int addPreInvariant(); int addPostInvariant(); + bool hasStaticCtorOrDtor(); void emitComment(Scope *sc); void toJsonBuffer(OutBuffer *buf); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -1004,6 +1008,7 @@ struct StaticDtorDeclaration : FuncDeclaration void semantic(Scope *sc); AggregateDeclaration *isThis(); int isVirtual(); + bool hasStaticCtorOrDtor(); int addPreInvariant(); int addPostInvariant(); void emitComment(Scope *sc); diff --git a/dmd2/dsymbol.c b/dmd2/dsymbol.c index 8e147c17..704c7f88 100644 --- a/dmd2/dsymbol.c +++ b/dmd2/dsymbol.c @@ -167,6 +167,12 @@ int Dsymbol::hasPointers() return 0; } +bool Dsymbol::hasStaticCtorOrDtor() +{ + //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); + return FALSE; +} + char *Dsymbol::toChars() { return ident ? ident->toChars() : (char *)"__anonymous"; @@ -1013,36 +1019,41 @@ Dsymbol *ScopeDsymbol::symtabInsert(Dsymbol *s) return symtab->insert(s); } +/**************************************** + * Return true if any of the members are static ctors or static dtors, or if + * any members have members that are. + */ + +bool ScopeDsymbol::hasStaticCtorOrDtor() +{ + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { Dsymbol *member = (*members)[i]; + + if (member->hasStaticCtorOrDtor()) + return TRUE; + } + } + return FALSE; +} + /*************************************** * Determine number of Dsymbols, folding in AttribDeclaration members. */ #if DMDV2 +static int dimDg(void *ctx, size_t n, Dsymbol *) +{ + ++*(size_t *)ctx; + return 0; +} + size_t ScopeDsymbol::dim(Dsymbols *members) { size_t n = 0; if (members) - { - for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; - AttribDeclaration *a = s->isAttribDeclaration(); - TemplateMixin *tm = s->isTemplateMixin(); - TemplateInstance *ti = s->isTemplateInstance(); - - if (a) - { - n += dim(a->decl); - } - else if (tm) - { - n += dim(tm->members); - } - else if (ti) - ; - else - n++; - } - } + foreach(members, &dimDg, &n); return n; } #endif @@ -1056,41 +1067,65 @@ size_t ScopeDsymbol::dim(Dsymbols *members) */ #if DMDV2 +struct GetNthSymbolCtx +{ + size_t nth; + Dsymbol *sym; +}; + +static int getNthSymbolDg(void *ctx, size_t n, Dsymbol *sym) +{ + GetNthSymbolCtx *p = (GetNthSymbolCtx *)ctx; + if (n == p->nth) + { p->sym = sym; + return 1; + } + return 0; +} + Dsymbol *ScopeDsymbol::getNth(Dsymbols *members, size_t nth, size_t *pn) { - if (!members) - return NULL; + GetNthSymbolCtx ctx = { nth, NULL }; + int res = foreach(members, &getNthSymbolDg, &ctx); + return res ? ctx.sym : NULL; +} +#endif - size_t n = 0; +/*************************************** + * Expands attribute declarations in members in depth first + * order. Calls dg(void *ctx, size_t symidx, Dsymbol *sym) for each + * member. + * If dg returns !=0, stops and returns that value else returns 0. + * Use this function to avoid the O(N + N^2/2) complexity of + * calculating dim and calling N times getNth. + */ + +#if DMDV2 +int ScopeDsymbol::foreach(Dsymbols *members, ScopeDsymbol::ForeachDg dg, void *ctx, size_t *pn) +{ + assert(members); + + size_t n = pn ? *pn : 0; // take over index + int result = 0; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; - AttribDeclaration *a = s->isAttribDeclaration(); - TemplateMixin *tm = s->isTemplateMixin(); - TemplateInstance *ti = s->isTemplateInstance(); - if (a) - { - s = getNth(a->decl, nth - n, &n); - if (s) - return s; - } - else if (tm) - { - s = getNth(tm->members, nth - n, &n); - if (s) - return s; - } - else if (ti) + if (AttribDeclaration *a = s->isAttribDeclaration()) + result = foreach(a->decl, dg, ctx, &n); + else if (TemplateMixin *tm = s->isTemplateMixin()) + result = foreach(tm->members, dg, ctx, &n); + else if (s->isTemplateInstance()) ; - else if (n == nth) - return s; else - n++; + result = dg(ctx, n++, s); + + if (result) + break; } if (pn) - *pn += n; - return NULL; + *pn = n; // update index + return result; } #endif @@ -1150,7 +1185,7 @@ Dsymbol *WithScopeSymbol::search(Loc loc, Identifier *ident, int flags) ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, Expression *e) : ScopeDsymbol() { - assert(e->op == TOKindex || e->op == TOKslice); + assert(e->op == TOKindex || e->op == TOKslice || e->op == TOKarray); exp = e; type = NULL; td = NULL; @@ -1225,6 +1260,68 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) pvar = &se->lengthVar; ce = se->e1; } + else if (exp->op == TOKarray) + { /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ + * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) + */ + ArrayExp *ae = (ArrayExp *)exp; + AggregateDeclaration *ad = NULL; + + Type *t = ae->e1->type->toBasetype(); + if (t->ty == Tclass) + { + ad = ((TypeClass *)t)->sym; + } + else if (t->ty == Tstruct) + { + ad = ((TypeStruct *)t)->sym; + } + assert(ad); + + Dsymbol *dsym = search_function(ad, Id::opDollar); + if (!dsym) // no dollar exists -- search in higher scope + return NULL; + VarDeclaration *v = ae->lengthVar; + if (!v) + { // $ is lazily initialized. Create it now. + TemplateDeclaration *td = dsym->isTemplateDeclaration(); + if (td) + { // Instantiate opDollar!(dim) with the index as a template argument + Objects *tdargs = new Objects(); + tdargs->setDim(1); + + Expression *x = new IntegerExp(0, ae->currentDimension, Type::tsize_t); + x = x->semantic(sc); + tdargs->data[0] = x; + + //TemplateInstance *ti = new TemplateInstance(loc, td, tdargs); + //ti->semantic(sc); + + DotTemplateInstanceExp *dte = new DotTemplateInstanceExp(loc, ae->e1, td->ident, tdargs); + + v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, dte)); + } + else + { /* opDollar exists, but it's a function, not a template. + * This is acceptable ONLY for single-dimension indexing. + * Note that it's impossible to have both template & function opDollar, + * because both take no arguments. + */ + if (ae->arguments->dim != 1) { + ae->error("%s only defines opDollar for one dimension", ad->toChars()); + return NULL; + } + FuncDeclaration *fd = dsym->isFuncDeclaration(); + assert(fd); + Expression * x = new DotVarExp(loc, ae->e1, fd); + + v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, x)); + } + v->semantic(sc); + ae->lengthVar = v; + } + return v; + } else /* Didn't find $, look in enclosing scope(s). */ diff --git a/dmd2/dsymbol.h b/dmd2/dsymbol.h index 27cc2629..e244b6f7 100644 --- a/dmd2/dsymbol.h +++ b/dmd2/dsymbol.h @@ -206,6 +206,7 @@ struct Dsymbol : Object virtual int oneMember(Dsymbol **ps); static int oneMembers(Dsymbols *members, Dsymbol **ps); virtual int hasPointers(); + virtual bool hasStaticCtorOrDtor(); virtual void addLocalClass(ClassDeclarations *) { } virtual void checkCtorConstInit() { } @@ -305,12 +306,16 @@ struct ScopeDsymbol : Dsymbol const char *kind(); FuncDeclaration *findGetMembers(); virtual Dsymbol *symtabInsert(Dsymbol *s); + bool hasStaticCtorOrDtor(); void emitMemberComments(Scope *sc); static size_t dim(Dsymbols *members); static Dsymbol *getNth(Dsymbols *members, size_t nth, size_t *pn = NULL); + typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s); + static int foreach(Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn=NULL); + ScopeDsymbol *isScopeDsymbol() { return this; } }; diff --git a/dmd2/expression.c b/dmd2/expression.c index 656c7431..1fe0671a 100644 --- a/dmd2/expression.c +++ b/dmd2/expression.c @@ -181,6 +181,15 @@ FuncDeclaration *hasThis(Scope *sc) fdthis = sc->parent->isFuncDeclaration(); //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : ""); + /* Special case for inside template constraint + */ + if (fdthis && (sc->flags & SCOPEstaticif) && fdthis->parent->isTemplateDeclaration()) + { + //TemplateDeclaration *td = fdthis->parent->isTemplateDeclaration(); + //printf("[%s] td = %s, fdthis->vthis = %p\n", td->loc.toChars(), td->toChars(), fdthis->vthis); + return fdthis->vthis ? fdthis : NULL; + } + // Go upwards until we find the enclosing member function fd = fdthis; while (1) @@ -828,14 +837,12 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, L1: if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) { - if (p->type->hasWild()) - { unsigned mod = p->type->wildMatch(arg->type); - if (mod) - { - wildmatch |= mod; - arg = arg->implicitCastTo(sc, p->type->substWildTo(mod)); - arg = arg->optimize(WANTvalue); - } + unsigned mod = arg->type->wildConvTo(p->type); + if (mod) + { + wildmatch |= mod; + arg = arg->implicitCastTo(sc, p->type->substWildTo(mod)); + arg = arg->optimize(WANTvalue); } else if (p->type != arg->type) { @@ -2996,8 +3003,7 @@ SuperExp::SuperExp(Loc loc) } Expression *SuperExp::semantic(Scope *sc) -{ FuncDeclaration *fd; - FuncDeclaration *fdthis; +{ ClassDeclaration *cd; Dsymbol *s; @@ -3007,22 +3013,22 @@ Expression *SuperExp::semantic(Scope *sc) if (type) return this; + FuncDeclaration *fd = hasThis(sc); + /* Special case for typeof(this) and typeof(super) since both * should work even if they are not inside a non-static member function */ - if (sc->intypeof) + if (!fd && sc->intypeof) { // Find enclosing class - for (Dsymbol *s = sc->parent; 1; s = s->parent) + for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent) { - ClassDeclaration *cd; - if (!s) { error("%s is not in a class scope", toChars()); goto Lerr; } - cd = s->isClassDeclaration(); + ClassDeclaration *cd = s->isClassDeclaration(); if (cd) { cd = cd->baseClass; @@ -3035,11 +3041,9 @@ Expression *SuperExp::semantic(Scope *sc) } } } - - fdthis = sc->parent->isFuncDeclaration(); - fd = hasThis(sc); if (!fd) goto Lerr; + assert(fd->vthis); var = fd->vthis; assert(var->parent); @@ -3060,6 +3064,7 @@ Expression *SuperExp::semantic(Scope *sc) else { type = cd->baseClass->type; + type = type->castMod(var->type->mod); } var->isVarDeclaration()->checkNestedReference(sc, loc); @@ -3096,7 +3101,7 @@ Expression *NullExp::semantic(Scope *sc) #endif // NULL is the same as (void *)0 if (!type) - type = Type::tvoid->pointerTo(); + type = Type::tnull; return this; } @@ -3519,10 +3524,19 @@ void StringExp::toMangleBuffer(OutBuffer *buf) default: assert(0); } + buf->reserve(1 + 11 + 2 * qlen); buf->writeByte(m); - buf->printf("%d_", qlen); - for (size_t i = 0; i < qlen; i++) - buf->printf("%02x", q[i]); + buf->printf("%d_", qlen); // nbytes <= 11 + + for (unsigned char *p = buf->data + buf->offset, *pend = p + 2 * qlen; + p < pend; p += 2, ++q) + { + unsigned char hi = *q >> 4 & 0xF; + p[0] = (hi < 10 ? hi + '0' : hi - 10 + 'a'); + unsigned char lo = *q & 0xF; + p[1] = (lo < 10 ? lo + '0' : lo - 10 + 'a'); + } + buf->offset += 2 * qlen; } /************************ ArrayLiteralExp ************************************/ @@ -4146,6 +4160,10 @@ Lagain: //printf("sds = %s, '%s'\n", sds->kind(), sds->toChars()); //printf("\tparent = '%s'\n", sds->parent->toChars()); sds->semantic(sc); + + AggregateDeclaration *ad = sds->isAggregateDeclaration(); + if (ad) + return (new TypeExp(loc, ad->type))->semantic(sc); } type = Type::tvoid; //printf("-2ScopeExp::semantic() %s\n", toChars()); @@ -7147,6 +7165,131 @@ Expression *CallExp::syntaxCopy() } +Expression *CallExp::resolveUFCS(Scope *sc) +{ + Expression *ethis = NULL; + DotIdExp *dotid; + DotTemplateInstanceExp *dotti; + Identifier *ident; + + if (e1->op == TOKdot) + { + dotid = (DotIdExp *)e1; + ident = dotid->ident; + ethis = dotid->e1 = dotid->e1->semantic(sc); + if (ethis->op == TOKdotexp) + return NULL; + ethis = resolveProperties(sc, ethis); + } + else if (e1->op == TOKdotti) + { + dotti = (DotTemplateInstanceExp *)e1; + ident = dotti->ti->name; + ethis = dotti->e1 = dotti->e1->semantic(sc); + if (ethis->op == TOKdotexp) + return NULL; + ethis = resolveProperties(sc, ethis); + } + + if (ethis && ethis->type) + { + AggregateDeclaration *ad; +Lagain: + Type *tthis = ethis->type->toBasetype(); + if (tthis->ty == Tclass) + { + ad = ((TypeClass *)tthis)->sym; + if (search_function(ad, ident)) + return NULL; + goto L1; + } + else if (tthis->ty == Tstruct) + { + ad = ((TypeStruct *)tthis)->sym; + if (search_function(ad, ident)) + return NULL; + L1: + if (ad->aliasthis) + { + ethis = new DotIdExp(ethis->loc, ethis, ad->aliasthis->ident); + ethis = ethis->semantic(sc); + ethis = resolveProperties(sc, ethis); + goto Lagain; + } + } + else if (tthis->ty == Taarray && e1->op == TOKdot) + { + if (ident == Id::remove) + { + /* Transform: + * aa.remove(arg) into delete aa[arg] + */ + if (!arguments || arguments->dim != 1) + { error("expected key as argument to aa.remove()"); + return new ErrorExp(); + } + Expression *key = arguments->tdata()[0]; + key = key->semantic(sc); + key = resolveProperties(sc, key); + key->rvalue(); + + TypeAArray *taa = (TypeAArray *)tthis; + key = key->implicitCastTo(sc, taa->index); + + return new RemoveExp(loc, ethis, key); + } + else if (ident == Id::apply || ident == Id::applyReverse) + { + return NULL; + } + else + { TypeAArray *taa = (TypeAArray *)tthis; + assert(taa->ty == Taarray); + StructDeclaration *sd = taa->getImpl(); + Dsymbol *s = sd->search(0, ident, 2); + if (s) + return NULL; + goto Lshift; + } + } + else if (tthis->ty == Tarray || tthis->ty == Tsarray) + { +Lshift: + if (!arguments) + arguments = new Expressions(); + arguments->shift(ethis); + if (e1->op == TOKdot) + { + /* Transform: + * array.id(args) into .id(array,args) + */ +#if DMDV2 + e1 = new DotIdExp(dotid->loc, + new IdentifierExp(dotid->loc, Id::empty), + ident); +#else + e1 = new IdentifierExp(dotid->loc, ident); +#endif + } + else if (e1->op == TOKdotti) + { + /* Transform: + * array.foo!(tiargs)(args) into .foo!(tiargs)(array,args) + */ +#if DMDV2 + e1 = new DotExp(dotti->loc, + new IdentifierExp(dotti->loc, Id::empty), + new ScopeExp(dotti->loc, dotti->ti)); +#else + e1 = new ScopeExp(dotti->loc, dotti->ti); +#endif + } + //printf("-> this = %s\n", toChars()); + } + } + return NULL; +} + Expression *CallExp::semantic(Scope *sc) { TypeFunction *tf; @@ -7177,61 +7320,9 @@ Expression *CallExp::semantic(Scope *sc) return semantic(sc); } - /* Transform: - * array.id(args) into .id(array,args) - * aa.remove(arg) into delete aa[arg] - */ - if (e1->op == TOKdot) - { - // BUG: we should handle array.a.b.c.e(args) too - - DotIdExp *dotid = (DotIdExp *)(e1); - dotid->e1 = dotid->e1->semantic(sc); - assert(dotid->e1); - if (dotid->e1->type) - { - TY e1ty = dotid->e1->type->toBasetype()->ty; - if (e1ty == Taarray && dotid->ident == Id::remove) - { - if (!arguments || arguments->dim != 1) - { error("expected key as argument to aa.remove()"); - return new ErrorExp(); - } - Expression *key = arguments->tdata()[0]; - key = key->semantic(sc); - key = resolveProperties(sc, key); - key->rvalue(); - - TypeAArray *taa = (TypeAArray *)dotid->e1->type->toBasetype(); - key = key->implicitCastTo(sc, taa->index); - - return new RemoveExp(loc, dotid->e1, key); - } - else if (e1ty == Tarray || e1ty == Tsarray || - (e1ty == Taarray && dotid->ident != Id::apply && dotid->ident != Id::applyReverse)) - { - if (e1ty == Taarray) - { TypeAArray *taa = (TypeAArray *)dotid->e1->type->toBasetype(); - assert(taa->ty == Taarray); - StructDeclaration *sd = taa->getImpl(); - Dsymbol *s = sd->search(0, dotid->ident, 2); - if (s) - goto L2; - } - if (!arguments) - arguments = new Expressions(); - arguments->shift(dotid->e1); -#if DMDV2 - e1 = new DotIdExp(dotid->loc, new IdentifierExp(dotid->loc, Id::empty), dotid->ident); -#else - e1 = new IdentifierExp(dotid->loc, dotid->ident); -#endif - } - - L2: - ; - } - } + Expression *e = resolveUFCS(sc); + if (e) + return e; #if 1 /* This recognizes: @@ -7769,7 +7860,7 @@ Lagain: if (sc->func->setImpure()) error("pure function '%s' cannot call impure function pointer '%s'", sc->func->toChars(), e1->toChars()); } - if (sc->func && !((TypeFunction *)t1)->trust <= TRUSTsystem) + if (sc->func && ((TypeFunction *)t1)->trust <= TRUSTsystem) { if (sc->func->setUnsafe()) error("safe function '%s' cannot call system function pointer '%s'", sc->func->toChars(), e1->toChars()); @@ -8181,6 +8272,8 @@ Expression *PtrExp::semantic(Scope *sc) case Tsarray: case Tarray: + if (!global.params.useDeprecated) + error("using * on an array is deprecated; use *(%s).ptr instead", e1->toChars()); type = ((TypeArray *)tb)->next; e1 = e1->castTo(sc, type->pointerTo()); break; @@ -9095,6 +9188,8 @@ ArrayExp::ArrayExp(Loc loc, Expression *e1, Expressions *args) : UnaExp(loc, TOKarray, sizeof(ArrayExp), e1) { arguments = args; + lengthVar = NULL; + currentDimension = 0; } Expression *ArrayExp::syntaxCopy() @@ -9639,6 +9734,29 @@ Expression *AssignExp::semantic(Scope *sc) // Rewrite (a[i] = value) to (a.opIndexAssign(value, i)) if (search_function(ad, Id::indexass)) { Expression *e = new DotIdExp(loc, ae->e1, Id::indexass); + // Deal with $ + for (size_t i = 0; i < ae->arguments->dim; i++) + { Expression *x = ae->arguments->tdata()[i]; + // Create scope for '$' variable for this dimension + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); + sym->loc = loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + ae->lengthVar = NULL; // Create it only if required + ae->currentDimension = i; // Dimension for $, if required + + x = x->semantic(sc); + if (!x->type) + ae->error("%s has no value", x->toChars()); + if (ae->lengthVar) + { // If $ was used, declare it now + Expression *av = new DeclarationExp(ae->loc, ae->lengthVar); + x = new CommaExp(0, av, x); + x->semantic(sc); + } + ae->arguments->tdata()[i] = x; + sc = sc->pop(); + } Expressions *a = (Expressions *)ae->arguments->copy(); a->insert(0, e2); @@ -10089,7 +10207,7 @@ Expression *AssignExp::checkToBoolean(Scope *sc) // if (a = b) ... // are usually mistakes. - error("'=' does not give a boolean result"); + error("assignment cannot be used as a condition, perhaps == was meant?"); return new ErrorExp(); } @@ -10865,7 +10983,7 @@ Expression *MinExp::semantic(Scope *sc) else if (t2->isintegral()) e = scaleFactor(sc); else - { error("incompatible types for minus"); + { error("can't subtract %s from pointer", t2->toChars()); return new ErrorExp(); } } @@ -11762,7 +11880,33 @@ Expression *CmpExp::semantic(Scope *sc) return new ErrorExp(); } + Expression *eb1 = e1; + Expression *eb2 = e2; + typeCombine(sc); + +#if 0 + // For integer comparisons, ensure the combined type can hold both arguments. + if (type && type->isintegral() && (op == TOKlt || op == TOKle || + op == TOKgt || op == TOKge)) + { + IntRange trange = IntRange::fromType(type); + + Expression *errorexp = 0; + if (!trange.contains(eb1->getIntRange())) + errorexp = eb1; + if (!trange.contains(eb2->getIntRange())) + errorexp = eb2; + + if (errorexp) + { + error("implicit conversion of '%s' to '%s' is unsafe in '(%s) %s (%s)'", + errorexp->toChars(), type->toChars(), eb1->toChars(), Token::toChars(op), eb2->toChars()); + return new ErrorExp(); + } + } +#endif + type = Type::tboolean; // Special handling for array comparisons diff --git a/dmd2/expression.h b/dmd2/expression.h index 37c25e1d..06c6447f 100644 --- a/dmd2/expression.h +++ b/dmd2/expression.h @@ -1135,6 +1135,7 @@ struct CallExp : UnaExp CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2); Expression *syntaxCopy(); + Expression *resolveUFCS(Scope *sc); Expression *semantic(Scope *sc); Expression *optimize(int result); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); @@ -1402,6 +1403,8 @@ struct ArrayLengthExp : UnaExp struct ArrayExp : UnaExp { Expressions *arguments; // Array of Expression's + size_t currentDimension; // for opDollar + VarDeclaration *lengthVar; ArrayExp(Loc loc, Expression *e1, Expressions *arguments); Expression *syntaxCopy(); diff --git a/dmd2/func.c b/dmd2/func.c index fd768e3c..1100a95d 100644 --- a/dmd2/func.c +++ b/dmd2/func.c @@ -423,7 +423,7 @@ void FuncDeclaration::semantic(Scope *sc) // ctor = (CtorDeclaration *)this; // if (!cd->ctor) // cd->ctor = ctor; - return; + goto Ldone; } #if 0 @@ -923,7 +923,8 @@ void FuncDeclaration::semantic3(Scope *sc) sc2->sw = NULL; sc2->fes = fes; sc2->linkage = LINKd; - sc2->stc &= ~(STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | + sc2->stc &= ~(STCauto | STCscope | STCstatic | STCabstract | + STCdeprecated | STCoverride | STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | STCproperty | STCsafe | STCtrusted | STCsystem); sc2->protection = PROTpublic; @@ -941,76 +942,16 @@ void FuncDeclaration::semantic3(Scope *sc) // Declare 'this' AggregateDeclaration *ad = isThis(); if (ad) - { VarDeclaration *v; - + { if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof) { error("function literals cannot be class members"); return; } else - { assert(!isNested() || sc->intypeof); // can't be both member and nested - assert(ad->handle); - Type *thandle = ad->handle; -#if STRUCTTHISREF - thandle = thandle->addMod(type->mod); - thandle = thandle->addStorageClass(storage_class); - //if (isPure()) - //thandle = thandle->addMod(MODconst); -#else - if (storage_class & STCconst || type->isConst()) - { - assert(0); // BUG: shared not handled - if (thandle->ty == Tclass) - thandle = thandle->constOf(); - else - { assert(thandle->ty == Tpointer); - thandle = thandle->nextOf()->constOf()->pointerTo(); - } - } - else if (storage_class & STCimmutable || type->isImmutable()) - { - if (thandle->ty == Tclass) - thandle = thandle->invariantOf(); - else - { assert(thandle->ty == Tpointer); - thandle = thandle->nextOf()->invariantOf()->pointerTo(); - } - } - else if (storage_class & STCshared || type->isShared()) - { - assert(0); // not implemented - } -#endif - v = new ThisDeclaration(loc, thandle); - //v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle); - v->storage_class |= STCparameter; -#if STRUCTTHISREF - if (thandle->ty == Tstruct) - v->storage_class |= STCref; -#endif - v->semantic(sc2); - if (!sc2->insert(v)) - assert(0); - v->parent = this; - vthis = v; - } - } - else if (isNested()) - { - /* The 'this' for a nested function is the link to the - * enclosing function's stack frame. - * Note that nested functions and member functions are disjoint. - */ - VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo()); - v->storage_class |= STCparameter; - v->semantic(sc2); - if (!sc2->insert(v)) - assert(0); - v->parent = this; - vthis = v; } + vthis = declareThis(sc2, ad); // Declare hidden variable _arguments[] and _argptr if (f->varargs == 1) @@ -1363,7 +1304,7 @@ void FuncDeclaration::semantic3(Scope *sc) if (!type->nextOf()) { ((TypeFunction *)type)->next = Type::tvoid; - type = type->semantic(loc, sc); + //type = type->semantic(loc, sc); // Removed with 6902 } f = (TypeFunction *)type; } @@ -1835,6 +1776,12 @@ void FuncDeclaration::semantic3(Scope *sc) f->trust = TRUSTsafe; } + // Do semantic type AFTER pure/nothrow inference. + if (inferRetType) + { + type = type->semantic(loc, sc); + } + if (global.gag && global.errors != nerrors) semanticRun = PASSsemanticdone; // Ensure errors get reported again else @@ -1852,6 +1799,76 @@ void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) bodyToCBuffer(buf, hgs); } +VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad) +{ + if (ad) + { VarDeclaration *v; + + { + assert(ad->handle); + Type *thandle = ad->handle; +#if STRUCTTHISREF + thandle = thandle->addMod(type->mod); + thandle = thandle->addStorageClass(storage_class); + //if (isPure()) + //thandle = thandle->addMod(MODconst); +#else + if (storage_class & STCconst || type->isConst()) + { + assert(0); // BUG: shared not handled + if (thandle->ty == Tclass) + thandle = thandle->constOf(); + else + { assert(thandle->ty == Tpointer); + thandle = thandle->nextOf()->constOf()->pointerTo(); + } + } + else if (storage_class & STCimmutable || type->isImmutable()) + { + if (thandle->ty == Tclass) + thandle = thandle->invariantOf(); + else + { assert(thandle->ty == Tpointer); + thandle = thandle->nextOf()->invariantOf()->pointerTo(); + } + } + else if (storage_class & STCshared || type->isShared()) + { + assert(0); // not implemented + } +#endif + v = new ThisDeclaration(loc, thandle); + //v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle); + v->storage_class |= STCparameter; +#if STRUCTTHISREF + if (thandle->ty == Tstruct) + v->storage_class |= STCref; +#endif + v->semantic(sc); + if (!sc->insert(v)) + assert(0); + v->parent = this; + return v; + } + } + else if (isNested()) + { + /* The 'this' for a nested function is the link to the + * enclosing function's stack frame. + * Note that nested functions and member functions are disjoint. + */ + VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo()); + v->storage_class |= STCparameter; + v->semantic(sc); + if (!sc->insert(v)) + assert(0); + v->parent = this; + return v; + } + + return NULL; +} + int FuncDeclaration::equals(Object *o) { if (this == o) @@ -3313,8 +3330,8 @@ int CtorDeclaration::addPostInvariant() /********************************* PostBlitDeclaration ****************************/ #if DMDV2 -PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc) - : FuncDeclaration(loc, endloc, Id::_postblit, STCundefined, NULL) +PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc) + : FuncDeclaration(loc, endloc, Id::_postblit, stc, NULL) { } @@ -3347,7 +3364,7 @@ void PostBlitDeclaration::semantic(Scope *sc) ad->postblits.push(this); if (!type) - type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); + type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class); sc = sc->push(); sc->stc &= ~STCstatic; // not static @@ -3557,6 +3574,11 @@ int StaticCtorDeclaration::isVirtual() return FALSE; } +bool StaticCtorDeclaration::hasStaticCtorOrDtor() +{ + return TRUE; +} + int StaticCtorDeclaration::addPreInvariant() { return FALSE; @@ -3684,6 +3706,11 @@ int StaticDtorDeclaration::isVirtual() return FALSE; } +bool StaticDtorDeclaration::hasStaticCtorOrDtor() +{ + return TRUE; +} + int StaticDtorDeclaration::addPreInvariant() { return FALSE; diff --git a/dmd2/idgen.c b/dmd2/idgen.c index 78b50226..10890311 100644 --- a/dmd2/idgen.c +++ b/dmd2/idgen.c @@ -66,6 +66,7 @@ Msgtable msgtable[] = { "Exception" }, { "AssociativeArray" }, { "Throwable" }, + { "Error" }, { "withSym", "__withSym" }, { "result", "__result" }, { "returnLabel", "__returnLabel" }, @@ -215,6 +216,7 @@ Msgtable msgtable[] = { "opStar" }, { "opDot" }, { "opDispatch" }, + { "opDollar" }, { "opUnary" }, { "opIndexUnary" }, { "opSliceUnary" }, diff --git a/dmd2/interpret.c b/dmd2/interpret.c index d298ce14..dffb7e36 100644 --- a/dmd2/interpret.c +++ b/dmd2/interpret.c @@ -24,6 +24,7 @@ #include "aggregate.h" #include "id.h" #include "utf.h" +#include "attrib.h" // for AttribDeclaration #include "template.h" TemplateInstance *isSpeculativeFunction(FuncDeclaration *fd); @@ -31,15 +32,130 @@ TemplateInstance *isSpeculativeFunction(FuncDeclaration *fd); #define LOG 0 #define LOGASSIGN 0 +#define SHOWPERFORMANCE 0 // Maximum allowable recursive function calls in CTFE #define CTFE_RECURSION_LIMIT 1000 +// The values of all CTFE variables. +struct CtfeStack +{ +private: + /* The stack. Every declaration we encounter is pushed here, + together with the VarDeclaration, and the previous + stack address of that variable, so that we can restore it + when we leave the stack frame. + Ctfe Stack addresses are just 0-based integers, but we save + them as 'void *' because ArrayBase can only do pointers. + */ + Expressions values; // values on the stack + VarDeclarations vars; // corresponding variables + ArrayBase savedId; // id of the previous state of that var + + /* Global constants get saved here after evaluation, so we never + * have to redo them. This saves a lot of time and memory. + */ + Expressions globalValues; // values of global constants + size_t framepointer; // current frame pointer + size_t maxStackPointer; // most stack we've ever used +public: + CtfeStack() : framepointer(0) + { + } + size_t stackPointer() + { + return values.dim; + } + // Largest number of stack positions we've used + size_t maxStackUsage() + { + return maxStackPointer; + } + // return the previous frame + size_t startFrame() + { + size_t oldframe = framepointer; + framepointer = stackPointer(); + return oldframe; + } + void endFrame(size_t oldframe) + { + popAll(framepointer); + framepointer = oldframe; + } + Expression *getValue(VarDeclaration *v) + { + if (v->isDataseg() && !v->isCTFE()) + { + assert(v->ctfeAdrOnStack >= 0 && + v->ctfeAdrOnStack < globalValues.dim); + return globalValues.tdata()[v->ctfeAdrOnStack]; + } + assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); + return values.tdata()[v->ctfeAdrOnStack]; + } + void setValue(VarDeclaration *v, Expression *e) + { + assert(!v->isDataseg() || v->isCTFE()); + assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); + values.tdata()[v->ctfeAdrOnStack] = e; + } + void push(VarDeclaration *v) + { + assert(!v->isDataseg() || v->isCTFE()); + if (v->ctfeAdrOnStack!= (size_t)-1 + && v->ctfeAdrOnStack >= framepointer) + { // Already exists in this frame, reuse it. + values.tdata()[v->ctfeAdrOnStack] = NULL; + return; + } + savedId.push((void *)(v->ctfeAdrOnStack)); + v->ctfeAdrOnStack = values.dim; + vars.push(v); + values.push(NULL); + } + void pop(VarDeclaration *v) + { + assert(!v->isDataseg() || v->isCTFE()); + int oldid = v->ctfeAdrOnStack; + v->ctfeAdrOnStack = (size_t)(savedId.tdata()[oldid]); + if (v->ctfeAdrOnStack == values.dim - 1) + { + values.pop(); + vars.pop(); + savedId.pop(); + } + } + void popAll(size_t stackpointer) + { + if (stackPointer() > maxStackPointer) + maxStackPointer = stackPointer(); + assert(values.dim >= stackpointer && stackpointer >= 0); + for (size_t i = stackpointer; i < values.dim; ++i) + { + VarDeclaration *v = vars.tdata()[i]; + v->ctfeAdrOnStack = (size_t)(savedId.tdata()[i]); + } + values.setDim(stackpointer); + vars.setDim(stackpointer); + savedId.setDim(stackpointer); + } + void saveGlobalConstant(VarDeclaration *v, Expression *e) + { + assert(v->isDataseg() && !v->isCTFE()); + v->ctfeAdrOnStack = globalValues.dim; + globalValues.push(e); + } +}; + +CtfeStack ctfeStack; + + struct InterState { InterState *caller; // calling function's InterState FuncDeclaration *fd; // function being interpreted - VarDeclarations vars; // variables used in this function + size_t framepointer; // frame pointer of previous frame Statement *start; // if !=NULL, start execution at this statement Statement *gotoTarget; /* target of EXP_GOTO_INTERPRET result; also * target of labelled EXP_BREAK_INTERPRET or @@ -63,15 +179,31 @@ struct CtfeStatus static int stackTraceCallsToSuppress; /* When printing a stack trace, * suppress this number of calls */ + static int maxCallDepth; // highest number of recursive calls + static int numArrayAllocs; // Number of allocated arrays + static int numAssignments; // total number of assignments executed }; int CtfeStatus::callDepth = 0; int CtfeStatus::stackTraceCallsToSuppress = 0; +int CtfeStatus::maxCallDepth = 0; +int CtfeStatus::numArrayAllocs = 0; +int CtfeStatus::numAssignments = 0; + +// CTFE diagnostic information +void printCtfePerformanceStats() +{ +#if SHOWPERFORMANCE + printf(" ---- CTFE Performance ----\n"); + printf("max call depth = %d\tmax stack = %d\n", CtfeStatus::maxCallDepth, ctfeStack.maxStackUsage()); + printf("array allocs = %d\tassignments = %d\n\n", CtfeStatus::numArrayAllocs, CtfeStatus::numAssignments); +#endif +} + Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference = NULL); Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); VarDeclaration *findParentVar(Expression *e, Expression *thisval); -void addVarToInterstate(InterState *istate, VarDeclaration *v); bool needToCopyLiteral(Expression *expr); Expression *copyLiteral(Expression *e); Expression *paintTypeOntoLiteral(Type *type, Expression *lit); @@ -79,6 +211,97 @@ Expression *findKeyInAA(AssocArrayLiteralExp *ae, Expression *e2); Expression *evaluateIfBuiltin(InterState *istate, Loc loc, FuncDeclaration *fd, Expressions *arguments, Expression *pthis); +// CTFE only expressions +#define TOKclassreference ((TOK)(TOKMAX+1)) +#define TOKthrownexception ((TOK)(TOKMAX+2)) + +// Reference to a class, or an interface. We need this when we +// point to a base class (we must record what the type is). +struct ClassReferenceExp : Expression +{ + StructLiteralExp *value; + ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type) : Expression(loc, TOKclassreference, sizeof(ClassReferenceExp)) + { + assert(lit && lit->sd && lit->sd->isClassDeclaration()); + this->value = lit; + this->type = type; + } + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue) + { + //printf("ClassReferenceExp::interpret() %s\n", value->toChars()); + return this; + } + char *toChars() + { + return value->toChars(); + } + ClassDeclaration *originalClass() + { + return value->sd->isClassDeclaration(); + } + // Return index of the field, or -1 if not found + int getFieldIndex(Type *fieldtype, size_t fieldoffset) + { + ClassDeclaration *cd = originalClass(); + size_t fieldsSoFar = 0; + for (size_t j = 0; j < value->elements->dim; j++) + { while (j - fieldsSoFar >= cd->fields.dim) + { fieldsSoFar += cd->fields.dim; + cd = cd->baseClass; + } + Dsymbol *s = cd->fields.tdata()[j - fieldsSoFar]; + VarDeclaration *v2 = s->isVarDeclaration(); + if (fieldoffset == v2->offset && + fieldtype->size() == v2->type->size()) + { return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar); + } + } + return -1; + } +}; + +// Fake class which holds the thrown exception. Used for implementing exception handling. +struct ThrownExceptionExp : Expression +{ + ClassReferenceExp *thrown; // the thing being tossed + ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Expression(loc, TOKthrownexception, sizeof(ThrownExceptionExp)) + { + this->thrown = victim; + this->type = type; + } + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue) + { + assert(0); // This should never be interpreted + return this; + } + char *toChars() + { + return "CTFE ThrownException"; + } + // Generate an error message when this exception is not caught + void generateUncaughtError() + { + thrown->error("Uncaught CTFE exception %s(%s)", thrown->type->toChars(), + thrown->value->elements->tdata()[0]->toChars()); + /* Also give the line where the throw statement was. We won't have it + * in the case where the ThrowStatement is generated internally + * (eg, in ScopeStatement) + */ + if (loc.filename && !loc.equals(thrown->loc)) + fprintf(stdmsg, "%s: thrown from here\n", loc.toChars()); + } +}; + +// True if 'e' is EXP_CANT_INTERPRET, or an exception +bool exceptionOrCantInterpret(Expression *e) +{ + if (e == EXP_CANT_INTERPRET) return true; + if (!e || e == EXP_GOTO_INTERPRET || e == EXP_VOID_INTERPRET + || e == EXP_BREAK_INTERPRET || e == EXP_CONTINUE_INTERPRET) + return false; + return e->op == TOKthrownexception; +} + // Used for debugging only void showCtfeExpr(Expression *e, int level = 0) @@ -87,11 +310,17 @@ void showCtfeExpr(Expression *e, int level = 0) Expressions *elements = NULL; // We need the struct definition to detect block assignment StructDeclaration *sd = NULL; - if (e->op == TOKstructliteral) { - elements = ((StructLiteralExp *)e)->elements; + ClassDeclaration *cd = NULL; + if (e->op == TOKstructliteral) + { elements = ((StructLiteralExp *)e)->elements; sd = ((StructLiteralExp *)e)->sd; printf("STRUCT type = %s %p :\n", e->type->toChars(), e); } + else if (e->op == TOKclassreference) + { elements = ((ClassReferenceExp *)e)->value->elements; + cd = ((ClassReferenceExp *)e)->originalClass(); + printf("CLASS type = %s %p :\n", e->type->toChars(), ((ClassReferenceExp *)e)->value->toChars()); + } else if (e->op == TOKarrayliteral) { elements = ((ArrayLiteralExp *)e)->elements; @@ -121,19 +350,38 @@ void showCtfeExpr(Expression *e, int level = 0) if (elements) { + size_t fieldsSoFar = 0; for (size_t i = 0; i < elements->dim; i++) - { Expression *z = elements->tdata()[i]; + { Expression *z = NULL; + Dsymbol *s = NULL; if (sd) + { s = sd->fields.tdata()[i]; + z = elements->tdata()[i]; + } + else if (cd) + { while (i - fieldsSoFar >= cd->fields.dim) + { fieldsSoFar += cd->fields.dim; + cd = cd->baseClass; + for (int j = level; j>0; --j) printf(" "); + printf(" BASE CLASS: %s\n", cd->toChars()); + } + s = cd->fields.tdata()[i - fieldsSoFar]; + size_t indx = (elements->dim - fieldsSoFar)- cd->fields.dim + i; + assert(indx >= 0); + assert(indx < elements->dim); + z = elements->tdata()[indx]; + } + if (!z) { + for (int j = level; j>0; --j) printf(" "); + printf(" void\n"); + continue; + } + + if (s) { - Dsymbol *s = sd->fields.tdata()[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v); // If it is a void assignment, use the default initializer - if (!z) { - for (int j = level; j>0; --j) printf(" "); - printf(" field:void\n"); - continue; - } if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray) { for (int j = level; --j;) printf(" "); @@ -207,6 +455,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument istatex.caller = istate; istatex.fd = this; istatex.localThis = thisarg; + istatex.framepointer = ctfeStack.startFrame(); Expressions vsave; // place to save previous parameter values size_t dim = 0; @@ -266,6 +515,13 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument if (earg == EXP_CANT_INTERPRET) return NULL; } + if (earg->op == TOKthrownexception) + { + if (istate) + return earg; + ((ThrownExceptionExp *)earg)->generateUncaughtError(); + return NULL; + } eargs.tdata()[i] = earg; } @@ -273,7 +529,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument { Expression *earg = eargs.tdata()[i]; Parameter *arg = Parameter::getNth(tf->parameters, i); VarDeclaration *v = parameters->tdata()[i]; - vsave.tdata()[i] = v->getValue(); + ctfeStack.push(v); #if LOG printf("arg[%d] = %s\n", i, earg->toChars()); #endif @@ -287,15 +543,6 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument return NULL; } v->setValueWithoutChecking(earg); - /* Don't restore the value of v2 upon function return - */ - for (size_t j = 0; j < (istate ? istate->vars.dim : 0); j++) - { VarDeclaration *vx = istate->vars.tdata()[j]; - if (vx == v2) - { istate->vars.tdata()[j] = NULL; - break; - } - } } else { // Value parameters and non-trivial references @@ -307,45 +554,14 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument #endif } } - // Don't restore the value of 'this' upon function return - if (needThis() && istate) - { - VarDeclaration *thisvar = findParentVar(thisarg, istate->localThis); - if (!thisvar) // it's a reference. Find which variable it refers to. - thisvar = findParentVar(thisarg->interpret(istate), istate->localThis); - for (size_t i = 0; i < istate->vars.dim; i++) - { VarDeclaration *v = istate->vars.tdata()[i]; - if (v == thisvar) - { istate->vars.tdata()[i] = NULL; - break; - } - } - } - /* Save the values of the local variables used - */ - Expressions valueSaves; - if (istate) - { - //printf("saving local variables...\n"); - valueSaves.setDim(istate->vars.dim); - for (size_t i = 0; i < istate->vars.dim; i++) - { VarDeclaration *v = istate->vars.tdata()[i]; - bool isParentVar = false; - /* Nested functions only restore their own local variables - * (not variables in the parent function) - */ - if (v && (!isNested() || v->parent == this)) - { - //printf("\tsaving [%d] %s = %s\n", i, v->toChars(), v->getValue() ? v->getValue()->toChars() : ""); - valueSaves.tdata()[i] = v->getValue(); - v->setValueNull(); - } - } - } + if (vresult) + ctfeStack.push(vresult); // Enter the function ++CtfeStatus::callDepth; + if (CtfeStatus::callDepth > CtfeStatus::maxCallDepth) + CtfeStatus::maxCallDepth = CtfeStatus::callDepth; Expression *e = NULL; while (1) @@ -385,35 +601,14 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument // Leave the function --CtfeStatus::callDepth; - /* Restore the parameter values - */ - for (size_t i = 0; i < dim; i++) - { - VarDeclaration *v = parameters->tdata()[i]; - v->setValueWithoutChecking(vsave.tdata()[i]); - } - /* Clear __result. (Bug 6049). - */ - if (vresult) - vresult->setValueNull(); + ctfeStack.endFrame(istatex.framepointer); + if (e == EXP_CANT_INTERPRET || !exceptionOrCantInterpret(e)) + return e; if (istate) - { - /* Restore the variable values - */ - //printf("restoring local variables...\n"); - for (size_t i = 0; i < istate->vars.dim; i++) - { VarDeclaration *v = istate->vars.tdata()[i]; - /* Nested functions only restore their own local variables - * (not variables in the parent function) - */ - if (v && (!isNested() || v->parent == this)) - { v->setValueWithoutChecking(valueSaves.tdata()[i]); - //printf("\trestoring [%d] %s = %s\n", i, v->toChars(), v->getValue() ? v->getValue()->toChars() : ""); - } - } - } - return e; + return e; + ((ThrownExceptionExp *)e)->generateUncaughtError(); + return NULL; } /******************************** Statement ***************************/ @@ -430,7 +625,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument * Returns: * NULL continue to next statement * EXP_CANT_INTERPRET cannot interpret statement at compile time - * !NULL expression from return statement + * !NULL expression from return statement, or thrown exception */ Expression *Statement::interpret(InterState *istate) @@ -457,6 +652,8 @@ Expression *ExpStatement::interpret(InterState *istate) //printf("-ExpStatement::interpret(): %p\n", e); return EXP_CANT_INTERPRET; } + if (e && e!= EXP_VOID_INTERPRET && e->op == TOKthrownexception) + return e; } return NULL; } @@ -547,6 +744,8 @@ Expression *IfStatement::interpret(InterState *istate) Expression *e = NULL; if (ifbody) e = ifbody->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; if (istate->start && elsebody) e = elsebody->interpret(istate); return e; @@ -555,7 +754,7 @@ Expression *IfStatement::interpret(InterState *istate) Expression *e = condition->interpret(istate); assert(e); //if (e == EXP_CANT_INTERPRET) printf("cannot interpret\n"); - if (e != EXP_CANT_INTERPRET) + if (e != EXP_CANT_INTERPRET && (e && e->op != TOKthrownexception)) { if (isTrueBool(e)) e = ifbody ? ifbody->interpret(istate) : NULL; @@ -627,14 +826,19 @@ Expression *ctfeEqual(enum TOK op, Type *type, Expression *e1, Expression *e2) } -void scrubArray(Expressions *elems); +void scrubArray(Loc loc, Expressions *elems); /* All results destined for use outside of CTFE need to have their CTFE-specific * features removed. * In particular, all slices must be resolved. */ -Expression *scrubReturnValue(Expression *e) +Expression *scrubReturnValue(Loc loc, Expression *e) { + if (e->op == TOKclassreference) + { + error(loc, "%s class literals cannot be returned from CTFE", ((ClassReferenceExp*)e)->originalClass()->toChars()); + return EXP_CANT_INTERPRET; + } if (e->op == TOKslice) { e = resolveSlice(e); @@ -642,30 +846,30 @@ Expression *scrubReturnValue(Expression *e) if (e->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)e; - scrubArray(se->elements); + scrubArray(loc, se->elements); } if (e->op == TOKarrayliteral) { - scrubArray(((ArrayLiteralExp *)e)->elements); + scrubArray(loc, ((ArrayLiteralExp *)e)->elements); } if (e->op == TOKassocarrayliteral) { AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; - scrubArray(aae->keys); - scrubArray(aae->values); + scrubArray(loc, aae->keys); + scrubArray(loc, aae->values); } return e; } // Scrub all members of an array -void scrubArray(Expressions *elems) +void scrubArray(Loc loc, Expressions *elems) { for (size_t i = 0; i < elems->dim; i++) { Expression *m = elems->tdata()[i]; if (!m) continue; - m = scrubReturnValue(m); + m = scrubReturnValue(loc, m); elems->tdata()[i] = m; } } @@ -710,11 +914,11 @@ Expression *ReturnStatement::interpret(InterState *istate) e = exp->interpret(istate, ctfeNeedLvalue); else e = exp->interpret(istate); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; if (!istate->caller) { - e = scrubReturnValue(e); + e = scrubReturnValue(loc, e); if (e == EXP_CANT_INTERPRET) return e; } @@ -839,7 +1043,7 @@ Expression *DoStatement::interpret(InterState *istate) Lcontinue: istate->gotoTarget = NULL; e = condition->interpret(istate); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) break; if (!e->isConst()) { e = EXP_CANT_INTERPRET; @@ -870,7 +1074,7 @@ Expression *ForStatement::interpret(InterState *istate) if (init) { e = init->interpret(istate); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; assert(!e); } @@ -907,7 +1111,7 @@ Expression *ForStatement::interpret(InterState *istate) if (!condition) goto Lhead; e = condition->interpret(istate); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) break; if (!e->isConst()) { e = EXP_CANT_INTERPRET; @@ -953,223 +1157,15 @@ Expression *ForStatement::interpret(InterState *istate) Expression *ForeachStatement::interpret(InterState *istate) { -#if 1 assert(0); // rewritten to ForStatement return NULL; -#else -#if LOG - printf("ForeachStatement::interpret()\n"); -#endif - if (istate->start == this) - istate->start = NULL; - if (istate->start) - return NULL; - - Expression *e = NULL; - Expression *eaggr; - - if (value->isOut() || value->isRef()) - return EXP_CANT_INTERPRET; - - eaggr = aggr->interpret(istate); - if (eaggr == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - - Expression *dim = ArrayLength(Type::tsize_t, eaggr); - if (dim == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - - Expression *keysave = key ? key->value : NULL; - Expression *valuesave = value->value; - - uinteger_t d = dim->toUInteger(); - uinteger_t index; - - if (op == TOKforeach) - { - for (index = 0; index < d; index++) - { - Expression *ekey = new IntegerExp(loc, index, Type::tsize_t); - if (key) - key->value = ekey; - e = Index(value->type, eaggr, ekey); - if (e == EXP_CANT_INTERPRET) - break; - value->value = e; - - e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - break; - if (e == EXP_BREAK_INTERPRET) - { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; - } - if (e == EXP_CONTINUE_INTERPRET) - { - if (istate->gotoTarget && istate->gotoTarget != this) - break; // continue at higher level - istate->gotoTarget = NULL; - e = NULL; - } - else if (e) - break; - } - } - else // TOKforeach_reverse - { - for (index = d; index-- != 0;) - { - Expression *ekey = new IntegerExp(loc, index, Type::tsize_t); - if (key) - key->value = ekey; - e = Index(value->type, eaggr, ekey); - if (e == EXP_CANT_INTERPRET) - break; - value->value = e; - - e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - break; - if (e == EXP_BREAK_INTERPRET) - { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; - } - if (e == EXP_CONTINUE_INTERPRET) - { - if (istate->gotoTarget && istate->gotoTarget != this) - break; // continue at higher level - istate->gotoTarget = NULL; - e = NULL; - } - else if (e) - break; - } - } - value->value = valuesave; - if (key) - key->value = keysave; - return e; -#endif } #if DMDV2 Expression *ForeachRangeStatement::interpret(InterState *istate) { -#if 1 assert(0); // rewritten to ForStatement return NULL; -#else -#if LOG - printf("ForeachRangeStatement::interpret()\n"); -#endif - if (istate->start == this) - istate->start = NULL; - if (istate->start) - return NULL; - - Expression *e = NULL; - Expression *elwr = lwr->interpret(istate); - if (elwr == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - - Expression *eupr = upr->interpret(istate); - if (eupr == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - - Expression *keysave = key->value; - - if (op == TOKforeach) - { - key->value = elwr; - - while (1) - { - e = Cmp(TOKlt, key->value->type, key->value, eupr); - if (e == EXP_CANT_INTERPRET) - break; - if (e->isBool(TRUE) == FALSE) - { e = NULL; - break; - } - - e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - break; - if (e == EXP_BREAK_INTERPRET) - { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; - } - if (e == EXP_CONTINUE_INTERPRET - && istate->gotoTarget && istate->gotoTarget != this) - break; // continue at higher level - if (e == NULL || e == EXP_CONTINUE_INTERPRET) - { e = Add(key->value->type, key->value, new IntegerExp(loc, 1, key->value->type)); - istate->gotoTarget = NULL; - if (e == EXP_CANT_INTERPRET) - break; - key->value = e; - } - else - break; - } - } - else // TOKforeach_reverse - { - key->value = eupr; - - do - { - e = Cmp(TOKgt, key->value->type, key->value, elwr); - if (e == EXP_CANT_INTERPRET) - break; - if (e->isBool(TRUE) == FALSE) - { e = NULL; - break; - } - - e = Min(key->value->type, key->value, new IntegerExp(loc, 1, key->value->type)); - if (e == EXP_CANT_INTERPRET) - break; - key->value = e; - - e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - break; - if (e == EXP_BREAK_INTERPRET) - { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; - } - if (e == EXP_CONTINUE_INTERPRET) - { - if (istate->gotoTarget && istate->gotoTarget != this) - break; // continue at higher level - istate->gotoTarget = NULL; - } - } while (e == NULL || e == EXP_CONTINUE_INTERPRET); - } - key->value = keysave; - return e; -#endif } #endif @@ -1202,8 +1198,8 @@ Expression *SwitchStatement::interpret(InterState *istate) Expression *econdition = condition->interpret(istate); - if (econdition == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(econdition)) + return econdition; if (econdition->op == TOKslice) econdition = resolveSlice(econdition); @@ -1214,8 +1210,8 @@ Expression *SwitchStatement::interpret(InterState *istate) { CaseStatement *cs = cases->tdata()[i]; e = ctfeEqual(TOKequal, Type::tint32, econdition, cs->exp); - if (e == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e)) + return e; if (e->isBool(TRUE)) { s = cs; break; @@ -1323,8 +1319,69 @@ Expression *TryCatchStatement::interpret(InterState *istate) printf("TryCatchStatement::interpret()\n"); #endif START() - error("try-catch statements are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; + Expression *e = body ? body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + return e; + if (!exceptionOrCantInterpret(e)) + return e; + // An exception was thrown + ThrownExceptionExp *ex = (ThrownExceptionExp *)e; + Type *extype = ex->thrown->originalClass()->type; + // Search for an appropriate catch clause. + for (size_t i = 0; i < catches->dim; i++) + { +#if DMDV1 + Catch *ca = (Catch *)catches->data[i]; +#else + Catch *ca = catches->tdata()[i]; +#endif + Type *catype = ca->type; + + if (catype->equals(extype) || catype->isBaseOf(extype, NULL)) + { // Execute the handler + if (ca->var) + { + ctfeStack.push(ca->var); + ca->var->createStackValue(ex->thrown); + } + return ca->handler->interpret(istate); + } + } + return e; +} + +bool isAnErrorException(ClassDeclaration *cd) +{ + return cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL); +} + +ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionExp *newest) +{ +#if LOG + printf("Collided exceptions %s %s\n", oldest->thrown->toChars(), newest->thrown->toChars()); +#endif +#if DMDV2 + // Little sanity check to make sure it's really a Throwable + ClassReferenceExp *boss = oldest->thrown; + assert(boss->value->elements->tdata()[4]->type->ty == Tclass); + ClassReferenceExp *collateral = newest->thrown; + if (isAnErrorException(collateral->originalClass()) + && !isAnErrorException(boss->originalClass())) + { // The new exception bypass the existing chain + assert(collateral->value->elements->tdata()[5]->type->ty == Tclass); + collateral->value->elements->tdata()[5] = boss; + return newest; + } + while (boss->value->elements->tdata()[4]->op == TOKclassreference) + { + boss = (ClassReferenceExp *)(boss->value->elements->tdata()[4]); + } + boss->value->elements->tdata()[4] = collateral; + return oldest; +#else + // for D1, the newest exception just clobbers the older one + return newest; +#endif } @@ -1334,8 +1391,20 @@ Expression *TryFinallyStatement::interpret(InterState *istate) printf("TryFinallyStatement::interpret()\n"); #endif START() - error("try-finally statements are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; + Expression *e = body ? body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + return e; + Expression *second = finalbody ? finalbody->interpret(istate) : NULL; + if (second == EXP_CANT_INTERPRET) + return second; + if (exceptionOrCantInterpret(second)) + { // Check for collided exceptions + if (exceptionOrCantInterpret(e)) + e = chainExceptions((ThrownExceptionExp *)e, (ThrownExceptionExp *)second); + else + e = second; + } + return e; } Expression *ThrowStatement::interpret(InterState *istate) @@ -1344,17 +1413,16 @@ Expression *ThrowStatement::interpret(InterState *istate) printf("ThrowStatement::interpret()\n"); #endif START() - error("throw statements are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; + Expression *e = exp->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + assert(e->op == TOKclassreference); + return new ThrownExceptionExp(loc, (ClassReferenceExp *)e); } Expression *OnScopeStatement::interpret(InterState *istate) { -#if LOG - printf("OnScopeStatement::interpret()\n"); -#endif - START() - error("scope guard statements are not yet supported in CTFE"); + assert(0); return EXP_CANT_INTERPRET; } @@ -1364,8 +1432,19 @@ Expression *WithStatement::interpret(InterState *istate) printf("WithStatement::interpret()\n"); #endif START() - error("with statements are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; + Expression *e = exp->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (wthis->type->ty == Tpointer && exp->type->ty != Tpointer) + { + e = new AddrExp(loc, e); + e->type = wthis->type; + } + ctfeStack.push(wthis); + wthis->createStackValue(e); + e = body ? body->interpret(istate) : EXP_VOID_INTERPRET; + ctfeStack.pop(wthis); + return e; } Expression *AsmStatement::interpret(InterState *istate) @@ -1404,6 +1483,8 @@ Expression *Expression::interpret(InterState *istate, CtfeGoal goal) Expression *ThisExp::interpret(InterState *istate, CtfeGoal goal) { + while (istate && !istate->localThis) + istate = istate->caller; if (istate && istate->localThis && istate->localThis->op == TOKstructliteral) return istate->localThis; if (istate && istate->localThis) @@ -1475,6 +1556,27 @@ Expression *FuncExp::interpret(InterState *istate, CtfeGoal goal) return this; } +/* Is it safe to convert from srcPointee* to destPointee* ? + * srcPointee is the genuine type (never void). + * destPointee may be void. + */ +bool isSafePointerCast(Type *srcPointee, Type*destPointee) +{ // It's OK if both are the same (modulo const) +#if DMDV2 + if (srcPointee->castMod(0) == destPointee->castMod(0)) + return true; +#else + if (srcPointee == destPointee) + return true; +#endif + // it's OK to cast to void* + if (destPointee->ty == Tvoid) + return true; + // It's OK if they are the same size integers, eg int* and uint* + return srcPointee->isintegral() && destPointee->isintegral() + && srcPointee->size() == destPointee->size(); +} + Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG @@ -1504,16 +1606,14 @@ Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) e->type = type; return e; } - if ( -#if DMDV2 - elemtype->castMod(0) != pointee->castMod(0) -#else - elemtype != pointee -#endif - // It's OK to cast from int[] to uint* - && !(elemtype->isintegral() && pointee->isintegral() - && elemtype->size() == pointee->size())) - { + if ( !isSafePointerCast(elemtype, pointee) ) + { // It's also OK to cast from &string to string*. + if ( offset == 0 && isSafePointerCast(var->type, pointee) ) + { + VarExp *ve = new VarExp(loc, var); + ve->type = type; + return ve; + } error("reinterpreting cast from %s to %s is not supported in CTFE", val->type->toChars(), type->toChars()); return EXP_CANT_INTERPRET; @@ -1540,24 +1640,11 @@ Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) return ie; } } - else if (offset == 0 && -#if DMDV2 - pointee->castMod(0) == var->type->castMod(0) -#else - pointee == var->type -#endif - ) + else if ( offset == 0 && isSafePointerCast(var->type, pointee) ) { - if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) - { - VarExp *ve = new VarExp(loc, var); - ve->type = type; - return ve; - } - Expression *e = getVarExp(loc, istate, var, goal); - e = new AddrExp(loc, e); - e->type = type; - return e; + VarExp *ve = new VarExp(loc, var); + ve->type = type; + return ve; } error("Cannot convert &%s to %s at compile time", var->type->toChars(), type->toChars()); @@ -1580,18 +1667,11 @@ Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) return EXP_CANT_INTERPRET; } Type *pointee = ((TypePointer *)type)->next; - if (pointee == ve->type) + if (isSafePointerCast(ve->var->type, pointee)) { - if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) { - ve = (VarExp*)ve->syntaxCopy(); - ve->type = type; - return ve; - } else { - Expression *e = getVarExp(loc, istate, ve->var, goal); - e = new AddrExp(loc, e); - e->type = type; - return e; - } + ve = new VarExp(loc, ve->var); + ve->type = type; + return ve; } } else if (e1->op == TOKindex) @@ -1629,8 +1709,7 @@ Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) TY tb = e1->type->toBasetype()->ty; bool needRef = (tb == Tarray || tb == Taarray || tb == Tclass); Expression *e = e1->interpret(istate, needRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); - - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; // Return a simplified address expression e = new AddrExp(loc, e); @@ -1671,6 +1750,8 @@ Expression * resolveReferences(Expression *e, Expression *thisval, bool *isRefer VarDeclaration *v = ve->var->isVarDeclaration(); if (v->type->ty == Tpointer) break; + if (v->ctfeAdrOnStack == (size_t)-1) // If not on the stack, can't possibly be a ref. + break; if (v && v->getValue() && v->getValue()->op == TOKvar) // it's probably a reference { // Make sure it's a real reference. @@ -1722,7 +1803,7 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal */ if (v->ident == Id::ctfe) return new IntegerExp(loc, 1, Type::tbool); - if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && v->init && !v->getValue()) + if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && v->init && !v->hasValue()) #else if (v->isConst() && v->init) #endif @@ -1735,7 +1816,7 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal v->inuse--; if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) fprintf(stdmsg, "%s: while evaluating %s.init\n", loc.toChars(), v->toChars()); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; e->type = v->type; } @@ -1748,10 +1829,13 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) fprintf(stdmsg, "%s: while evaluating %s.init\n", loc.toChars(), v->toChars()); } - if (e && e != EXP_CANT_INTERPRET) - v->setValueWithoutChecking(e); + if (e && e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) + if (v->isDataseg()) + ctfeStack.saveGlobalConstant(v, e); + else + v->setValueWithoutChecking(e); } - else if (v->isCTFE() && !v->getValue()) + else if (v->isCTFE() && !v->hasValue()) { if (v->init && v->type->size() != 0) { @@ -1771,14 +1855,14 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal return EXP_CANT_INTERPRET; } else - { e = v->getValue(); + { e = v->hasValue() ? v->getValue() : NULL; if (!e && !v->isCTFE() && v->isDataseg()) { error(loc, "static variable %s cannot be read at compile time", v->toChars()); e = EXP_CANT_INTERPRET; } else if (!e) error(loc, "variable %s is used before initialization", v->toChars()); - else if (e == EXP_CANT_INTERPRET) + else if (exceptionOrCantInterpret(e)) return e; else if ((goal == ctfeNeedLvalue) || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral @@ -1823,13 +1907,13 @@ Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) { // If it is a reference, return the thing it's pointing to. VarDeclaration *v = var->isVarDeclaration(); - if (v && v->getValue() && (v->storage_class & (STCref | STCout))) + if (v && v->hasValue() && (v->storage_class & (STCref | STCout))) return v->getValue(); if (v && !v->isDataseg() && !v->isCTFE() && !istate) { error("variable %s cannot be referenced at compile time", v->toChars()); return EXP_CANT_INTERPRET; } - else if (v && !v->getValue() && !v->isCTFE() && v->isDataseg()) + else if (v && !v->hasValue() && !v->isCTFE() && v->isDataseg()) { error("static variable %s cannot be referenced at compile time", v->toChars()); return EXP_CANT_INTERPRET; } @@ -1837,7 +1921,7 @@ Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) } Expression *e = getVarExp(loc, istate, var, goal); // A VarExp may include an implicit cast. It must be done explicitly. - if (e != EXP_CANT_INTERPRET) + if (e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) e = paintTypeOntoLiteral(type, e); return e; } @@ -1851,11 +1935,24 @@ Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) VarDeclaration *v = declaration->isVarDeclaration(); if (v) { - if (v->getValue()) - { - addVarToInterstate(istate, v); - v->setValueNull(); + if (v->toAlias()->isTupleDeclaration()) + { // Reserve stack space for all tuple members + TupleDeclaration *td =v->toAlias()->isTupleDeclaration(); + if (!td->objects) + return NULL; + for(int i= 0; i < td->objects->dim; ++i) + { + Object * o = td->objects->tdata()[i]; + Expression *ex = isExpression(o); + DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL; + VarDeclaration *v2 = s ? s->s->isVarDeclaration() : NULL; + assert(v2); + if (!v2->isDataseg() || v2->isCTFE()) + ctfeStack.push(v2); + } } + if (!v->isDataseg() || v->isCTFE()) + ctfeStack.push(v); Dsymbol *s = v->toAlias(); if (s == v && !v->isStatic() && v->init) { @@ -1887,16 +1984,24 @@ Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) } else if (s->isTupleDeclaration() && !v->init) e = NULL; + else if (v->isStatic() && !v->init) + e = NULL; // Just ignore static variables which aren't read or written yet else { - error("Declaration %s is not yet implemented in CTFE", toChars()); + error("Static variable %s cannot be modified at compile time", v->toChars()); e = EXP_CANT_INTERPRET; } } else if (declaration->isAttribDeclaration() || declaration->isTemplateMixin() || declaration->isTupleDeclaration()) - { // These can be made to work, too lazy now + { // Check for static struct declarations, which aren't executable + AttribDeclaration *ad = declaration->isAttribDeclaration(); + if (ad && ad->decl && ad->decl->dim == 1 + && ad->decl->tdata()[0]->isAggregateDeclaration()) + return NULL; // static struct declaration. Nothing to do. + + // These can be made to work, too lazy now error("Declaration %s is not yet implemented in CTFE", toChars()); e = EXP_CANT_INTERPRET; } @@ -1922,7 +2027,7 @@ Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) Expression *ex; ex = e->interpret(istate); - if (ex == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ex)) { delete expsx; return ex; } @@ -1942,6 +2047,7 @@ Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) { if (!expsx) { expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; expsx->setDim(exps->dim); for (size_t j = 0; j < i; j++) { @@ -1977,6 +2083,8 @@ Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) ex = e->interpret(istate); if (ex == EXP_CANT_INTERPRET) goto Lerror; + if (ex->op == TOKthrownexception) + return ex; /* If any changes, do Copy On Write */ @@ -1984,6 +2092,7 @@ Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) { if (!expsx) { expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; expsx->setDim(elements->dim); for (size_t j = 0; j < elements->dim; j++) { @@ -2033,6 +2142,9 @@ Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) ex = ekey->interpret(istate); if (ex == EXP_CANT_INTERPRET) goto Lerr; + if (ex->op == TOKthrownexception) + return ex; + /* If any changes, do Copy On Write */ @@ -2046,6 +2158,8 @@ Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) ex = evalue->interpret(istate); if (ex == EXP_CANT_INTERPRET) goto Lerr; + if (ex->op == TOKthrownexception) + return ex; /* If any changes, do Copy On Write */ @@ -2127,9 +2241,9 @@ Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) continue; Expression *ex = e->interpret(istate); - if (ex == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ex)) { delete expsx; - return EXP_CANT_INTERPRET; + return ex; } /* If any changes, do Copy On Write @@ -2138,6 +2252,7 @@ Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) { if (!expsx) { expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; expsx->setDim(elements->dim); for (size_t j = 0; j < elements->dim; j++) { @@ -2171,8 +2286,12 @@ ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Type *type, { Expressions *elements = new Expressions(); elements->setDim(dim); + bool mustCopy = needToCopyLiteral(elem); for (size_t i = 0; i < dim; i++) - elements->tdata()[i] = elem; + { if (mustCopy) + elem = copyLiteral(elem); + elements->tdata()[i] = elem; + } ArrayLiteralExp *ae = new ArrayLiteralExp(0, elements); ae->type = type; return ae; @@ -2210,16 +2329,15 @@ Expression *recursivelyCreateArrayLiteral(Type *newtype, InterState *istate, Expressions *arguments, int argnum) { Expression *lenExpr = ((arguments->tdata()[argnum]))->interpret(istate); - if (lenExpr == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(lenExpr)) + return lenExpr; size_t len = (size_t)(lenExpr->toInteger()); Type *elemType = ((TypeArray *)newtype)->next; - if (elemType->ty == Tarray) + if (elemType->ty == Tarray && argnum < arguments->dim - 1) { - assert(argnum < arguments->dim - 1); Expression *elem = recursivelyCreateArrayLiteral(elemType, istate, arguments, argnum + 1); - if (elem == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(elem)) return elem; Expressions *elements = new Expressions(); @@ -2271,10 +2389,50 @@ Expression *NewExp::interpret(InterState *istate, CtfeGoal goal) e->type = type; return e; } - if (newtype->ty == Tclass) + if (newtype->toBasetype()->ty == Tclass) { - error("classes are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; + ClassDeclaration *cd = ((TypeClass *)newtype->toBasetype())->sym; + size_t totalFieldCount = 0; + for (ClassDeclaration *c = cd; c; c = c->baseClass) + totalFieldCount += c->fields.dim; + Expressions *elems = new Expressions; + elems->setDim(totalFieldCount); + size_t fieldsSoFar = totalFieldCount; + for (ClassDeclaration *c = cd; c; c = c->baseClass) + { + fieldsSoFar -= c->fields.dim; + for (size_t i = 0; i < c->fields.dim; i++) + { + Dsymbol *s = c->fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + Expression *m = v->init ? v->init->toExpression() : v->type->defaultInitLiteral(); + if (exceptionOrCantInterpret(m)) + return m; + elems->tdata()[fieldsSoFar+i] = m; + } + } + // Hack: we store a ClassDeclaration instead of a StructDeclaration. + // We probably won't get away with this. + StructLiteralExp *se = new StructLiteralExp(loc, (StructDeclaration *)cd, elems, newtype); + Expression *e = new ClassReferenceExp(loc, se, type); + if (member) + { // Call constructor + if (!member->fbody) + { + Expression *ctorfail = evaluateIfBuiltin(istate, loc, member, arguments, e); + if (ctorfail && exceptionOrCantInterpret(ctorfail)) + return ctorfail; + if (ctorfail) + return e; + member->error("%s cannot be constructed at compile time, because the constructor has no available source code", newtype->toChars()); + return EXP_CANT_INTERPRET; + } + Expression * ctorfail = member->interpret(istate, arguments, e); + if (exceptionOrCantInterpret(ctorfail)) + return ctorfail; + } + return e; } error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; @@ -2288,13 +2446,10 @@ Expression *UnaExp::interpretCommon(InterState *istate, CtfeGoal goal, Expressi printf("UnaExp::interpretCommon() %s\n", toChars()); #endif e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e1)) + return e1; e = (*fp)(type, e1); return e; - -Lcant: - return EXP_CANT_INTERPRET; } #define UNA_INTERPRET(op) \ @@ -2365,6 +2520,11 @@ Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type, Expression *eptr, Expression *e2) { + if (eptr->type->nextOf()->ty == Tvoid) + { + error(loc, "cannot perform arithmetic on void* pointers at compile time"); + return EXP_CANT_INTERPRET; + } dinteger_t ofs1, ofs2; if (eptr->op == TOKaddress) eptr = ((AddrExp *)eptr)->e1; @@ -2430,25 +2590,31 @@ Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer && op == TOKmin) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; e2 = this->e2->interpret(istate, ctfeNeedLvalue); - if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e2)) + return e2; return pointerDifference(loc, type, e1, e2); } if (this->e1->type->ty == Tpointer && this->e2->type->isintegral()) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; e2 = this->e2->interpret(istate); - if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e2)) + return e2; return pointerArithmetic(loc, op, type, e1, e2); } if (this->e2->type->ty == Tpointer && this->e1->type->isintegral() && op==TOKadd) { - e2 = this->e2->interpret(istate, ctfeNeedLvalue); e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e2)) + return e1; return pointerArithmetic(loc, op, type, e2, e1); } if (this->e1->type->ty == Tpointer || this->e2->type->ty == Tpointer) @@ -2457,14 +2623,14 @@ Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) return EXP_CANT_INTERPRET; } e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->isConst() != 1) goto Lcant; e2 = this->e2->interpret(istate); - if (e2 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e2)) + return e2; if (e2->isConst() != 1) goto Lcant; @@ -2569,9 +2735,11 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + return e1; e2 = this->e2->interpret(istate, ctfeNeedLvalue); - if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e2)) + return e2; e = comparePointers(loc, op, type, e1, e2); if (e == EXP_CANT_INTERPRET) { @@ -2582,8 +2750,8 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp return e; } e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op == TOKslice) e1 = resolveSlice(e1); @@ -2598,8 +2766,8 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp } e2 = this->e2->interpret(istate); - if (e2 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e2)) + return e2; if (e2->op == TOKslice) e2 = resolveSlice(e2); if (e2->isConst() != 1 && @@ -2639,6 +2807,7 @@ BIN_INTERPRET2(Cmp) Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem) { Expressions *expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; expsx->setDim(oldelems->dim); for (size_t j = 0; j < expsx->dim; j++) { @@ -2650,26 +2819,6 @@ Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expre return expsx; } -/******************************** - * Add v to the istate list, unless it already exists there. - */ -void addVarToInterstate(InterState *istate, VarDeclaration *v) -{ - if (!v->isParameter()) - { - for (size_t i = 0; 1; i++) - { - if (i == istate->vars.dim) - { istate->vars.push(v); - //printf("\tadding %s to istate\n", v->toChars()); - break; - } - if (v == istate->vars.tdata()[i]) - break; - } - } -} - // Create a new struct literal, which is the same as se except that se.field[offset] = elem Expression * modifyStructField(Type *type, StructLiteralExp *se, size_t offset, Expression *newval) { @@ -2700,8 +2849,8 @@ Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae, Expressi { j--; Expression *ekey = aae->keys->tdata()[j]; Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, index); - if (ex == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(ex)) + return ex; if (ex->isBool(TRUE)) { valuesx->tdata()[j] = newval; updated = 1; @@ -2801,6 +2950,7 @@ Expressions *copyLiteralArray(Expressions *oldelems) { if (!oldelems) return oldelems; + CtfeStatus::numArrayAllocs++; Expressions *newelems = new Expressions(); newelems->setDim(oldelems->dim); for (size_t i = 0; i < oldelems->dim; i++) @@ -2857,7 +3007,7 @@ Expression *copyLiteral(Expression *e) { Expression *m = oldelems->tdata()[i]; // We need the struct definition to detect block assignment - StructDeclaration *sd = se->sd; + AggregateDeclaration *sd = se->sd; Dsymbol *s = sd->fields.tdata()[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v); @@ -2921,6 +3071,8 @@ Expression *copyLiteral(Expression *e) r->type = e->type; return r; } + else if (e->op == TOKclassreference) + return new ClassReferenceExp(e->loc, ((ClassReferenceExp *)e)->value, e->type); else { e->error("Internal Compiler Error: CTFE literal %s", e->toChars()); @@ -2972,6 +3124,30 @@ Expression *paintTypeOntoLiteral(Type *type, Expression *lit) return e; } + +Expression *ctfeCast(Loc loc, Type *type, Type *to, Expression *e) +{ + if (e->op == TOKnull) + return paintTypeOntoLiteral(to, e); + if (e->op == TOKclassreference) + { // Disallow reinterpreting class casts. Do this by ensuring that + // the original class can implicitly convert to the target class + ClassDeclaration *originalClass = ((ClassReferenceExp *)e)->originalClass(); + if (originalClass->type->implicitConvTo(to)) + return paintTypeOntoLiteral(to, e); + else + { + error(loc, "cannot reinterpret class from %s to %s at compile time", originalClass->toChars(), to->toChars()); + return EXP_CANT_INTERPRET; + } + } + Expression *r = Cast(type, to, e); + if (r == EXP_CANT_INTERPRET) + error(loc, "cannot cast %s to %s at compile time", r->toChars(), to->toChars()); + return r; +} + + /* Set a slice of char array literal 'existingAE' from a string 'newval'. * existingAE[firstIndex..firstIndex+newval.length] = newval. */ @@ -3136,6 +3312,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ error("value of %s is not known at compile time", e1->toChars()); return returnValue; } + ++CtfeStatus::numAssignments; /* Before we begin, we need to know if this is a reference assignment * (dynamic array, AA, or class) or a value assignment. * Determining this for slice assignments are tricky: we need to know @@ -3170,8 +3347,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } bool wantRef = false; if (!fp && this->e1->type->toBasetype() == this->e2->type->toBasetype() && - (e1->type->toBasetype()->ty == Tarray || e1->type->toBasetype()->ty == Taarray || - e1->type->toBasetype()->ty == Tclass) + (e1->type->toBasetype()->ty == Tarray || e1->type->toBasetype()->ty == Taarray) // e = *x is never a reference, because *x is always a value && this->e2->op != TOKstar ) @@ -3218,12 +3394,12 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (fp) { - if (e1->op == TOKcast) + while (e1->op == TOKcast) { CastExp *ce = (CastExp *)e1; e1 = ce->e1; } } - if (e1 == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e1)) return e1; // First, deal with this = e; and call() = e; @@ -3237,7 +3413,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ istate->awaitingLvalueReturn = true; e1 = e1->interpret(istate); istate->awaitingLvalueReturn = oldWaiting; - if (e1 == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e1)) return e1; if (e1->op == TOKarrayliteral || e1->op == TOKstring) { @@ -3252,8 +3428,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (e1->op == TOKstar) { e1 = e1->interpret(istate, ctfeNeedLvalue); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; if (!(e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex || e1->op == TOKslice)) { @@ -3265,9 +3441,10 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (!(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex || e1->op == TOKslice)) - printf("CTFE internal error: unsupported assignment %s\n", toChars()); - assert(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar - || e1->op == TOKindex || e1->op == TOKslice); + { + error("CTFE internal error: unsupported assignment %s", toChars()); + return EXP_CANT_INTERPRET; + } Expression * newval = NULL; @@ -3280,9 +3457,9 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ newval = this->e2->interpret(istate, ctfeNeedLvalue); else newval = this->e2->interpret(istate); + if (exceptionOrCantInterpret(newval)) + return newval; } - if (newval == EXP_CANT_INTERPRET) - return newval; // ---------------------------------------------------- // Deal with read-modify-write assignments. // Set 'newval' to the final assignment value @@ -3294,13 +3471,13 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ { // If it isn't a simple assignment, we need the existing value Expression * oldval = e1->interpret(istate); - if (oldval == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(oldval)) + return oldval; while (oldval->op == TOKvar) { oldval = resolveReferences(oldval, istate->localThis); oldval = oldval->interpret(istate); - if (oldval == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(oldval)) return oldval; } @@ -3323,9 +3500,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ op == TOKplusplus || op == TOKminusminus)) { oldval = this->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(oldval)) + return oldval; newval = this->e2->interpret(istate); - if (oldval == EXP_CANT_INTERPRET || newval == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(newval)) + return newval; newval = pointerArithmetic(loc, op, type, oldval, newval); } else if (this->e1->type->ty == Tpointer) @@ -3342,9 +3521,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } + if (exceptionOrCantInterpret(newval)) + return newval; // Determine the return value - returnValue = Cast(type, type, post ? oldval : newval); - if (returnValue == EXP_CANT_INTERPRET) + returnValue = ctfeCast(loc, type, type, post ? oldval : newval); + if (exceptionOrCantInterpret(returnValue)) return returnValue; } else @@ -3404,8 +3585,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (e1->op == TOKstar) { // arr.length+=n becomes (t=&arr, *(t).length=*(t).length+n); e1 = e1->interpret(istate, ctfeNeedLvalue); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; } } else @@ -3428,15 +3609,12 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ return EXP_CANT_INTERPRET; } } - newval = Cast(type, type, newval); - if (newval == EXP_CANT_INTERPRET) - { - error("CTFE error: cannot cast %s to type %s", this->e2->toChars(), type->toChars()); - return EXP_CANT_INTERPRET; - } + newval = ctfeCast(loc, type, type, newval); + if (exceptionOrCantInterpret(newval)) + return newval; returnValue = newval; } - if (newval == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(newval)) return newval; // ------------------------------------------------- @@ -3481,7 +3659,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (wantRef && !fp && this->e1->op != TOKarraylength) { newval = this->e2->interpret(istate, ctfeNeedLvalue); - if (newval == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(newval)) return newval; // If it is an assignment from a array function parameter passed by // reference, resolve the reference. (This should NOT happen for @@ -3525,15 +3703,15 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ // Get the AA to be modified. (We do an LvalueRef interpret, unless it // is a simple ref parameter -- in which case, we just want the value) aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (aggregate == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(aggregate)) + return aggregate; if (aggregate->op == TOKassocarrayliteral) { // Normal case, ultimate parent AA already exists // We need to walk from the deepest index up, checking that an AA literal // already exists on each level. Expression *index = ((IndexExp *)e1)->e2->interpret(istate); - if (index == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(index)) + return index; if (index->op == TOKslice) // only happens with AA assignment index = resolveSlice(index); AssocArrayLiteralExp *existingAA = (AssocArrayLiteralExp *)aggregate; @@ -3544,14 +3722,14 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ xe = (IndexExp *)xe->e1; Expression *indx = xe->e2->interpret(istate); - if (indx == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(indx)) + return indx; if (indx->op == TOKslice) // only happens with AA assignment indx = resolveSlice(indx); // Look up this index in it up in the existing AA, to get the next level of AA. AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(existingAA, indx); - if (newAA == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(newAA)) return newAA; if (!newAA) { // Doesn't exist yet, create an empty AA... @@ -3579,8 +3757,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) { Expression *index = ((IndexExp *)e1)->e2->interpret(istate); - if (index == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(index)) + return index; if (index->op == TOKslice) // only happens with AA assignment index = resolveSlice(index); Expressions *valuesx = new Expressions(); @@ -3609,7 +3787,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ // (in which case, we already have the lvalue). if (this->e1->op != TOKcall) e1 = e1->interpret(istate, ctfeNeedLvalue); - if (e1 == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e1)) return e1; if (e1->op == TOKstructliteral && newval->op == TOKstructliteral) { @@ -3632,8 +3810,6 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ { VarExp *ve = (VarExp *)e1; VarDeclaration *v = ve->var->isVarDeclaration(); - if (!destinationIsReference) - addVarToInterstate(istate, v); if (wantRef) { v->setValueNull(); @@ -3655,7 +3831,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } else { - if (e1->type->toBasetype()->ty == Tarray || e1->type->toBasetype()->ty == Taarray) + TY tyE1 = e1->type->toBasetype()->ty; + if (tyE1 == Tarray || tyE1 == Taarray) { // arr op= arr if (!v->getValue()) v->createRefValue(newval); @@ -3679,31 +3856,36 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (wantRef && exx->op != TOKstructliteral) { exx = exx->interpret(istate); - if (exx == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(exx)) return exx; } - if (exx->op != TOKstructliteral) + if (exx->op != TOKstructliteral && exx->op != TOKclassreference) { error("CTFE internal error: Dotvar assignment"); return EXP_CANT_INTERPRET; } - StructLiteralExp *se = (StructLiteralExp *)exx; VarDeclaration *member = ((DotVarExp *)e1)->var->isVarDeclaration(); if (!member) { error("CTFE internal error: Dotvar assignment"); return EXP_CANT_INTERPRET; } - int fieldi = se->getFieldIndex(member->type, member->offset); + StructLiteralExp *se = exx->op == TOKstructliteral + ? (StructLiteralExp *)exx + : ((ClassReferenceExp *)exx)->value; + int fieldi = exx->op == TOKstructliteral + ? se->getFieldIndex(member->type, member->offset) + : ((ClassReferenceExp *)exx)->getFieldIndex(member->type, member->offset); if (fieldi == -1) + { + error("CTFE internal error: cannot find field %s in %s", member->toChars(), exx->toChars()); return EXP_CANT_INTERPRET; + } assert(fieldi>=0 && fieldi < se->elements->dim); if (newval->op == TOKstructliteral) assignInPlace(se->elements->tdata()[fieldi], newval); else se->elements->tdata()[fieldi] = newval; - if (ultimateVar && !destinationIsReference) - addVarToInterstate(istate, ultimateVar); return returnValue; } else if (e1->op == TOKindex) @@ -3736,14 +3918,15 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (ie->lengthVar) { IntegerExp *dollarExp = new IntegerExp(loc, destarraylen, Type::tsize_t); + ctfeStack.push(ie->lengthVar); ie->lengthVar->createStackValue(dollarExp); } } Expression *index = ie->e2->interpret(istate); if (ie->lengthVar) - ie->lengthVar->setValueNull(); // $ is defined only inside [] - if (index == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + ctfeStack.pop(ie->lengthVar); // $ is defined only inside [] + if (exceptionOrCantInterpret(index)) + return index; assert (index->op != TOKslice); // only happens with AA assignment @@ -3758,8 +3941,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ { dinteger_t ofs; aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (aggregate == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(aggregate)) + return aggregate; if (aggregate->op == TOKnull) { error("cannot index through null pointer %s", ie->e1->toChars()); @@ -3791,8 +3974,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ { Expression *origagg = aggregate; aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (aggregate == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(aggregate)) + return aggregate; // The array could be an index of an AA. Resolve it if so. if (aggregate->op == TOKindex) { @@ -3902,6 +4085,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (sexp->lengthVar) { Expression *arraylen = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(sexp->lengthVar); sexp->lengthVar->createStackValue(arraylen); } @@ -3909,12 +4093,18 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ Expression *lower = NULL; if (sexp->upr) upper = sexp->upr->interpret(istate); + if (exceptionOrCantInterpret(upper)) + { + if (sexp->lengthVar) + ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] + return upper; + } if (sexp->lwr) lower = sexp->lwr->interpret(istate); if (sexp->lengthVar) - sexp->lengthVar->setValueNull(); // $ is defined only in [L..U] - if (upper == EXP_CANT_INTERPRET || lower == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] + if (exceptionOrCantInterpret(lower)) + return lower; size_t dim = dollar; size_t upperbound = upper ? upper->toInteger() : dim; @@ -3944,8 +4134,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ aggregate->op == TOKstar || aggregate->op == TOKcall) { aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (aggregate == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(aggregate)) + return aggregate; // The array could be an index of an AA. Resolve it if so. if (aggregate->op == TOKindex) { @@ -4176,26 +4366,40 @@ Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) printf("AndAndExp::interpret() %s\n", toChars()); #endif Expression *e = e1->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + + int result; if (e != EXP_CANT_INTERPRET) { if (e->isBool(FALSE)) - e = new IntegerExp(e1->loc, 0, type); + result = 0; else if (isTrueBool(e)) { e = e2->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; if (e != EXP_CANT_INTERPRET) { if (e->isBool(FALSE)) - e = new IntegerExp(e1->loc, 0, type); + result = 0; else if (isTrueBool(e)) - e = new IntegerExp(e1->loc, 1, type); + result = 1; else + { + error("%s does not evaluate to a boolean", e->toChars()); e = EXP_CANT_INTERPRET; + } } } else + { + error("%s cannot be interpreted as a boolean", e->toChars()); e = EXP_CANT_INTERPRET; + } } + if (e != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) + e = new IntegerExp(loc, result, type); return e; } @@ -4205,26 +4409,41 @@ Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) printf("OrOrExp::interpret() %s\n", toChars()); #endif Expression *e = e1->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + + int result; if (e != EXP_CANT_INTERPRET) { if (isTrueBool(e)) - e = new IntegerExp(e1->loc, 1, type); + result = 1; else if (e->isBool(FALSE)) { e = e2->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + if (e != EXP_CANT_INTERPRET) { if (e->isBool(FALSE)) - e = new IntegerExp(e1->loc, 0, type); + result = 0; else if (isTrueBool(e)) - e = new IntegerExp(e1->loc, 1, type); + result = 1; else + { + error("%s cannot be interpreted as a boolean", e->toChars()); e = EXP_CANT_INTERPRET; + } } } else + { + error("%s cannot be interpreted as a boolean", e->toChars()); e = EXP_CANT_INTERPRET; + } } + if (e != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) + e = new IntegerExp(loc, result, type); return e; } @@ -4289,7 +4508,7 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) if (ecall->op == TOKcall) { ecall = e1->interpret(istate); - if (ecall == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ecall)) return ecall; } if (ecall->op == TOKstar) @@ -4302,7 +4521,7 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) else { ecall = getVarExp(loc, istate, vd, goal); - if (ecall == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ecall)) return ecall; #if IN_LLVM @@ -4317,22 +4536,24 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) #endif } } + else if (pe->op == TOKsymoff) + fd = ((SymOffExp *)pe)->var->isFuncDeclaration(); else ecall = ((PtrExp*)ecall)->e1->interpret(istate); } - if (ecall == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ecall)) return ecall; if (ecall->op == TOKindex) { ecall = e1->interpret(istate); - if (ecall == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ecall)) return ecall; } if (ecall->op == TOKdotvar && !((DotVarExp*)ecall)->var->isFuncDeclaration()) { ecall = e1->interpret(istate); - if (ecall == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(ecall)) return ecall; } @@ -4386,13 +4607,43 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) { if (pthis->op == TOKcomma) pthis = pthis->interpret(istate); - if (pthis == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(pthis)) + return pthis; // Evaluate 'this' + Expression *oldpthis = pthis; if (pthis->op != TOKvar) pthis = pthis->interpret(istate, ctfeNeedLvalue); - if (pthis == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(pthis)) + return pthis; + if (fd->isVirtual()) + { // Make a virtual function call. + Expression *thisval = pthis; + if (pthis->op == TOKvar) + { assert(((VarExp*)thisval)->var->isVarDeclaration()); + thisval = ((VarExp*)thisval)->var->isVarDeclaration()->getValue(); + } + // Get the function from the vtable of the original class + ClassDeclaration *cd; + if (thisval && thisval->op == TOKnull) + { + error("function call through null class reference %s", pthis->toChars()); + return EXP_CANT_INTERPRET; + } + if (oldpthis->op == TOKsuper) + { assert(oldpthis->type->ty == Tclass); + cd = ((TypeClass *)oldpthis->type)->sym; + } + else + { + assert(thisval && thisval->op == TOKclassreference); + cd = ((ClassReferenceExp *)thisval)->originalClass(); + } + // We can't just use the vtable index to look it up, because + // vtables for interfaces don't get populated until the glue layer. + fd = cd->findFunc(fd->ident, (TypeFunction *)fd->type); + + assert(fd); + } } } // Check for built-in functions @@ -4405,6 +4656,8 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) { e = arguments->tdata()[1]; e = e->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; if (e != EXP_CANT_INTERPRET) { if (e->op == TOKslice) @@ -4447,7 +4700,13 @@ Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) // the variable to be created in, we need to create one now. InterState istateComma; if (!istate && firstComma->e1->op == TOKdeclaration) + { + assert(ctfeStack.stackPointer() == 0); + ctfeStack.startFrame(); istate = &istateComma; + } + + Expression *e = EXP_CANT_INTERPRET; // If the comma returns a temporary variable, it needs to be an lvalue // (this is particularly important for struct constructors) @@ -4457,19 +4716,23 @@ Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) { VarExp* ve = (VarExp *)e2; VarDeclaration *v = ve->var->isVarDeclaration(); + ctfeStack.push(v); if (!v->init && !v->getValue()) { v->createRefValue(copyLiteral(v->type->defaultInitLiteral())); } if (!v->getValue()) { Expression *newval = v->init->toExpression(); -// v->setRefValue(v->init->toExpression()); // Bug 4027. Copy constructors are a weird case where the // initializer is a void function (the variable is modified // through a reference parameter instead). newval = newval->interpret(istate); - if (newval == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(newval)) + { + if (istate == &istateComma) + ctfeStack.endFrame(0); + return newval; + } if (newval != EXP_VOID_INTERPRET) { // v isn't necessarily null. @@ -4477,12 +4740,19 @@ Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) } } if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) - return e2; - return e2->interpret(istate, goal); + e = e2; + else + e = e2->interpret(istate, goal); } - Expression *e = e1->interpret(istate, ctfeNeedNothing); - if (e != EXP_CANT_INTERPRET) - e = e2->interpret(istate, goal); + else + { + e = e1->interpret(istate, ctfeNeedNothing); + if (!exceptionOrCantInterpret(e)) + e = e2->interpret(istate, goal); + } + // If we created a temporary stack frame, end it now. + if (istate == &istateComma) + ctfeStack.endFrame(0); return e; } @@ -4495,25 +4765,24 @@ Expression *CondExp::interpret(InterState *istate, CtfeGoal goal) if (econd->type->ty == Tpointer && econd->type->nextOf()->ty != Tfunction) { e = econd->interpret(istate, ctfeNeedLvalue); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; if (e->op != TOKnull) e = new IntegerExp(loc, 1, Type::tbool); } else e = econd->interpret(istate); - if (e != EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) + return e; + if (isTrueBool(e)) + e = e1->interpret(istate, goal); + else if (e->isBool(FALSE)) + e = e2->interpret(istate, goal); + else { - if (isTrueBool(e)) - e = e1->interpret(istate, goal); - else if (e->isBool(FALSE)) - e = e2->interpret(istate, goal); - else - { - error("%s does not evaluate to boolean result at compile time", - econd->toChars()); - e = EXP_CANT_INTERPRET; - } + error("%s does not evaluate to boolean result at compile time", + econd->toChars()); + e = EXP_CANT_INTERPRET; } return e; } @@ -4527,8 +4796,8 @@ Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) #endif e1 = this->e1->interpret(istate); assert(e1); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice || e1->op == TOKassocarrayliteral || e1->op == TOKnull) { @@ -4581,12 +4850,11 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) { // Indexing a pointer. Note that there is no $ in this case. e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - + if (exceptionOrCantInterpret(e1)) + return e1; e2 = this->e2->interpret(istate); - if (e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e2)) + return e2; dinteger_t indx = e2->toInteger(); dinteger_t ofs; Expression *agg = getAggregateFromPointer(e1, &ofs); @@ -4607,8 +4875,8 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) return Index(type, agg, new IntegerExp(loc, indx+ofs, Type::tsize_t)); } e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op == TOKnull) { @@ -4625,14 +4893,15 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) { uinteger_t dollar = resolveArrayLength(e1); Expression *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(lengthVar); lengthVar->createStackValue(dollarExp); } e2 = this->e2->interpret(istate); if (lengthVar) - lengthVar->setValueNull(); // $ is defined only inside [] - if (e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + ctfeStack.pop(lengthVar); // $ is defined only inside [] + if (exceptionOrCantInterpret(e2)) + return e2; if (e1->op == TOKslice && e2->op == TOKint64) { // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] @@ -4670,7 +4939,7 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) e2->toChars(), this->e1->toChars()); return EXP_CANT_INTERPRET; } - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; assert(!e->checkSideEffect(2)); e = paintTypeOntoLiteral(type, e); @@ -4704,8 +4973,8 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) { // Slicing a pointer. Note that there is no $ in this case. e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op == TOKint64) { error("cannot slice invalid pointer %s of value %s", @@ -4716,11 +4985,11 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) /* Evaluate lower and upper bounds of slice */ lwr = this->lwr->interpret(istate); - if (lwr == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(lwr)) + return lwr; upr = this->upr->interpret(istate); - if (upr == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(upr)) + return upr; uinteger_t ilwr; uinteger_t iupr; ilwr = lwr->toInteger(); @@ -4756,8 +5025,8 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) e1 = this->e1; // Will get duplicated anyway else e1 = this->e1->interpret(istate, goal); - if (e1 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op == TOKvar) e1 = e1->interpret(istate); @@ -4780,20 +5049,24 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) if (lengthVar) { IntegerExp *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(lengthVar); lengthVar->createStackValue(dollarExp); } /* Evaluate lower and upper bounds of slice */ lwr = this->lwr->interpret(istate); - if (lwr != EXP_CANT_INTERPRET) - upr = this->upr->interpret(istate); - if (lengthVar) - lengthVar->setValueNull(); // $ is defined only inside [L..U] - if (lwr == EXP_CANT_INTERPRET || upr == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(lwr)) { - return EXP_CANT_INTERPRET; + if (lengthVar) + ctfeStack.pop(lengthVar);; // $ is defined only inside [L..U] + return lwr; } + upr = this->upr->interpret(istate); + if (lengthVar) + ctfeStack.pop(lengthVar); // $ is defined only inside [L..U] + if (exceptionOrCantInterpret(upr)) + return upr; Expression *e; uinteger_t ilwr; @@ -4850,10 +5123,10 @@ Expression *InExp::interpret(InterState *istate, CtfeGoal goal) printf("InExp::interpret() %s\n", toChars()); #endif Expression *e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e1)) return e1; Expression *e2 = this->e2->interpret(istate); - if (e2 == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e2)) return e2; if (e2->op == TOKnull) return new NullExp(loc, type); @@ -4865,7 +5138,7 @@ Expression *InExp::interpret(InterState *istate, CtfeGoal goal) if (e1->op == TOKslice) e1 = resolveSlice(e1); e = findKeyInAA((AssocArrayLiteralExp *)e2, e1); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; if (!e) return new NullExp(loc, type); @@ -4883,29 +5156,21 @@ Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) printf("CatExp::interpret() %s\n", toChars()); #endif e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - { - goto Lcant; - } + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op == TOKslice) { e1 = resolveSlice(e1); } e2 = this->e2->interpret(istate); - if (e2 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e2)) + return e2; if (e2->op == TOKslice) e2 = resolveSlice(e2); e = Cat(type, e1, e2); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); return e; - -Lcant: -#if LOG - printf("CatExp::interpret() %s CANT\n", toChars()); -#endif - return EXP_CANT_INTERPRET; } #if DMDV2 @@ -4946,28 +5211,56 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) printf("CastExp::interpret() %s\n", toChars()); #endif e1 = this->e1->interpret(istate, goal); - if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e1)) + return e1; + // If the expression has been cast to void, do nothing. + if (to->ty == Tvoid && goal == ctfeNeedNothing) + return e1; if (to->ty == Tpointer && e1->op != TOKnull) - { // Deal with casts from char[] to char * + { Type *pointee = ((TypePointer *)type)->next; - if (e1->type->ty == Tarray || e1->type->ty == Tsarray) + // Implement special cases of normally-unsafe casts +#if DMDV2 + if (pointee->ty == Taarray && e1->op == TOKaddress + && isAssocArray(((AddrExp*)e1)->e1->type)) + { // cast from template AA pointer to true AA pointer is OK. + return paintTypeOntoLiteral(to, e1); + } +#endif + if (e1->op == TOKint64) + { // Happens with Windows HANDLEs, for example. + return paintTypeOntoLiteral(to, e1); + } + bool castBackFromVoid = false; + if (e1->type->ty == Tarray || e1->type->ty == Tsarray || e1->type->ty == Tpointer) { // Check for unsupported type painting operations - Type *elemtype = ((TypeArray *)(e1->type))->next; - if ( -#if DMDV2 - e1->type->nextOf()->castMod(0) != to->nextOf()->castMod(0) -#else - e1->type->nextOf() != to->nextOf() -#endif - && !(elemtype->isintegral() && pointee->isintegral() - && elemtype->size() == pointee->size())) + // For slices, we need the type being sliced, + // since it may have already been type painted + Type *elemtype = e1->type->nextOf(); + if (e1->op == TOKslice) + elemtype = ((SliceExp *)e1)->e1->type->nextOf(); + // Allow casts from X* to void *, and X** to void** for any X. + // But don't allow cast from X* to void**. + // So, we strip all matching * from source and target to find X. + // Allow casts to X* from void* only if the 'void' was originally an X; + // we check this later on. + Type *ultimatePointee = pointee; + Type *ultimateSrc = elemtype; + while (ultimatePointee->ty == Tpointer && ultimateSrc->ty == Tpointer) { - error("reinterpreting cast from %s to %s is not supported in CTFE", - e1->type->toChars(), type->toChars()); + ultimatePointee = ultimatePointee->nextOf(); + ultimateSrc = ultimateSrc->nextOf(); + } + if (ultimatePointee->ty != Tvoid && ultimateSrc->ty != Tvoid + && !isSafePointerCast(elemtype, pointee)) + { + error("reinterpreting cast from %s* to %s* is not supported in CTFE", + elemtype->toChars(), pointee->toChars()); return EXP_CANT_INTERPRET; } + if (ultimateSrc->ty == Tvoid) + castBackFromVoid = true; } if (e1->op == TOKslice) @@ -4990,20 +5283,57 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) { // type painting operation IndexExp *ie = (IndexExp *)e1; e = new IndexExp(e1->loc, ie->e1, ie->e2); + if (castBackFromVoid) + { + // get the original type. For strings, it's just the type... + Type *origType = ie->e1->type->nextOf(); + // ..but for arrays of type void*, it's the type of the element + Expression *xx = NULL; + if (ie->e1->op == TOKarrayliteral && ie->e2->op == TOKint64) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)ie->e1; + uinteger_t indx = ie->e2->toInteger(); + if (indx < ale->elements->dim) + xx = ale->elements->tdata()[indx]; + } + if (xx && xx->op == TOKindex) + origType = ((IndexExp *)xx)->e1->type->nextOf(); + else if (xx && xx->op == TOKaddress) + origType= ((AddrExp *)xx)->e1->type; + else if (xx && xx->op == TOKvar) + origType = ((VarExp *)xx)->var->type; + if (!isSafePointerCast(origType, pointee)) + { + error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", + origType->toChars(), pointee->toChars()); + return EXP_CANT_INTERPRET; + } + } e->type = type; return e; } - if (e1->op == TOKint64) - { // Happens with Windows HANDLEs, for example. - return paintTypeOntoLiteral(to, e1); + if (e1->op == TOKaddress) + { + Type *origType = ((AddrExp *)e1)->type; + if (isSafePointerCast(origType, pointee)) + { + e = new AddrExp(loc, ((AddrExp *)e1)->e1); + e->type = type; + return e; + } } -#if DMDV2 - if (pointee->ty == Taarray && e1->op == TOKaddress - && isAssocArray(((AddrExp*)e1)->e1->type)) - { // cast from template AA pointer to true AA pointer is OK. - return paintTypeOntoLiteral(to, e1); + if (e1->op == TOKvar) + { // type painting operation + Type *origType = ((VarExp *)e1)->var->type; + if (castBackFromVoid && !isSafePointerCast(origType, pointee)) + { + error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", + origType->toChars(), pointee->toChars()); + return EXP_CANT_INTERPRET; + } + e = new VarExp(loc, ((VarExp *)e1)->var); + e->type = type; + return e; } -#endif error("pointer cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); return EXP_CANT_INTERPRET; @@ -5019,13 +5349,7 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) // types of identical size. if ((to->ty == Tsarray || to->ty == Tarray) && (e1->type->ty == Tsarray || e1->type->ty == Tarray) && -#if DMDV2 - e1->type->nextOf()->castMod(0) != to->nextOf()->castMod(0) -#else - e1->type->nextOf() != to->nextOf() -#endif - && !(to->nextOf()->isTypeBasic() && e1->type->nextOf()->isTypeBasic() - && to->nextOf()->size() == e1->type->nextOf()->size()) ) + !isSafePointerCast(e1->type->nextOf(), to->nextOf()) ) { error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); return EXP_CANT_INTERPRET; @@ -5036,22 +5360,9 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) { return new IntegerExp(loc, e1->op != TOKnull, to); } - if (e1->op == TOKnull) - return paintTypeOntoLiteral(to, e1); - - e = Cast(type, to, e1); - if (e == EXP_CANT_INTERPRET) - error("%s cannot be interpreted at compile time", toChars()); - return e; - -Lcant: -#if LOG - printf("CastExp::interpret() %s CANT\n", toChars()); -#endif - return EXP_CANT_INTERPRET; + return ctfeCast(loc, type, to, e1); } - Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; Expression *e1; @@ -5074,15 +5385,15 @@ Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) if (this->e1->type->ty == Tpointer && this->e1->type->nextOf()->ty != Tfunction) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); - if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e1)) + return e1; if (e1->op != TOKnull) return new IntegerExp(loc, 1, Type::tbool); } else e1 = this->e1->interpret(istate); - if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e1)) + return e1; if (isTrueBool(e1)) { } @@ -5091,8 +5402,8 @@ Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) if (msg) { e = msg->interpret(istate); - if (e == EXP_CANT_INTERPRET) - goto Lcant; + if (exceptionOrCantInterpret(e)) + return e; error("%s", e->toChars()); } else @@ -5123,16 +5434,15 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) { AddrExp *ade = (AddrExp *)ae->e1; Expression *ex = ade->e1; ex = ex->interpret(istate); - if (ex != EXP_CANT_INTERPRET) - { - if (ex->op == TOKstructliteral) - { StructLiteralExp *se = (StructLiteralExp *)ex; - dinteger_t offset = ae->e2->toInteger(); - e = se->getField(type, offset); - if (!e) - e = EXP_CANT_INTERPRET; - return e; - } + if (exceptionOrCantInterpret(ex)) + return ex; + if (ex->op == TOKstructliteral) + { StructLiteralExp *se = (StructLiteralExp *)ex; + dinteger_t offset = ae->e2->toInteger(); + e = se->getField(type, offset); + if (!e) + e = EXP_CANT_INTERPRET; + return e; } } e = Ptr(type, e1); @@ -5149,7 +5459,7 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) { // It's possible we have an array bounds error. We need to make sure it // errors with this line number, not the one where the pointer was set. e = e1->interpret(istate, ctfeNeedLvalue); - if (e == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(e)) return e; if (!(e->op == TOKvar || e->op == TOKdotvar || e->op == TOKindex || e->op == TOKslice || e->op == TOKaddress)) @@ -5162,6 +5472,16 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) if (e->op == TOKindex && e->type->ty == Tpointer) { IndexExp *ie = (IndexExp *)e; + // Is this a real index to an array of pointers, or just a CTFE pointer? + // If the index has the same levels of indirection, it's an index + int srcLevels = 0; + int destLevels = 0; + for(Type *xx = ie->e1->type; xx->ty == Tpointer; xx = xx->nextOf()) + ++srcLevels; + for(Type *xx = e->type->nextOf(); xx->ty == Tpointer; xx = xx->nextOf()) + ++destLevels; + bool isGenuineIndex = (srcLevels == destLevels); + if ((ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) && ie->e2->op == TOKint64) { @@ -5175,10 +5495,28 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) toChars(), len); return EXP_CANT_INTERPRET; } - return Index(type, ie->e1, ie->e2); + e = Index(type, ie->e1, ie->e2); + if (isGenuineIndex) + { + if (e->op == TOKindex) + e = e->interpret(istate, goal); + else if (e->op == TOKaddress) + e = paintTypeOntoLiteral(type, ((AddrExp *)e)->e1); + } + return e; } if (ie->e1->op == TOKassocarrayliteral) - return Index(type, ie->e1, ie->e2); + { + e = Index(type, ie->e1, ie->e2); + if (isGenuineIndex) + { + if (e->op == TOKindex) + e = e->interpret(istate, goal); + else if (e->op == TOKaddress) + e = paintTypeOntoLiteral(type, ((AddrExp *)e)->e1); + } + return e; + } } if (e->op == TOKstructliteral) return e; @@ -5189,7 +5527,11 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) if (e->op == TOKdotvar || e->op == TOKindex) e = e->interpret(istate, goal); } - if (e == EXP_CANT_INTERPRET) + else if (e->op == TOKvar) + { + e = e->interpret(istate, goal); + } + if (exceptionOrCantInterpret(e)) return e; } else if (e->op == TOKaddress) @@ -5217,6 +5559,8 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) #endif Expression *ex = e1->interpret(istate); + if (exceptionOrCantInterpret(ex)) + return ex; if (ex != EXP_CANT_INTERPRET) { #if DMDV2 @@ -5228,58 +5572,65 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) #endif if (ex->op == TOKaddress) ex = ((AddrExp *)ex)->e1; - if (ex->op == TOKstructliteral) - { StructLiteralExp *se = (StructLiteralExp *)ex; - VarDeclaration *v = var->isVarDeclaration(); - if (v) + VarDeclaration *v = var->isVarDeclaration(); + if (!v) + error("CTFE internal error: %s", toChars()); + if (ex->op == TOKnull && ex->type->toBasetype()->ty == Tclass) + { error("class '%s' is null and cannot be dereferenced", e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (ex->op == TOKstructliteral || ex->op == TOKclassreference) + { + StructLiteralExp *se = ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value : (StructLiteralExp *)ex; + // We can't use getField, because it makes a copy + int i = -1; + if (ex->op == TOKclassreference) + i = ((ClassReferenceExp *)ex)->getFieldIndex(type, v->offset); + else + i = se->getFieldIndex(type, v->offset); + if (i == -1) { - if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) - { - // We can't use getField, because it makes a copy - int i = se->getFieldIndex(type, v->offset); - if (i == -1) - { - error("couldn't find field %s in %s", v->toChars(), type->toChars()); - return EXP_CANT_INTERPRET; - } - e = se->elements->tdata()[i]; - // If it is an lvalue literal, return it... - if (e->op == TOKstructliteral) - return e; - if ((type->ty == Tsarray || goal == ctfeNeedLvalue) && ( - e->op == TOKarrayliteral || - e->op == TOKassocarrayliteral || e->op == TOKstring || - e->op == TOKslice)) - return e; - /* Element is an allocated pointer, which was created in - * CastExp. - */ - if (goal == ctfeNeedLvalue && e->op == TOKindex && - e->type == type && - (type->ty == Tpointer && type->nextOf()->ty != Tfunction)) - return e; - // ...Otherwise, just return the (simplified) dotvar expression - e = new DotVarExp(loc, ex, v); - e->type = type; - return e; - } - e = se->getField(type, v->offset); - if (!e) - { - error("couldn't find field %s in %s", v->toChars(), type->toChars()); - e = EXP_CANT_INTERPRET; - } - // If it is an rvalue literal, return it... - if (e->op == TOKstructliteral || e->op == TOKarrayliteral || - e->op == TOKassocarrayliteral || e->op == TOKstring) - return e; - if (type->ty == Tpointer && type->nextOf()->ty != Tfunction) - { - assert(e->type == type); - return e; - } - return e->interpret(istate, goal); + error("couldn't find field %s of type %s in %s", v->toChars(), type->toChars(), se->toChars()); + return EXP_CANT_INTERPRET; } + e = se->elements->tdata()[i]; + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + { + // If it is an lvalue literal, return it... + if (e->op == TOKstructliteral) + return e; + if ((type->ty == Tsarray || goal == ctfeNeedLvalue) && ( + e->op == TOKarrayliteral || + e->op == TOKassocarrayliteral || e->op == TOKstring || + e->op == TOKslice)) + return e; + /* Element is an allocated pointer, which was created in + * CastExp. + */ + if (goal == ctfeNeedLvalue && e->op == TOKindex && + e->type == type && + (type->ty == Tpointer && type->nextOf()->ty != Tfunction)) + return e; + // ...Otherwise, just return the (simplified) dotvar expression + e = new DotVarExp(loc, ex, v); + e->type = type; + return e; + } + if (!e) + { + error("couldn't find field %s in %s", v->toChars(), type->toChars()); + e = EXP_CANT_INTERPRET; + } + // If it is an rvalue literal, return it... + if (e->op == TOKstructliteral || e->op == TOKarrayliteral || + e->op == TOKassocarrayliteral || e->op == TOKstring) + return e; + if (type->ty == Tpointer && type->nextOf()->ty != Tfunction) + { + assert(e->type == type); + return e; + } + return e->interpret(istate, goal); } else error("%s.%s is not yet implemented at compile time", e1->toChars(), var->toChars()); @@ -5298,10 +5649,10 @@ Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) printf("RemoveExp::interpret() %s\n", toChars()); #endif Expression *agg = e1->interpret(istate); - if (agg == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(agg)) return agg; Expression *index = e2->interpret(istate); - if (index == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(index)) return index; if (agg->op == TOKnull) return EXP_VOID_INTERPRET; @@ -5313,8 +5664,8 @@ Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) for (size_t j = 0; j < valuesx->dim; ++j) { Expression *ekey = keysx->tdata()[j]; Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, index); - if (ex == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(ex)) + return ex; if (ex->isBool(TRUE)) ++removed; else if (removed != 0) @@ -5336,6 +5687,8 @@ Expression *interpret_length(InterState *istate, Expression *earg) earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; + if (exceptionOrCantInterpret(earg)) + return earg; dinteger_t len = 0; if (earg->op == TOKassocarrayliteral) len = ((AssocArrayLiteralExp *)earg)->keys->dim; @@ -5352,6 +5705,8 @@ Expression *interpret_keys(InterState *istate, Expression *earg, Type *elemType) earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; + if (exceptionOrCantInterpret(earg)) + return earg; if (earg->op == TOKnull) return new NullExp(earg->loc); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) @@ -5371,6 +5726,8 @@ Expression *interpret_values(InterState *istate, Expression *earg, Type *elemTyp earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; + if (exceptionOrCantInterpret(earg)) + return earg; if (earg->op == TOKnull) return new NullExp(earg->loc); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) @@ -5386,7 +5743,7 @@ Expression *interpret_values(InterState *istate, Expression *earg, Type *elemTyp // signature is int delegate(ref Value) OR int delegate(ref Key, ref Value) Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *deleg) { aa = aa->interpret(istate); - if (aa == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(aa)) return aa; if (aa->op != TOKassocarrayliteral) return new IntegerExp(deleg->loc, 0, Type::tsize_t); @@ -5425,8 +5782,8 @@ Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *de if (numParams == 2) args.tdata()[0] = ekey; eresult = fd->interpret(istate, &args, pthis); - if (eresult == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + if (exceptionOrCantInterpret(eresult)) + return eresult; assert(eresult->op == TOKint64); if (((IntegerExp *)eresult)->value != 0) @@ -5679,9 +6036,8 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del args.tdata()[numParams - 1] = val; eresult = fd->interpret(istate, &args, pthis); - if (eresult == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - + if (exceptionOrCantInterpret(eresult)) + return eresult; assert(eresult->op == TOKint64); if (((IntegerExp *)eresult)->value != 0) return eresult; @@ -5720,7 +6076,7 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, { Expression *earg = arguments->tdata()[i]; earg = earg->interpret(istate); - if (earg == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(earg)) return earg; args.tdata()[i] = earg; } @@ -5785,6 +6141,30 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, } } #endif +#if DMDV2 + if (pthis && !fd->fbody && fd->isCtorDeclaration() && fd->parent && fd->parent->parent && fd->parent->parent->ident == Id::object) + { + if (pthis->op == TOKclassreference && fd->parent->ident == Id::Throwable) + { // At present, the constructors just copy their arguments into the struct. + // But we might need some magic if stack tracing gets added to druntime. + StructLiteralExp *se = ((ClassReferenceExp *)pthis)->value; + assert(arguments->dim <= se->elements->dim); + for (int i = 0; i < arguments->dim; ++i) + { + Expression *e = arguments->tdata()[i]->interpret(istate); + if (exceptionOrCantInterpret(e)) + return e; + se->elements->tdata()[i] = e; + } + return EXP_VOID_INTERPRET; + } + } +#endif + if (nargs == 1 && !pthis && + (fd->ident == Id::criticalenter || fd->ident == Id::criticalexit)) + { // Support synchronized{} as a no-op + return EXP_VOID_INTERPRET; + } if (!pthis) { size_t idlen = strlen(fd->ident->string); @@ -5801,7 +6181,7 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, (s == 'c' || s == 'w' || s == 'd') && c != s) { Expression *str = arguments->tdata()[0]; str = str->interpret(istate); - if (str == EXP_CANT_INTERPRET) + if (exceptionOrCantInterpret(str)) return str; return foreachApplyUtf(istate, str, arguments->tdata()[1], rvs); } @@ -5816,9 +6196,10 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, * These functions exist to check for compiler CTFE bugs. */ -bool isStackValueValid(Expression *newval) +bool IsStackValueValid(Expression *newval) { - if (newval->type->ty == Tpointer && newval->type->nextOf()->ty != Tfunction) + if (newval->type->ty == Tnull || + newval->type->ty == Tpointer && newval->type->nextOf()->ty != Tfunction) { if (newval->op == TOKaddress || newval->op == TOKnull || newval->op == TOKstring) @@ -5839,8 +6220,10 @@ bool isStackValueValid(Expression *newval) newval->error("CTFE internal error: illegal pointer value %s\n", newval->toChars()); return false; } - if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || - (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || + if (newval->op == TOKclassreference || (newval->op == TOKnull && newval->type->ty == Tclass)) + return true; + if ((newval->op == TOKarrayliteral) || ( newval->op == TOKstructliteral) || + (newval->op == TOKstring) || (newval->op == TOKassocarrayliteral) || (newval->op == TOKnull) || (newval->op == TOKslice)) { return false; } @@ -5894,7 +6277,7 @@ bool isStackValueValid(Expression *newval) return false; } -bool isRefValueValid(Expression *newval) +bool IsRefValueValid(Expression *newval) { assert(newval); if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || @@ -5904,10 +6287,9 @@ bool isRefValueValid(Expression *newval) } // Dynamic arrays passed by ref may be null. When this happens // they may originate from an index or dotvar expression. - if (newval->type->ty == Tarray || newval->type->ty == Taarray - || newval->type->ty == Tclass) + if (newval->type->ty == Tarray || newval->type->ty == Taarray) if (newval->op == TOKdotvar || newval->op == TOKindex) - return isStackValueValid(newval); // actually must be null + return IsStackValueValid(newval); // actually must be null if (newval->op == TOKslice) { SliceExp *se = (SliceExp *)newval; @@ -5920,40 +6302,49 @@ bool isRefValueValid(Expression *newval) return false; } +bool VarDeclaration::hasValue() +{ + if (ctfeAdrOnStack == (size_t)-1) + return false; + return NULL != getValue(); +} + +Expression *VarDeclaration::getValue() +{ + return ctfeStack.getValue(this); +} + void VarDeclaration::setValueNull() { - literalvalue = NULL; + ctfeStack.setValue(this, NULL); } // Don't check for validity void VarDeclaration::setValueWithoutChecking(Expression *newval) { - assert(!newval || isStackValueValid(newval) || isRefValueValid(newval)); - literalvalue = newval; + ctfeStack.setValue(this, newval); } + void VarDeclaration::createRefValue(Expression *newval) { - assert(!literalvalue); - assert(isRefValueValid(newval)); - literalvalue = newval; + assert(IsRefValueValid(newval)); + ctfeStack.setValue(this, newval); } void VarDeclaration::setRefValue(Expression *newval) { - assert(literalvalue); - assert(isRefValueValid(newval)); - literalvalue = newval; + assert(IsRefValueValid(newval)); + ctfeStack.setValue(this, newval); } void VarDeclaration::setStackValue(Expression *newval) { - assert(literalvalue); - assert(isStackValueValid(newval)); - literalvalue = newval; + assert(IsStackValueValid(newval)); + ctfeStack.setValue(this, newval); } + void VarDeclaration::createStackValue(Expression *newval) { - assert(!literalvalue); - assert(isStackValueValid(newval)); - literalvalue = newval; + assert(IsStackValueValid(newval)); + ctfeStack.setValue(this, newval); } diff --git a/dmd2/intrange.c b/dmd2/intrange.c index 0c0ed521..baf099c0 100644 --- a/dmd2/intrange.c +++ b/dmd2/intrange.c @@ -21,7 +21,7 @@ static uinteger_t copySign(uinteger_t x, bool sign) { // return sign ? -x : x; - return (x - sign) ^ -sign; + return (x - (uinteger_t)sign) ^ -(uinteger_t)sign; } #ifndef UINT64_MAX diff --git a/dmd2/mars.c b/dmd2/mars.c index f05ea012..b1af8a43 100644 --- a/dmd2/mars.c +++ b/dmd2/mars.c @@ -54,6 +54,8 @@ void obj_start(char *srcfile); void obj_end(Library *library, File *objfile); #endif +void printCtfePerformanceStats(); + Global global; Global::Global() @@ -100,7 +102,7 @@ Global::Global() "\nMSIL back-end (alpha release) by Cristian L. Vlasceanu and associates."; #endif ; - version = "v2.056"; + version = "v2.057"; #if IN_LLVM ldc_version = "LDC trunk"; llvm_version = "LLVM 3.0"; @@ -1318,6 +1320,8 @@ int main(int argc, char *argv[]) if (global.errors || global.warnings) fatal(); + printCtfePerformanceStats(); + Library *library = NULL; if (global.params.lib) { diff --git a/dmd2/mtype.c b/dmd2/mtype.c index 2034a7d6..b05a9b9d 100644 --- a/dmd2/mtype.c +++ b/dmd2/mtype.c @@ -271,6 +271,8 @@ void Type::init() mangleChar[Tslice] = '@'; mangleChar[Treturn] = '@'; + mangleChar[Tnull] = 'n'; // same as TypeNone + for (size_t i = 0; i < TMAX; i++) { if (!mangleChar[i]) fprintf(stdmsg, "ty = %zd\n", i); @@ -293,6 +295,9 @@ void Type::init() } basic[Terror] = new TypeError(); + tnull = new TypeNull(); + tnull->deco = tnull->merge()->deco; + tvoidptr = tvoid->pointerTo(); tstring = tchar->invariantOf()->arrayOf(); @@ -376,24 +381,6 @@ Type *Type::trySemantic(Loc loc, Scope *sc) return t; } -/******************************* - * Determine if converting 'this' to 'to' is an identity operation, - * a conversion to const operation, or the types aren't the same. - * Returns: - * MATCHexact 'this' == 'to' - * MATCHconst 'to' is const - * MATCHnomatch conversion to mutable or invariant - */ - -MATCH Type::constConv(Type *to) -{ - if (equals(to)) - return MATCHexact; - if (ty == to->ty && MODimplicitConv(mod, to->mod)) - return MATCHconst; - return MATCHnomatch; -} - /******************************** * Convert to 'const'. */ @@ -410,7 +397,7 @@ Type *Type::constOf() Type *t = makeConst(); t = t->merge(); t->fixTo(this); - //printf("-Type::constOf() %p %s\n", t, toChars()); + //printf("-Type::constOf() %p %s\n", t, t->toChars()); return t; } @@ -1226,7 +1213,10 @@ Type *Type::addMod(unsigned mod) break; case MODshared | MODwild: - t = sharedWildOf(); + if (isConst()) + t = sharedConstOf(); + else + t = sharedWildOf(); break; default: @@ -1259,16 +1249,6 @@ Type *Type::addStorageClass(StorageClass stc) return addMod(mod); } -/************************** - * Return type with the top level of it being mutable. - */ -Type *Type::toHeadMutable() -{ - if (!mod) - return this; - return mutableOf(); -} - Type *Type::pointerTo() { if (ty == Terror) @@ -1308,6 +1288,43 @@ Type *Type::arrayOf() return arrayof; } +Type *Type::aliasthisOf() +{ + AggregateDeclaration *ad = NULL; + if (ty == Tclass) + { + ad = ((TypeClass *)this)->sym; + goto L1; + } + else if (ty == Tstruct) + { + ad = ((TypeStruct *)this)->sym; + L1: + if (!ad->aliasthis) + return NULL; + + Declaration *d = ad->aliasthis->isDeclaration(); + if (d) + { assert(d->type); + Type *t = d->type; + if (d->isVarDeclaration() && d->needThis()) + { + t = t->addMod(this->mod); + } + else if (d->isFuncDeclaration()) + { + FuncDeclaration *fd = (FuncDeclaration *)d; + Expression *ethis = this->defaultInit(0); + fd = fd->overloadResolve(0, ethis, NULL); + if (fd) + t = ((TypeFunction *)fd->type)->next; + } + return t; + } + } + return NULL; +} + Dsymbol *Type::toDsymbol(Scope *sc) { return NULL; @@ -1527,21 +1544,23 @@ void Type::toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod) { if (mod != this->mod) { - if (this->mod & MODshared) + if (!(mod & MODshared) && (this->mod & MODshared)) { MODtoBuffer(buf, this->mod & MODshared); buf->writeByte('('); } - if (this->mod & ~MODshared) + int m1 = this->mod & ~MODshared; + int m2 = (mod ^ m1) & m1; + if (m2) { - MODtoBuffer(buf, this->mod & ~MODshared); + MODtoBuffer(buf, m2); buf->writeByte('('); toCBuffer2(buf, hgs, this->mod); buf->writeByte(')'); } else toCBuffer2(buf, hgs, this->mod); - if (this->mod & MODshared) + if (!(mod & MODshared) && (this->mod & MODshared)) { buf->writeByte(')'); } @@ -1561,11 +1580,16 @@ void Type::modToBuffer(OutBuffer *buf) */ Type *Type::merge() -{ Type *t; - +{ if (ty == Terror) return this; + if (ty == Ttypeof) return this; + if (ty == Tident) return this; + if (ty == Tinstance) return this; + if (nextOf() && !nextOf()->merge()->deco) + return this; + //printf("merge(%s)\n", toChars()); - t = this; + Type *t = this; assert(t); if (!deco) { @@ -1769,6 +1793,107 @@ MATCH Type::implicitConvTo(Type *to) return MATCHnomatch; } +/******************************* + * Determine if converting 'this' to 'to' is an identity operation, + * a conversion to const operation, or the types aren't the same. + * Returns: + * MATCHexact 'this' == 'to' + * MATCHconst 'to' is const + * MATCHnomatch conversion to mutable or invariant + */ + +MATCH Type::constConv(Type *to) +{ + //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to->toChars()); + if (equals(to)) + return MATCHexact; + if (ty == to->ty && MODimplicitConv(mod, to->mod)) + return MATCHconst; + return MATCHnomatch; +} + +/*************************************** + * Return MOD bits matching this type to wild parameter type (tprm). + */ + +unsigned Type::wildConvTo(Type *tprm) +{ + //printf("Type::wildConvTo this = '%s', tprm = '%s'\n", toChars(), tprm->toChars()); + + if (tprm->isWild() && implicitConvTo(tprm->substWildTo(MODconst))) + { + if (isWild()) + return MODwild; + else if (isConst()) + return MODconst; + else if (isImmutable()) + return MODimmutable; + else if (isMutable()) + return MODmutable; + else + assert(0); + } + return 0; +} + +Type *Type::substWildTo(unsigned mod) +{ + //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod); + Type *t; + + if (nextOf()) + { + t = nextOf()->substWildTo(mod); + if (t == nextOf()) + t = this; + else + { + if (ty == Tpointer) + t = t->pointerTo(); + else if (ty == Tarray) + t = t->arrayOf(); + else if (ty == Tsarray) + t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy()); + else if (ty == Taarray) + { + t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy()); + t = t->merge(); + } + else + assert(0); + + t = t->addMod(this->mod); + } + } + else + t = this; + + if (isWild()) + { + if (mod & MODconst) + t = isShared() ? t->sharedConstOf() : t->constOf(); + else if (mod & MODimmutable) + t = t->invariantOf(); + else if (mod & MODwild) + t = isShared() ? t->sharedWildOf() : t->wildOf(); + else + t = isShared() ? t->sharedOf() : t->mutableOf(); + } + + //printf("-Type::substWildTo t = %s\n", t->toChars()); + return t; +} + +/************************** + * Return type with the top level of it being mutable. + */ +Type *Type::toHeadMutable() +{ + if (!mod) + return this; + return mutableOf(); +} + Expression *Type::getProperty(Loc loc, Identifier *ident) { Expression *e; @@ -2085,100 +2210,6 @@ int Type::hasWild() return mod & MODwild; } -/*************************************** - * Return MOD bits matching argument type (targ) to wild parameter type (this). - */ - -unsigned getWildModConv(Type *twild, Type *targ) -{ - assert(twild); - assert(targ); - - unsigned mod = 0; - - if (twild->nextOf()) - mod = getWildModConv(twild->nextOf(), targ->nextOf()); - - if (!mod) - { - if (twild->isWild()) - { - if (targ->isWild()) - mod = MODwild; - else if (targ->isConst()) - mod = MODconst; - else if (targ->isImmutable()) - mod = MODimmutable; - else if (targ->isMutable()) - mod = MODmutable; - else - assert(0); - } - } - - return mod; -} - -unsigned Type::wildMatch(Type *targ) -{ - //printf("Type::wildMatch this = '%s', targ = '%s'\n", toChars(), targ->toChars()); - assert(hasWild()); - - Type *tc = substWildTo(MODconst); - if (targ->implicitConvTo(tc)) - return getWildModConv(this, targ); - - return 0; -} - -Type *Type::substWildTo(unsigned mod) -{ - //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod); - Type *t; - - if (nextOf()) - { - t = nextOf()->substWildTo(mod); - if (t == nextOf()) - t = this; - else - { - if (ty == Tpointer) - t = t->pointerTo(); - else if (ty == Tarray) - t = t->arrayOf(); - else if (ty == Tsarray) - t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy()); - else if (ty == Taarray) - { - t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy()); - t = t->merge(); - } - else - assert(0); - - t = t->addMod(this->mod); - } - } - else - t = this; - - if (isWild()) - { - if (mod & MODconst) - t = t->constOf(); - else if (mod & MODimmutable) - t = t->invariantOf(); - else if (mod & MODwild) - t = t->wildOf(); - else - t = t->mutableOf(); - } - - //printf("-Type::substWildTo t = %s\n", t->toChars()); - return t; -} - /******************************** * We've mistakenly parsed this as a type. * Redo it as an Expression. @@ -2244,6 +2275,12 @@ TypeError::TypeError() { } +Type *TypeError::syntaxCopy() +{ + // No semantic analysis done, no need to copy + return this; +} + void TypeError::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) { buf->writestring("_error_"); @@ -2357,8 +2394,10 @@ Type *TypeNext::makeShared() //(next->deco || next->ty == Tfunction) && !next->isImmutable() && !next->isShared()) { - if (next->isConst() || next->isWild()) + if (next->isConst()) t->next = next->sharedConstOf(); + else if (next->isWild()) + t->next = next->sharedWildOf(); else t->next = next->sharedOf(); } @@ -2429,7 +2468,10 @@ Type *TypeNext::makeSharedWild() //(next->deco || next->ty == Tfunction) && !next->isImmutable() && !next->isSharedConst()) { - t->next = next->sharedWildOf(); + if (next->isConst()) + t->next = next->sharedConstOf(); + else + t->next = next->sharedWildOf(); } if (ty == Taarray) { @@ -2466,6 +2508,22 @@ MATCH TypeNext::constConv(Type *to) return m; } +unsigned TypeNext::wildConvTo(Type *tprm) +{ + if (ty == Tfunction) + return 0; + + unsigned mod = 0; + Type *tn = tprm->nextOf(); + if (!tn) + return 0; + mod = next->wildConvTo(tn); + if (!mod) + mod = Type::wildConvTo(tprm); + + return mod; +} + void TypeNext::transitive() { @@ -3167,7 +3225,14 @@ MATCH TypeBasic::implicitConvTo(Type *to) #if DMDV2 if (ty == to->ty) { - return (mod == to->mod) ? MATCHexact : MATCHconst; + if (mod == to->mod) + return MATCHexact; + else if (MODimplicitConv(mod, to->mod)) + return MATCHconst; + else if (!((mod ^ to->mod) & MODshared)) // for wild matching + return MATCHconst; + else + return MATCHconvert; } #endif @@ -3598,7 +3663,10 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) if (dim) { dinteger_t n, n2; + int errors = global.errors; dim = semanticLength(sc, tbn, dim); + if (errors != global.errors) + goto Lerror; dim = dim->optimize(WANTvalue); if (sc && sc->parameterSpecialization && dim->op == TOKvar && @@ -3787,7 +3855,8 @@ MATCH TypeSArray::implicitConvTo(Type *to) if (next->equals(ta->next) || // next->implicitConvTo(ta->next) >= MATCHconst || next->constConv(ta->next) != MATCHnomatch || - (ta->next->isBaseOf(next, &offset) && offset == 0) || + (ta->next->isBaseOf(next, &offset) && offset == 0 && + !ta->next->isMutable()) || ta->next->ty == Tvoid) return MATCHconvert; return MATCHnomatch; @@ -4026,6 +4095,13 @@ MATCH TypeDArray::implicitConvTo(Type *to) if (!MODimplicitConv(next->mod, ta->next->mod)) return MATCHnomatch; // not const-compatible + // Check head inout conversion: + // T [] -> inout(const(T)[]) + // const(T)[] -> inout(const(T)[]) + if (isMutable() && ta->isWild()) + if ((next->isMutable() || next->isConst()) && ta->next->isConst()) + return MATCHnomatch; + /* Allow conversion to void[] */ if (next->ty != Tvoid && ta->next->ty == Tvoid) @@ -4052,9 +4128,10 @@ MATCH TypeDArray::implicitConvTo(Type *to) } #endif - /* Conversion of array of derived to array of base + /* Conversion of array of derived to array of const(base) */ - if (ta->next->isBaseOf(next, &offset) && offset == 0) + if (ta->next->isBaseOf(next, &offset) && offset == 0 && + !ta->next->isMutable()) return MATCHconvert; } return Type::implicitConvTo(to); @@ -4452,6 +4529,13 @@ MATCH TypeAArray::implicitConvTo(Type *to) if (!MODimplicitConv(index->mod, ta->index->mod)) return MATCHnomatch; // not const-compatible + // Check head inout conversion: + // V [K] -> inout(const(V)[K]) + // const(V)[K] -> inout(const(V)[K]) + if (isMutable() && ta->isWild()) + if ((next->isMutable() || next->isConst()) && ta->next->isConst()) + return MATCHnomatch; + MATCH m = next->constConv(ta->next); MATCH mi = index->constConv(ta->index); if (m != MATCHnomatch && mi != MATCHnomatch) @@ -4463,6 +4547,16 @@ MATCH TypeAArray::implicitConvTo(Type *to) return m; } } + else if (to->ty == Tstruct && ((TypeStruct *)to)->sym->ident == Id::AssociativeArray) + { + int errs = global.startGagging(); + Type *from = getImpl()->type; + if (global.endGagging(errs)) + { + return MATCHnomatch; + } + return from->implicitConvTo(to); + } return Type::implicitConvTo(to); } @@ -4573,6 +4667,13 @@ MATCH TypePointer::implicitConvTo(Type *to) if (!MODimplicitConv(next->mod, tp->next->mod)) return MATCHnomatch; // not const-compatible + // Check head inout conversion: + // T * -> inout(const(T)*) + // const(T)* -> inout(const(T)*) + if (isMutable() && tp->isWild()) + if ((next->isMutable() || next->isConst()) && tp->next->isConst()) + return MATCHnomatch; + /* Alloc conversion to void* */ if (next->ty != Tvoid && tp->next->ty == Tvoid) @@ -5313,25 +5414,32 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) fparam->defaultArg = fparam->defaultArg->implicitCastTo(argsc, fparam->type); } - /* If fparam turns out to be a tuple, the number of parameters may + /* If fparam after semantic() turns out to be a tuple, the number of parameters may * change. */ if (t->ty == Ttuple) { - // Propagate storage class from tuple parameters to their element-parameters. TypeTuple *tt = (TypeTuple *)t; - if (tt->arguments) + if (fparam->storageClass && tt->arguments && tt->arguments->dim) { + /* Propagate additional storage class from tuple parameters to their + * element-parameters. + * Make a copy, as original may be referenced elsewhere. + */ size_t tdim = tt->arguments->dim; + Parameters *newparams = new Parameters(); + newparams->setDim(tdim); for (size_t j = 0; j < tdim; j++) - { Parameter *narg = tt->arguments->tdata()[j]; - narg->storageClass |= fparam->storageClass; + { Parameter *narg = (*tt->arguments)[j]; + newparams->tdata()[j] = new Parameter(narg->storageClass | fparam->storageClass, + narg->type, narg->ident, narg->defaultArg); } - fparam->storageClass = 0; + fparam->type = new TypeTuple(newparams); } + fparam->storageClass = 0; /* Reset number of parameters, and back up one to do this fparam again, - * now that it is the first element of a tuple + * now that it is a tuple */ dim = Parameter::dim(tf->parameters); i--; @@ -5353,6 +5461,9 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) else error(loc, "auto can only be used for template function parameters"); } + + // Remove redundant storage classes for type, they are already applied + fparam->storageClass &= ~(STC_TYPECTOR | STCin); } argsc->pop(); } @@ -5475,7 +5586,6 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) { //printf("TypeFunction::callMatch() %s\n", toChars()); MATCH match = MATCHexact; // assume exact match - bool exactwildmatch = FALSE; bool wildmatch = FALSE; if (ethis) @@ -5559,24 +5669,10 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) else m = arg->implicitConvTo(p->type); //printf("match %d\n", m); - if (p->type->hasWild()) + if (m == MATCHnomatch && arg->type->wildConvTo(p->type)) { - if (m == MATCHnomatch) - { - if (p->type->wildMatch(arg->type)) - { - wildmatch = TRUE; // mod matched to wild - m = MATCHconst; - } - } - else - exactwildmatch = TRUE; // wild matched to wild - - /* If both are allowed, then there could be more than one - * binding of mod to wild, leaving a gaping type hole. - */ - //if (wildmatch && exactwildmatch) - // m = MATCHnomatch; + wildmatch = TRUE; // mod matched to wild + m = MATCHconst; } } @@ -6014,7 +6110,10 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, else e = e->type->dotExp(sc, e, id); } - *pe = e; + if (e->op == TOKtype) + *pt = e->type; + else + *pe = e; } else { @@ -6531,6 +6630,8 @@ Type *TypeTypeof::semantic(Loc loc, Scope *sc) goto Lerr; } + t = t->addMod(mod); + /* typeof should reflect the true type, * not what 'auto' would have gotten us. */ @@ -6970,19 +7071,6 @@ Dsymbol *TypeTypedef::toDsymbol(Scope *sc) return sym; } -Type *TypeTypedef::toHeadMutable() -{ - if (!mod) - return this; - - Type *tb = toBasetype(); - Type *t = tb->toHeadMutable(); - if (t->equals(tb)) - return this; - else - return mutableOf(); -} - void TypeTypedef::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) { Type::toDecoBuffer(buf, flag, mangle); @@ -7118,6 +7206,18 @@ MATCH TypeTypedef::constConv(Type *to) return MATCHnomatch; } +Type *TypeTypedef::toHeadMutable() +{ + if (!mod) + return this; + + Type *tb = toBasetype(); + Type *t = tb->toHeadMutable(); + if (t->equals(tb)) + return this; + else + return mutableOf(); +} Expression *TypeTypedef::defaultInit(Loc loc) { @@ -7424,7 +7524,7 @@ L1: e = e->semantic(sc); return e; } - if (d->needThis() && fd && fd->vthis) + else if (d->needThis() && fd && fd->vthis) { e = new DotVarExp(e->loc, new ThisExp(e->loc), d); e = e->semantic(sc); @@ -7441,7 +7541,7 @@ L1: accessCheck(e->loc, sc, e, d); ve = new VarExp(e->loc, d); e = new CommaExp(e->loc, e, ve); - e->type = d->type; + e = e->semantic(sc); return e; } @@ -7585,33 +7685,6 @@ int TypeStruct::hasPointers() return FALSE; } -static MATCH aliasthisConvTo(AggregateDeclaration *ad, Type *from, Type *to) -{ - assert(ad->aliasthis); - Declaration *d = ad->aliasthis->isDeclaration(); - if (d) - { assert(d->type); - Type *t = d->type; - if (d->isVarDeclaration() && d->needThis()) - { - t = t->addMod(from->mod); - } - else if (d->isFuncDeclaration()) - { - FuncDeclaration *fd = (FuncDeclaration *)d; - Expression *ethis = from->defaultInit(0); - fd = fd->overloadResolve(0, ethis, NULL); - if (fd) - { - t = ((TypeFunction *)fd->type)->next; - } - } - MATCH m = t->implicitConvTo(to); - return m; - } - return MATCHnomatch; -} - MATCH TypeStruct::implicitConvTo(Type *to) { MATCH m; @@ -7659,17 +7732,12 @@ MATCH TypeStruct::implicitConvTo(Type *to) } } else if (sym->aliasthis) - m = aliasthisConvTo(sym, this, to); + m = aliasthisOf()->implicitConvTo(to); else m = MATCHnomatch; // no match return m; } -Type *TypeStruct::toHeadMutable() -{ - return this; -} - MATCH TypeStruct::constConv(Type *to) { if (equals(to)) @@ -7680,6 +7748,22 @@ MATCH TypeStruct::constConv(Type *to) return MATCHnomatch; } +unsigned TypeStruct::wildConvTo(Type *tprm) +{ + if (ty == tprm->ty && sym == ((TypeStruct *)tprm)->sym) + return Type::wildConvTo(tprm); + + if (sym->aliasthis) + return aliasthisOf()->wildConvTo(tprm); + + return 0; +} + +Type *TypeStruct::toHeadMutable() +{ + return this; +} + /***************************** TypeClass *****************************/ @@ -8061,7 +8145,7 @@ L1: accessCheck(e->loc, sc, e, d); ve = new VarExp(e->loc, d); e = new CommaExp(e->loc, e, ve); - e->type = d->type; + e = e->semantic(sc); return e; } @@ -8123,7 +8207,7 @@ MATCH TypeClass::implicitConvTo(Type *to) m = MATCHnomatch; if (sym->aliasthis) - m = aliasthisConvTo(sym, this, to); + m = aliasthisOf()->implicitConvTo(to); return m; } @@ -8138,6 +8222,23 @@ MATCH TypeClass::constConv(Type *to) return MATCHnomatch; } +unsigned TypeClass::wildConvTo(Type *tprm) +{ + Type *tcprm = tprm->substWildTo(MODconst); + + if (constConv(tcprm)) + return Type::wildConvTo(tprm); + + ClassDeclaration *cdprm = tcprm->isClassHandle(); + if (cdprm && cdprm->isBaseOf(sym, NULL)) + return Type::wildConvTo(tprm); + + if (sym->aliasthis) + return aliasthisOf()->wildConvTo(tprm); + + return 0; +} + Type *TypeClass::toHeadMutable() { return this; @@ -8481,6 +8582,58 @@ void TypeSlice::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) buf->printf("%s]", upr->toChars()); } +/***************************** TypeNull *****************************/ + +TypeNull::TypeNull() + : Type(Tnull) +{ +} + +Type *TypeNull::syntaxCopy() +{ + // No semantic analysis done, no need to copy + return this; +} + +MATCH TypeNull::implicitConvTo(Type *to) +{ + //printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", this, to); + //printf("from: %s\n", toChars()); + //printf("to : %s\n", to->toChars()); + MATCH m = Type::implicitConvTo(to); + if (m) + return m; + + // NULL implicitly converts to any pointer type or dynamic array + //if (type->ty == Tpointer && type->nextOf()->ty == Tvoid) + { + Type *tb= to->toBasetype(); + if (tb->ty == Tpointer || tb->ty == Tarray || + tb->ty == Taarray || tb->ty == Tclass || + tb->ty == Tdelegate) + return MATCHconst; + } + + return MATCHnomatch; +} + +void TypeNull::toDecoBuffer(OutBuffer *buf, int flag) +{ + //tvoidptr->toDecoBuffer(buf, flag); + Type::toDecoBuffer(buf, flag); +} + +void TypeNull::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + buf->writestring("typeof(null)"); +} + +d_uns64 TypeNull::size(Loc loc) { return tvoidptr->size(loc); } +//Expression *TypeNull::getProperty(Loc loc, Identifier *ident) { return new ErrorExp(); } +//Expression *TypeNull::dotExp(Scope *sc, Expression *e, Identifier *ident) { return new ErrorExp(); } +Expression *TypeNull::defaultInit(Loc loc) { return new NullExp(0, Type::tnull); } +//Expression *TypeNull::defaultInitLiteral(Loc loc) { return new ErrorExp(); } + /***************************** Parameter *****************************/ Parameter::Parameter(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg) @@ -8557,11 +8710,12 @@ void Parameter::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *argu { OutBuffer argbuf; - for (size_t i = 0; i < arguments->dim; i++) + size_t dim = Parameter::dim(arguments); + for (size_t i = 0; i < dim; i++) { if (i) buf->writestring(", "); - Parameter *arg = arguments->tdata()[i]; + Parameter *arg = Parameter::getNth(arguments, i); if (arg->storageClass & STCauto) buf->writestring("auto "); @@ -8609,43 +8763,39 @@ void Parameter::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *argu buf->writeByte(')'); } +static const int mangleFlag = 0x01; + +static int argsToDecoBufferDg(void *ctx, size_t n, Parameter *arg, int flags) +{ + arg->toDecoBuffer((OutBuffer *)ctx, flags & mangleFlag); + return 0; +} void Parameter::argsToDecoBuffer(OutBuffer *buf, Parameters *arguments, bool mangle) { //printf("Parameter::argsToDecoBuffer()\n"); - // Write argument types if (arguments) - { - size_t dim = Parameter::dim(arguments); - for (size_t i = 0; i < dim; i++) - { - Parameter *arg = Parameter::getNth(arguments, i); - arg->toDecoBuffer(buf, mangle); - } - } + foreach(arguments, &argsToDecoBufferDg, buf, 0, mangle ? mangleFlag : 0); } - /**************************************** * Determine if parameter list is really a template parameter list * (i.e. it has auto or alias parameters) */ +static int isTPLDg(void *ctx, size_t n, Parameter *arg, int) +{ + if (arg->storageClass & (STCalias | STCauto | STCstatic)) + return 1; + return 0; +} + int Parameter::isTPL(Parameters *arguments) { //printf("Parameter::isTPL()\n"); - if (arguments) - { - size_t dim = Parameter::dim(arguments); - for (size_t i = 0; i < dim; i++) - { - Parameter *arg = Parameter::getNth(arguments, i); - if (arg->storageClass & (STCalias | STCauto | STCstatic)) - return 1; - } - } + return foreach(arguments, &isTPLDg, NULL); return 0; } @@ -8697,6 +8847,7 @@ void Parameter::toDecoBuffer(OutBuffer *buf, bool mangle) break; default: #ifdef DEBUG + printf("storageClass = x%lx\n", storageClass & (STCin | STCout | STCref | STClazy)); halt(); #endif assert(0); @@ -8716,23 +8867,17 @@ void Parameter::toDecoBuffer(OutBuffer *buf, bool mangle) * Determine number of arguments, folding in tuples. */ +static int dimDg(void *ctx, size_t n, Parameter *, int) +{ + ++*(size_t *)ctx; + return 0; +} + size_t Parameter::dim(Parameters *args) { size_t n = 0; if (args) - { - for (size_t i = 0; i < args->dim; i++) - { Parameter *arg = args->tdata()[i]; - Type *t = arg->type->toBasetype(); - - if (t->ty == Ttuple) - { TypeTuple *tu = (TypeTuple *)t; - n += dim(tu->arguments); - } - else - n++; - } - } + foreach(args, &dimDg, &n); return n; } @@ -8744,29 +8889,59 @@ size_t Parameter::dim(Parameters *args) * of Parameters */ +struct GetNthParamCtx +{ + size_t nth; + Parameter *arg; +}; + +static int getNthParamDg(void *ctx, size_t n, Parameter *arg, int) +{ + GetNthParamCtx *p = (GetNthParamCtx *)ctx; + if (n == p->nth) + { p->arg = arg; + return 1; + } + return 0; +} + Parameter *Parameter::getNth(Parameters *args, size_t nth, size_t *pn) { - if (!args) - return NULL; + GetNthParamCtx ctx = { nth, NULL }; + int res = foreach(args, &getNthParamDg, &ctx); + return res ? ctx.arg : NULL; +} - size_t n = 0; +/*************************************** + * Expands tuples in args in depth first order. Calls + * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter. + * If dg returns !=0, stops and returns that value else returns 0. + * Use this function to avoid the O(N + N^2/2) complexity of + * calculating dim and calling N times getNth. + */ + +int Parameter::foreach(Parameters *args, Parameter::ForeachDg dg, void *ctx, size_t *pn, int flags) +{ + assert(args && dg); + + size_t n = pn ? *pn : 0; // take over index + int result = 0; for (size_t i = 0; i < args->dim; i++) { Parameter *arg = args->tdata()[i]; Type *t = arg->type->toBasetype(); if (t->ty == Ttuple) { TypeTuple *tu = (TypeTuple *)t; - arg = getNth(tu->arguments, nth - n, &n); - if (arg) - return arg; + result = foreach(tu->arguments, dg, ctx, &n, flags); } - else if (n == nth) - return arg; else - n++; + result = dg(ctx, n++, arg, flags); + + if (result) + break; } if (pn) - *pn += n; - return NULL; + *pn = n; // update index + return result; } diff --git a/dmd2/mtype.h b/dmd2/mtype.h index e1d5b26e..de57ad2e 100644 --- a/dmd2/mtype.h +++ b/dmd2/mtype.h @@ -70,8 +70,8 @@ enum ENUMTY Tident, Tclass, Tstruct, - Tenum, + Ttypedef, Tdelegate, Tnone, @@ -81,8 +81,8 @@ enum ENUMTY Tint16, Tuns16, Tint32, - Tuns32, + Tint64, Tuns64, Tfloat32, @@ -92,8 +92,8 @@ enum ENUMTY Timaginary64, Timaginary80, Tcomplex32, - Tcomplex64, + Tcomplex80, Tbool, Tchar, @@ -102,10 +102,11 @@ enum ENUMTY Terror, Tinstance, Ttypeof, - Ttuple, Tslice, + Treturn, + Tnull, TMAX }; typedef unsigned char TY; // ENUMTY @@ -187,6 +188,8 @@ struct Type : Object static Type *tstring; // immutable(char)[] #define terror basic[Terror] // for error recovery + #define tnull basic[Tnull] // for null type + #define tsize_t basic[Tsize_t] // matches size_t alias #define tptrdiff_t basic[Tptrdiff_t] // matches ptrdiff_t alias #define thash_t tsize_t // matches hash_t alias @@ -293,6 +296,7 @@ struct Type : Object Type *pointerTo(); Type *referenceTo(); Type *arrayOf(); + Type *aliasthisOf(); virtual Type *makeConst(); virtual Type *makeInvariant(); virtual Type *makeShared(); @@ -302,10 +306,12 @@ struct Type : Object virtual Type *makeMutable(); virtual Dsymbol *toDsymbol(Scope *sc); virtual Type *toBasetype(); - virtual Type *toHeadMutable(); virtual int isBaseOf(Type *t, int *poffset); - virtual MATCH constConv(Type *to); virtual MATCH implicitConvTo(Type *to); + virtual MATCH constConv(Type *to); + virtual unsigned wildConvTo(Type *tprm); + Type *substWildTo(unsigned mod); + virtual Type *toHeadMutable(); virtual ClassDeclaration *isClassHandle(); virtual Expression *getProperty(Loc loc, Identifier *ident); virtual Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); @@ -326,8 +332,6 @@ struct Type : Object virtual int builtinTypeInfo(); virtual Type *reliesOnTident(); virtual int hasWild(); - unsigned wildMatch(Type *targ); - Type *substWildTo(unsigned mod); virtual Expression *toExpression(); virtual int hasPointers(); virtual TypeTuple *toArgTypes(); @@ -359,6 +363,7 @@ struct Type : Object struct TypeError : Type { TypeError(); + Type *syntaxCopy(); void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); @@ -387,6 +392,7 @@ struct TypeNext : Type Type *makeSharedWild(); Type *makeMutable(); MATCH constConv(Type *to); + unsigned wildConvTo(Type *tprm); void transitive(); }; @@ -788,6 +794,7 @@ struct TypeStruct : Type TypeTuple *toArgTypes(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); + unsigned wildConvTo(Type *tprm); Type *toHeadMutable(); #if CPP_MANGLE void toCppMangle(OutBuffer *buf, CppMangleState *cms); @@ -874,6 +881,7 @@ struct TypeTypedef : Type Type *toBasetype(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); + Type *toHeadMutable(); Expression *defaultInit(Loc loc); Expression *defaultInitLiteral(Loc loc); int isZeroInit(Loc loc); @@ -885,7 +893,6 @@ struct TypeTypedef : Type int hasPointers(); TypeTuple *toArgTypes(); int hasWild(); - Type *toHeadMutable(); #if CPP_MANGLE void toCppMangle(OutBuffer *buf, CppMangleState *cms); #endif @@ -912,6 +919,9 @@ struct TypeClass : Type ClassDeclaration *isClassHandle(); int isBaseOf(Type *t, int *poffset); MATCH implicitConvTo(Type *to); + MATCH constConv(Type *to); + unsigned wildConvTo(Type *tprm); + Type *toHeadMutable(); Expression *defaultInit(Loc loc); int isZeroInit(Loc loc); MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); @@ -921,13 +931,9 @@ struct TypeClass : Type int hasPointers(); TypeTuple *toArgTypes(); int builtinTypeInfo(); -#if DMDV2 - Type *toHeadMutable(); - MATCH constConv(Type *to); #if CPP_MANGLE void toCppMangle(OutBuffer *buf, CppMangleState *cms); #endif -#endif #if IN_DMD type *toCtype(); @@ -967,6 +973,23 @@ struct TypeSlice : TypeNext void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); }; +struct TypeNull : Type +{ + TypeNull(); + + Type *syntaxCopy(); + void toDecoBuffer(OutBuffer *buf, int flag); + MATCH implicitConvTo(Type *to); + + void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); + + d_uns64 size(Loc loc); + //Expression *getProperty(Loc loc, Identifier *ident); + //Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); + Expression *defaultInit(Loc loc); + //Expression *defaultInitLiteral(Loc loc); +}; + /**************************************************************/ //enum InOut { None, In, Out, InOut, Lazy }; @@ -991,6 +1014,9 @@ struct Parameter : Object static int isTPL(Parameters *arguments); static size_t dim(Parameters *arguments); static Parameter *getNth(Parameters *arguments, size_t nth, size_t *pn = NULL); + + typedef int (*ForeachDg)(void *ctx, size_t paramidx, Parameter *param, int flags); + static int foreach(Parameters *args, ForeachDg dg, void *ctx, size_t *pn=NULL, int flags = 0); }; extern int PTRSIZE; diff --git a/dmd2/opover.c b/dmd2/opover.c index 9a9b40be..b4e61247 100644 --- a/dmd2/opover.c +++ b/dmd2/opover.c @@ -28,6 +28,7 @@ #include "mtype.h" #include "init.h" #include "expression.h" +#include "scope.h" #include "id.h" #include "declaration.h" #include "aggregate.h" @@ -373,6 +374,29 @@ Expression *ArrayExp::op_overload(Scope *sc) Dsymbol *fd = search_function(ad, opId()); if (fd) { + for (size_t i = 0; i < arguments->dim; i++) + { Expression *x = arguments->tdata()[i]; + // Create scope for '$' variable for this dimension + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, this); + sym->loc = loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + lengthVar = NULL; // Create it only if required + currentDimension = i; // Dimension for $, if required + + x = x->semantic(sc); + if (!x->type) + error("%s has no value", x->toChars()); + if (lengthVar) + { // If $ was used, declare it now + Expression *av = new DeclarationExp(loc, lengthVar); + x = new CommaExp(0, av, x); + x->semantic(sc); + } + arguments->tdata()[i] = x; + sc = sc->pop(); + } + /* Rewrite op e1[arguments] as: * e1.opIndex(arguments) */ diff --git a/dmd2/parse.c b/dmd2/parse.c index b43dd7da..99519b72 100644 --- a/dmd2/parse.c +++ b/dmd2/parse.c @@ -266,6 +266,8 @@ Dsymbols *Parser::parseDeclDefs(int once) } else { + if (!global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; goto Lstc; } @@ -407,7 +409,11 @@ Dsymbols *Parser::parseDeclDefs(int once) else if (token.value == TOKwild) stc = STCwild; else + { + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; + } goto Lstc; case TOKfinal: stc = STCfinal; goto Lstc; case TOKauto: stc = STCauto; goto Lstc; @@ -981,7 +987,8 @@ Dsymbol *Parser::parseCtor() nextToken(); nextToken(); check(TOKrparen); - PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0); + StorageClass stc = parsePostfix(); + PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0, stc); parseContracts(f); return f; } @@ -1278,6 +1285,8 @@ Parameters *Parser::parseParameters(int *pvarargs) case TOKimmutable: if (peek(&token)->value == TOKlparen) goto Ldefault; + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; goto L2; @@ -2366,7 +2375,9 @@ Type *Parser::parseBasicType() check(TOKlparen); t = parseType(); check(TOKrparen); - if (t->isShared()) + if (t->isImmutable()) + ; + else if (t->isShared()) t = t->makeSharedConst(); else t = t->makeConst(); @@ -2390,7 +2401,9 @@ Type *Parser::parseBasicType() check(TOKlparen); t = parseType(); check(TOKrparen); - if (t->isConst()) + if (t->isImmutable()) + ; + else if (t->isConst()) t = t->makeSharedConst(); else if (t->isWild()) t = t->makeSharedWild(); @@ -2404,7 +2417,9 @@ Type *Parser::parseBasicType() check(TOKlparen); t = parseType(); check(TOKrparen); - if (t->isShared()) + if (t->isImmutable()/* || t->isConst()*/) + ; + else if (t->isShared()) t = t->makeSharedWild(); else t = t->makeWild(); @@ -2511,7 +2526,7 @@ Type *Parser::parseBasicType2(Type *t) return NULL; } -Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl, StorageClass storage_class) +Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl, StorageClass storage_class, int* pdisable) { Type *ts; //printf("parseDeclarator(tpl = %p)\n", tpl); @@ -2643,6 +2658,8 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * stc |= storage_class; // merge prefix storage classes Type *tf = new TypeFunction(arguments, t, varargs, linkage, stc); tf = tf->addSTC(stc); + if (pdisable) + *pdisable = stc & STCdisable ? 1 : 0; /* Insert tf into * ts -> ... -> t @@ -2673,6 +2690,7 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *comment) { StorageClass stc; + int disable; Type *ts; Type *t; Type *tfirst; @@ -2711,6 +2729,8 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c } break; case TOKtypedef: + if (!global.params.useDeprecated) + error("use of typedef is deprecated; use alias instead"); tok = token.value; nextToken(); break; @@ -2731,6 +2751,8 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c case TOKimmutable: if (peek(&token)->value == TOKlparen) break; + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; goto L1; @@ -2814,7 +2836,11 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c token.value == TOKidentifier && (tk = peek(&token))->value == TOKlparen && skipParens(tk, &tk) && - peek(tk)->value == TOKlparen) + ((tk = peek(tk)), 1) && + skipAttributes(tk, &tk) && + (tk->value == TOKlparen || + tk->value == TOKlcurly) + ) { ts = NULL; } @@ -2835,7 +2861,7 @@ L2: TemplateParameters *tpl = NULL; ident = NULL; - t = parseDeclarator(ts, &ident, &tpl, storage_class); + t = parseDeclarator(ts, &ident, &tpl, storage_class, &disable); assert(t); if (!tfirst) tfirst = t; @@ -2855,7 +2881,10 @@ L2: init = parseInitializer(); } if (tok == TOKtypedef) - v = new TypedefDeclaration(loc, ident, t, init); + { v = new TypedefDeclaration(loc, ident, t, init); + if (!global.params.useDeprecated) + error("use of typedef is deprecated; use alias instead"); + } else { if (init) error("alias cannot have initializer"); @@ -2902,7 +2931,7 @@ L2: //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t->toChars(), storage_class); FuncDeclaration *f = - new FuncDeclaration(loc, 0, ident, storage_class, t); + new FuncDeclaration(loc, 0, ident, storage_class | (disable ? STCdisable : 0), t); addComment(f, comment); if (tpl) constraint = parseConstraint(); @@ -5031,7 +5060,8 @@ int Parser::skipAttributes(Token *t, Token **pt) //case TOKmanifest: break; case TOKat: - if (parseAttribute() == STCundefined) + t = peek(t); + if (t->value == TOKidentifier) break; goto Lerror; default: @@ -5335,6 +5365,8 @@ Expression *Parser::parsePrimaryExp() token.value == TOKdelegate || token.value == TOKreturn)) { + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); tok2 = token.value; nextToken(); } @@ -5708,6 +5740,8 @@ Expression *Parser::parseUnaryExp() } else if ((token.value == TOKimmutable || token.value == TOKinvariant) && peekNext() == TOKrparen) { + if (token.value == TOKinvariant && !global.params.useDeprecated) + error("use of 'invariant' rather than 'immutable' is deprecated"); m = MODimmutable; goto Lmod2; } diff --git a/dmd2/parse.h b/dmd2/parse.h index 2f03a7df..0e43cc25 100644 --- a/dmd2/parse.h +++ b/dmd2/parse.h @@ -108,7 +108,7 @@ struct Parser : Lexer Type *parseType(Identifier **pident = NULL, TemplateParameters **tpl = NULL); Type *parseBasicType(); Type *parseBasicType2(Type *t); - Type *parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl = NULL, StorageClass storage_class = 0); + Type *parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl = NULL, StorageClass storage_class = 0, int* pdisable = NULL); Dsymbols *parseDeclarations(StorageClass storage_class, unsigned char *comment); void parseContracts(FuncDeclaration *f); void checkDanglingElse(Loc elseloc); diff --git a/dmd2/statement.c b/dmd2/statement.c index b16c2346..60ea2c89 100644 --- a/dmd2/statement.c +++ b/dmd2/statement.c @@ -449,6 +449,12 @@ Statement *CompileStatement::semantic(Scope *sc) return s->semantic(sc); } +int CompileStatement::blockExit(bool mustNotThrow) +{ + assert(global.errors); + return BEfallthru; +} + /******************************** CompoundStatement ***************************/ @@ -1493,6 +1499,7 @@ Lretry: { // Declare key if (arg->storageClass & (STCout | STCref | STClazy)) error("no storage class for key %s", arg->ident->toChars()); + arg->type = arg->type->semantic(loc, sc); TY keyty = arg->type->ty; if (keyty != Tint32 && keyty != Tuns32) { @@ -3707,9 +3714,26 @@ Statement *ReturnStatement::semantic(Scope *sc) Type *tfret = tf->nextOf(); if (tfret) { - if (tfret != Type::terror && !exp->type->equals(tfret)) - error("mismatched function return type inference of %s and %s", - exp->type->toChars(), tfret->toChars()); + if (tfret != Type::terror) + { + if (!exp->type->equals(tfret)) + { + int m1 = exp->type->implicitConvTo(tfret); + int m2 = tfret->implicitConvTo(exp->type); + //printf("exp->type = %s m2<-->m1 tret %s\n", exp->type->toChars(), tfret->toChars()); + //printf("m1 = %d, m2 = %d\n", m1, m2); + + if (m1 && m2) + ; + else if (!m1 && m2) + tf->next = exp->type; + else if (m1 && !m2) + ; + else + error("mismatched function return type inference of %s and %s", + exp->type->toChars(), tfret->toChars()); + } + } /* The "refness" is determined by the first return statement, * not all of them. This means: @@ -3739,7 +3763,7 @@ Statement *ReturnStatement::semantic(Scope *sc) tf->isref = FALSE; // return by value } tf->next = exp->type; - fd->type = tf->semantic(loc, sc); + //fd->type = tf->semantic(loc, sc); // Removed with 6902 if (!fd->tintro) { tret = fd->type->nextOf(); tbret = tret->toBasetype(); @@ -3755,6 +3779,8 @@ Statement *ReturnStatement::semantic(Scope *sc) exp = exp->castTo(sc, exp->type->invariantOf()); } + if (fd->tintro) + exp = exp->implicitCastTo(sc, fd->type->nextOf()); exp = exp->implicitCastTo(sc, tret); if (!((TypeFunction *)fd->type)->isref) exp = exp->optimize(WANTvalue); diff --git a/dmd2/statement.h b/dmd2/statement.h index 69655615..c96c5b40 100644 --- a/dmd2/statement.h +++ b/dmd2/statement.h @@ -211,6 +211,7 @@ struct CompileStatement : Statement void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statements *flatten(Scope *sc); Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); }; struct CompoundStatement : Statement diff --git a/dmd2/template.c b/dmd2/template.c index c621c05a..36103e7c 100644 --- a/dmd2/template.c +++ b/dmd2/template.c @@ -766,7 +766,25 @@ MATCH TemplateDeclaration::matchWithInstance(TemplateInstance *ti, Expression *e = constraint->syntaxCopy(); Scope *sc = paramscope->push(); sc->flags |= SCOPEstaticif; + + FuncDeclaration *fd = onemember && onemember->toAlias() ? + onemember->toAlias()->isFuncDeclaration() : NULL; + Dsymbol *s = parent; + while (s->isTemplateInstance() || s->isTemplateMixin()) + s = s->parent; + AggregateDeclaration *ad = s->isAggregateDeclaration(); + VarDeclaration *vthissave; + if (fd && ad) + { + vthissave = fd->vthis; + fd->vthis = fd->declareThis(paramscope, ad); + } + e = e->semantic(sc); + + if (fd && fd->vthis) + fd->vthis = vthissave; + sc->pop(); e = e->optimize(WANTvalue | WANTinterpret); if (e->isBool(TRUE)) @@ -924,7 +942,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objec #if 0 printf("\nTemplateDeclaration::deduceFunctionTemplateMatch() %s\n", toChars()); - for (i = 0; i < fargs->dim; i++) + for (size_t i = 0; i < fargs->dim; i++) { Expression *e = fargs->tdata()[i]; printf("\tfarg[%d] is %s, type is %s\n", i, e->toChars(), e->type->toChars()); } @@ -1188,7 +1206,9 @@ L2: } } else - { Expression *farg = fargs->tdata()[i]; + { + Expression *farg = fargs->tdata()[i]; +Lretry: #if 0 printf("\tfarg->type = %s\n", farg->type->toChars()); printf("\tfparam->type = %s\n", fparam->type->toChars()); @@ -1208,6 +1228,14 @@ L2: argtype = argtype->invariantOf(); } } + + /* Remove top const for dynamic array types and pointer types + */ + if ((argtype->ty == Tarray || argtype->ty == Tpointer) && + !argtype->isMutable()) + { + argtype = argtype->mutableOf(); + } #endif MATCH m; @@ -1219,18 +1247,41 @@ L2: /* If no match, see if there's a conversion to a delegate */ - if (!m && fparam->type->toBasetype()->ty == Tdelegate) - { - TypeDelegate *td = (TypeDelegate *)fparam->type->toBasetype(); - TypeFunction *tf = (TypeFunction *)td->next; - - if (!tf->varargs && Parameter::dim(tf->parameters) == 0) + if (!m) + { Type *tbp = fparam->type->toBasetype(); + Type *tba = farg->type->toBasetype(); + AggregateDeclaration *ad; + if (tbp->ty == Tdelegate) { - m = farg->type->deduceType(paramscope, tf->next, parameters, &dedtypes); - if (!m && tf->next->toBasetype()->ty == Tvoid) - m = MATCHconvert; + TypeDelegate *td = (TypeDelegate *)fparam->type->toBasetype(); + TypeFunction *tf = (TypeFunction *)td->next; + + if (!tf->varargs && Parameter::dim(tf->parameters) == 0) + { + m = farg->type->deduceType(paramscope, tf->next, parameters, &dedtypes); + if (!m && tf->next->toBasetype()->ty == Tvoid) + m = MATCHconvert; + } + //printf("\tm2 = %d\n", m); + } + else if (tba->ty == Tclass) + { + ad = ((TypeClass *)tba)->sym; + goto Lad; + } + else if (tba->ty == Tstruct) + { + ad = ((TypeStruct *)tba)->sym; + Lad: + if (ad->aliasthis) + { + Expression *e = new DotIdExp(farg->loc, farg, ad->aliasthis->ident); + e = e->semantic(sc); + e = resolveProperties(sc, e); + farg = e; + goto Lretry; + } } - //printf("\tm2 = %d\n", m); } if (m) @@ -1399,8 +1450,24 @@ Lmatch: int nerrors = global.errors; + FuncDeclaration *fd = onemember && onemember->toAlias() ? + onemember->toAlias()->isFuncDeclaration() : NULL; + Dsymbol *s = parent; + while (s->isTemplateInstance() || s->isTemplateMixin()) + s = s->parent; + AggregateDeclaration *ad = s->isAggregateDeclaration(); + VarDeclaration *vthissave; + if (fd && ad) + { + vthissave = fd->vthis; + fd->vthis = fd->declareThis(paramscope, ad); + } + e = e->semantic(paramscope); + if (fd && fd->vthis) + fd->vthis = vthissave; + previous = pr.prev; // unlink from threaded list if (nerrors != global.errors) // if any errors from evaluating the constraint, no match @@ -1682,6 +1749,11 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, return NULL; } +bool TemplateDeclaration::hasStaticCtorOrDtor() +{ + return FALSE; // don't scan uninstantiated templates +} + void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { #if 0 // Should handle template functions for doc generation @@ -2107,33 +2179,23 @@ MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame // Extra check that array dimensions must match if (tparam) { + if (tparam->ty == Tarray) + { MATCH m; + + m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); + if (m == MATCHexact) + m = MATCHconvert; + return m; + } + + Identifier *id = NULL; if (tparam->ty == Tsarray) { TypeSArray *tp = (TypeSArray *)tparam; - if (tp->dim->op == TOKvar && ((VarExp *)tp->dim)->var->storage_class & STCtemplateparameter) - { int i = templateIdentifierLookup(((VarExp *)tp->dim)->var->ident, parameters); - // This code matches code in TypeInstance::deduceType() - if (i == -1) - goto Lnomatch; - TemplateParameter *tp = parameters->tdata()[i]; - TemplateValueParameter *tvp = tp->isTemplateValueParameter(); - if (!tvp) - goto Lnomatch; - Expression *e = (Expression *)dedtypes->tdata()[i]; - if (e) - { - if (!dim->equals(e)) - goto Lnomatch; - } - else - { Type *vt = tvp->valType->semantic(0, sc); - MATCH m = (MATCH)dim->implicitConvTo(vt); - if (!m) - goto Lnomatch; - dedtypes->tdata()[i] = dim; - } + { + id = ((VarExp *)tp->dim)->var->ident; } else if (dim->toInteger() != tp->dim->toInteger()) return MATCHnomatch; @@ -2141,43 +2203,37 @@ MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame else if (tparam->ty == Taarray) { TypeAArray *tp = (TypeAArray *)tparam; - if (tp->index->ty == Tident) - { TypeIdentifier *tident = (TypeIdentifier *)tp->index; - - if (tident->idents.dim == 0) - { Identifier *id = tident->ident; - - for (size_t i = 0; i < parameters->dim; i++) - { - TemplateParameter *tp = parameters->tdata()[i]; - - if (tp->ident->equals(id)) - { // Found the corresponding template parameter - TemplateValueParameter *tvp = tp->isTemplateValueParameter(); - if (!tvp || !tvp->valType->isintegral()) - goto Lnomatch; - - if (dedtypes->tdata()[i]) - { - if (!dim->equals(dedtypes->tdata()[i])) - goto Lnomatch; - } - else - { dedtypes->tdata()[i] = dim; - } - return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); - } - } - } + if (tp->index->ty == Tident && + ((TypeIdentifier *)tp->index)->idents.dim == 0) + { + id = ((TypeIdentifier *)tp->index)->ident; } } - else if (tparam->ty == Tarray) - { MATCH m; - - m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); - if (m == MATCHexact) - m = MATCHconvert; - return m; + if (id) + { + // This code matches code in TypeInstance::deduceType() + int i = templateIdentifierLookup(id, parameters); + if (i == -1) + goto Lnomatch; + TemplateParameter *tprm = parameters->tdata()[i]; + TemplateValueParameter *tvp = tprm->isTemplateValueParameter(); + if (!tvp) + goto Lnomatch; + Expression *e = (Expression *)dedtypes->tdata()[i]; + if (e) + { + if (!dim->equals(e)) + goto Lnomatch; + } + else + { + Type *vt = tvp->valType->semantic(0, sc); + MATCH m = (MATCH)dim->implicitConvTo(vt); + if (!m) + goto Lnomatch; + dedtypes->tdata()[i] = dim; + } + return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); } } return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); @@ -2228,6 +2284,7 @@ MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *para { Parameter *fparam = Parameter::getNth(tp->parameters, i); fparam->type = fparam->type->addStorageClass(fparam->storageClass); + fparam->storageClass &= ~(STC_TYPECTOR | STCin); } //printf("\t-> this = %d, ", ty); print(); //printf("\t-> tparam = %d, ", tparam->ty); tparam->print(); @@ -3490,6 +3547,19 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, ei = ei->optimize(WANTvalue | WANTinterpret); } + //printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty); + vt = valType->semantic(0, sc); + //printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars()); + //printf("vt = %s\n", vt->toChars()); + + if (ei->type) + { + m = (MATCH)ei->implicitConvTo(vt); + //printf("m: %d\n", m); + if (!m) + goto Lnomatch; + } + if (specValue) { if (!ei || ei == edummy) @@ -3504,6 +3574,7 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, ei = ei->syntaxCopy(); ei = ei->semantic(sc); + ei = ei->implicitCastTo(sc, vt); ei = ei->optimize(WANTvalue | WANTinterpret); //ei->type = ei->type->toHeadMutable(); //printf("\tei: %s, %s\n", ei->toChars(), ei->type->toChars()); @@ -3511,24 +3582,20 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, if (!ei->equals(e)) goto Lnomatch; } - else if (dedtypes->tdata()[i]) - { // Must match already deduced value - Expression *e = (Expression *)dedtypes->tdata()[i]; - - if (!ei || !ei->equals(e)) - goto Lnomatch; - } - - //printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty); - vt = valType->semantic(0, sc); - //printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars()); - //printf("vt = %s\n", vt->toChars()); - if (ei->type) + else { - m = (MATCH)ei->implicitConvTo(vt); - //printf("m: %d\n", m); - if (!m) - goto Lnomatch; + if (dedtypes->tdata()[i]) + { // Must match already deduced value + Expression *e = (Expression *)dedtypes->tdata()[i]; + + if (!ei || !ei->equals(e)) + goto Lnomatch; + } + else if (m != MATCHexact) + { + ei = ei->implicitCastTo(sc, vt); + ei = ei->optimize(WANTvalue | WANTinterpret); + } } dedtypes->tdata()[i] = ei; diff --git a/dmd2/template.h b/dmd2/template.h index 141630cb..18117e43 100644 --- a/dmd2/template.h +++ b/dmd2/template.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -81,6 +81,7 @@ struct TemplateDeclaration : ScopeDsymbol void semantic(Scope *sc); int overloadInsert(Dsymbol *s); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + bool hasStaticCtorOrDtor(); const char *kind(); char *toChars(); diff --git a/dmd2/traits.c b/dmd2/traits.c index e0c7d42e..849b0434 100644 --- a/dmd2/traits.c +++ b/dmd2/traits.c @@ -258,9 +258,21 @@ Expression *TraitsExp::semantic(Scope *sc) } if (ident == Id::hasMember) - { /* Take any errors as meaning it wasn't found + { + if (t) + { + Dsymbol *sym = t->toDsymbol(sc); + Dsymbol *sm = sym->search(loc, id, 0); + if (sm) + goto Ltrue; + } + + /* Take any errors as meaning it wasn't found */ - e = e->trySemantic(sc); + Scope *sc2 = sc->push(); + //sc2->inHasMember++; + e = e->trySemantic(sc2); + sc2->pop(); if (!e) { if (global.gag) { @@ -344,40 +356,56 @@ Expression *TraitsExp::semantic(Scope *sc) error("%s %s has no members", s->kind(), s->toChars()); goto Lfalse; } - Expressions *exps = new Expressions; - while (1) - { size_t sddim = ScopeDsymbol::dim(sd->members); - for (size_t i = 0; i < sddim; i++) + + // use a struct as local function + struct PushIdentsDg + { + static int dg(void *ctx, size_t n, Dsymbol *sm) { - Dsymbol *sm = ScopeDsymbol::getNth(sd->members, i); if (!sm) - break; + return 1; //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(); + Identifiers *idents = (Identifiers *)ctx; - /* Skip if already present in exps[] + /* Skip if already present in idents[] */ - for (size_t j = 0; j < exps->dim; j++) - { StringExp *se2 = (StringExp *)exps->tdata()[j]; - if (strcmp(str, (char *)se2->string) == 0) - goto Lnext; + for (size_t j = 0; j < idents->dim; j++) + { Identifier *id = idents->tdata()[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 } - StringExp *se = new StringExp(loc, str); - exps->push(se); + idents->push(sm->ident); } - Lnext: - ; + return 0; } - ClassDeclaration *cd = sd->isClassDeclaration(); - if (cd && cd->baseClass && ident == Id::allMembers) - sd = cd->baseClass; // do again with base class - else - break; + }; + + Identifiers *idents = new Identifiers; + ScopeDsymbol::foreach(sd->members, &PushIdentsDg::dg, idents); + + ClassDeclaration *cd = sd->isClassDeclaration(); + if (cd && cd->baseClass && ident == Id::allMembers) + { sd = cd->baseClass; // do again with base class + ScopeDsymbol::foreach(sd->members, &PushIdentsDg::dg, 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->tdata()[i]; + StringExp *se = new StringExp(loc, id->toChars()); + exps->tdata()[i] = se; + } + #if DMDV1 Expression *e = new ArrayLiteralExp(loc, exps); #endif diff --git a/gen/functions.cpp b/gen/functions.cpp index 5188dd0f..a5024c69 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -306,10 +306,13 @@ void DtoResolveFunction(FuncDeclaration* fdecl) if (fdecl->ir.resolved) return; fdecl->ir.resolved = true; - // If errors occurred compiling it, such as bugzilla 6118 Type *type = fdecl->type; - if (type && type->ty == Tfunction && ((TypeFunction *)type)->next->ty == Terror) - return; + // If errors occurred compiling it, such as bugzilla 6118 + if (type && type->ty == Tfunction) { + Type *next = ((TypeFunction *)type)->next; + if (!next || next->ty == Terror) + return; + } //printf("resolve function: %s\n", fdecl->toPrettyChars()); diff --git a/runtime/druntime b/runtime/druntime index 24e79c6c..10e4512f 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit 24e79c6c38b4cfc9aca1d3583a1c0fffc102c9ce +Subproject commit 10e4512f453fcafde9448a41c672ee7315229963 diff --git a/runtime/phobos b/runtime/phobos index 2bebc8f8..1f6264e9 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 2bebc8f8ba261b41fb0252b225232b8f46051b57 +Subproject commit 1f6264e94236e5cd20d211e136f05a88cb536b00